Skip to content

Commit

Permalink
feat: Move JNI lookup code to AutolinkedHybridObject to avoid spagh…
Browse files Browse the repository at this point in the history
…etti (#319)

* feat: Move JNI lookup code to `AutolinkedHybridObject`

* fix: Extract name & format

* fix: Remove null check from generated code

* Fix typename

* whoops

* Update AutolinkedHybridObject.hpp

* Update AutolinkedHybridObject.hpp

* hä geht eh so oida

* `AutolinkedHybridObject` -> `ConstructableHybridObject`
  • Loading branch information
mrousavy authored Nov 11, 2024
1 parent 1469f52 commit cd142c0
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export function createJNIHybridObjectRegistration({
}: Props): JNIHybridObjectRegistration {
const { JHybridTSpec } = getHybridObjectName(hybridObjectName)
const jniNamespace = NitroConfig.getAndroidPackage('c++/jni', jniClassName)
const javaNamespace = NitroConfig.getAndroidPackage(
'java/kotlin',
jniClassName
)

return {
requiredImports: [
Expand All @@ -37,42 +33,18 @@ export function createJNIHybridObjectRegistration({
language: 'c++',
space: 'system',
},
{
name: 'NitroModules/DefaultConstructableObject.hpp',
language: 'c++',
space: 'system',
},
],
cppCode: `
HybridObjectRegistry::registerHybridObjectConstructor(
"${hybridObjectName}",
[]() -> std::shared_ptr<HybridObject> {
static jni::alias_ref<jni::JClass> javaClass;
static jni::JConstructor<${JHybridTSpec}::javaobject()> defaultConstructor;
static bool isInitialized = false;
try {
if (!isInitialized) {
javaClass = jni::findClassStatic("${jniNamespace}");
defaultConstructor = javaClass->getConstructor<${JHybridTSpec}::javaobject()>();
isInitialized = true;
}
} catch (const jni::JniException& exc) {
std::string message = exc.what();
if (message.find("ClassNotFoundException")) {
throw std::runtime_error("Couldn't find class \`${javaNamespace}\`!\\n"
"- Make sure the class exists in the specified namespace.\\n"
"- Make sure the class is not stripped. If you are using ProGuard, add \`@Keep\` and \`@DoNotStrip\` annotations to ${jniClassName}.");
} else if (message.find("NoSuchMethodError")) {
throw std::runtime_error("Couldn't find ${jniClassName}'s default constructor!\\n"
"- If you want to autolink ${jniClassName}, add a default constructor that takes zero arguments.\\n"
"- If you need arguments to create instances of ${jniClassName}, create a separate HybridObject that acts as a factory for this HybridObject to create instances of it with parameters.\\n"
"- If you already have a default constructor, make sure it is not being stripped. If you are using ProGuard, add \`@Keep\` and \`@DoNotStrip\` annotations to the default constructor.");
} else {
throw;
}
}
auto instance = javaClass->newObject(defaultConstructor);
#ifdef NITRO_DEBUG
if (instance == nullptr) [[unlikely]] {
throw std::runtime_error("Failed to create an instance of \\"${JHybridTSpec}\\" - the constructor returned null!");
}
#endif
static DefaultConstructableObject<${JHybridTSpec}::javaobject> object("${jniNamespace}");
auto instance = object.create();
auto globalRef = jni::make_global(instance);
return JNISharedPtr::make_shared_from_jni<${JHybridTSpec}>(globalRef);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "JHybridBaseSpec.hpp"
#include "JHybridChildSpec.hpp"
#include <NitroModules/JNISharedPtr.hpp>
#include <NitroModules/DefaultConstructableObject.hpp>
#include "HybridTestObjectCpp.hpp"

namespace margelo::nitro::image {
Expand Down Expand Up @@ -49,37 +50,8 @@ int initialize(JavaVM* vm) {
HybridObjectRegistry::registerHybridObjectConstructor(
"ImageFactory",
[]() -> std::shared_ptr<HybridObject> {
static jni::alias_ref<jni::JClass> javaClass;
static jni::JConstructor<JHybridImageFactorySpec::javaobject()> defaultConstructor;
static bool isInitialized = false;
try {
if (!isInitialized) {
javaClass = jni::findClassStatic("com/margelo/nitro/image/ImageFactory");
defaultConstructor = javaClass->getConstructor<JHybridImageFactorySpec::javaobject()>();
isInitialized = true;
}
} catch (const jni::JniException& exc) {
std::string message = exc.what();
if (message.find("ClassNotFoundException")) {
throw std::runtime_error("Couldn't find class `com.margelo.nitro.image.ImageFactory`!\n"
"- Make sure the class exists in the specified namespace.\n"
"- Make sure the class is not stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to ImageFactory.");
} else if (message.find("NoSuchMethodError")) {
throw std::runtime_error("Couldn't find ImageFactory's default constructor!\n"
"- If you want to autolink ImageFactory, add a default constructor that takes zero arguments.\n"
"- If you need arguments to create instances of ImageFactory, create a separate HybridObject that acts as a factory for this HybridObject to create instances of it with parameters.\n"
"- If you already have a default constructor, make sure it is not being stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to the default constructor.");
} else {
throw;
}
}

auto instance = javaClass->newObject(defaultConstructor);
#ifdef NITRO_DEBUG
if (instance == nullptr) [[unlikely]] {
throw std::runtime_error("Failed to create an instance of \"JHybridImageFactorySpec\" - the constructor returned null!");
}
#endif
static DefaultConstructableObject<JHybridImageFactorySpec::javaobject> object("com/margelo/nitro/image/ImageFactory");
auto instance = object.create();
auto globalRef = jni::make_global(instance);
return JNISharedPtr::make_shared_from_jni<JHybridImageFactorySpec>(globalRef);
}
Expand All @@ -96,113 +68,26 @@ int initialize(JavaVM* vm) {
HybridObjectRegistry::registerHybridObjectConstructor(
"TestObjectSwiftKotlin",
[]() -> std::shared_ptr<HybridObject> {
static jni::alias_ref<jni::JClass> javaClass;
static jni::JConstructor<JHybridTestObjectSwiftKotlinSpec::javaobject()> defaultConstructor;
static bool isInitialized = false;
try {
if (!isInitialized) {
javaClass = jni::findClassStatic("com/margelo/nitro/image/HybridTestObjectKotlin");
defaultConstructor = javaClass->getConstructor<JHybridTestObjectSwiftKotlinSpec::javaobject()>();
isInitialized = true;
}
} catch (const jni::JniException& exc) {
std::string message = exc.what();
if (message.find("ClassNotFoundException")) {
throw std::runtime_error("Couldn't find class `com.margelo.nitro.image.HybridTestObjectKotlin`!\n"
"- Make sure the class exists in the specified namespace.\n"
"- Make sure the class is not stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to HybridTestObjectKotlin.");
} else if (message.find("NoSuchMethodError")) {
throw std::runtime_error("Couldn't find HybridTestObjectKotlin's default constructor!\n"
"- If you want to autolink HybridTestObjectKotlin, add a default constructor that takes zero arguments.\n"
"- If you need arguments to create instances of HybridTestObjectKotlin, create a separate HybridObject that acts as a factory for this HybridObject to create instances of it with parameters.\n"
"- If you already have a default constructor, make sure it is not being stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to the default constructor.");
} else {
throw;
}
}

auto instance = javaClass->newObject(defaultConstructor);
#ifdef NITRO_DEBUG
if (instance == nullptr) [[unlikely]] {
throw std::runtime_error("Failed to create an instance of \"JHybridTestObjectSwiftKotlinSpec\" - the constructor returned null!");
}
#endif
static DefaultConstructableObject<JHybridTestObjectSwiftKotlinSpec::javaobject> object("com/margelo/nitro/image/HybridTestObjectKotlin");
auto instance = object.create();
auto globalRef = jni::make_global(instance);
return JNISharedPtr::make_shared_from_jni<JHybridTestObjectSwiftKotlinSpec>(globalRef);
}
);
HybridObjectRegistry::registerHybridObjectConstructor(
"Base",
[]() -> std::shared_ptr<HybridObject> {
static jni::alias_ref<jni::JClass> javaClass;
static jni::JConstructor<JHybridBaseSpec::javaobject()> defaultConstructor;
static bool isInitialized = false;
try {
if (!isInitialized) {
javaClass = jni::findClassStatic("com/margelo/nitro/image/HybridBase");
defaultConstructor = javaClass->getConstructor<JHybridBaseSpec::javaobject()>();
isInitialized = true;
}
} catch (const jni::JniException& exc) {
std::string message = exc.what();
if (message.find("ClassNotFoundException")) {
throw std::runtime_error("Couldn't find class `com.margelo.nitro.image.HybridBase`!\n"
"- Make sure the class exists in the specified namespace.\n"
"- Make sure the class is not stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to HybridBase.");
} else if (message.find("NoSuchMethodError")) {
throw std::runtime_error("Couldn't find HybridBase's default constructor!\n"
"- If you want to autolink HybridBase, add a default constructor that takes zero arguments.\n"
"- If you need arguments to create instances of HybridBase, create a separate HybridObject that acts as a factory for this HybridObject to create instances of it with parameters.\n"
"- If you already have a default constructor, make sure it is not being stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to the default constructor.");
} else {
throw;
}
}

auto instance = javaClass->newObject(defaultConstructor);
#ifdef NITRO_DEBUG
if (instance == nullptr) [[unlikely]] {
throw std::runtime_error("Failed to create an instance of \"JHybridBaseSpec\" - the constructor returned null!");
}
#endif
static DefaultConstructableObject<JHybridBaseSpec::javaobject> object("com/margelo/nitro/image/HybridBase");
auto instance = object.create();
auto globalRef = jni::make_global(instance);
return JNISharedPtr::make_shared_from_jni<JHybridBaseSpec>(globalRef);
}
);
HybridObjectRegistry::registerHybridObjectConstructor(
"Child",
[]() -> std::shared_ptr<HybridObject> {
static jni::alias_ref<jni::JClass> javaClass;
static jni::JConstructor<JHybridChildSpec::javaobject()> defaultConstructor;
static bool isInitialized = false;
try {
if (!isInitialized) {
javaClass = jni::findClassStatic("com/margelo/nitro/image/HybridChild");
defaultConstructor = javaClass->getConstructor<JHybridChildSpec::javaobject()>();
isInitialized = true;
}
} catch (const jni::JniException& exc) {
std::string message = exc.what();
if (message.find("ClassNotFoundException")) {
throw std::runtime_error("Couldn't find class `com.margelo.nitro.image.HybridChild`!\n"
"- Make sure the class exists in the specified namespace.\n"
"- Make sure the class is not stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to HybridChild.");
} else if (message.find("NoSuchMethodError")) {
throw std::runtime_error("Couldn't find HybridChild's default constructor!\n"
"- If you want to autolink HybridChild, add a default constructor that takes zero arguments.\n"
"- If you need arguments to create instances of HybridChild, create a separate HybridObject that acts as a factory for this HybridObject to create instances of it with parameters.\n"
"- If you already have a default constructor, make sure it is not being stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to the default constructor.");
} else {
throw;
}
}

auto instance = javaClass->newObject(defaultConstructor);
#ifdef NITRO_DEBUG
if (instance == nullptr) [[unlikely]] {
throw std::runtime_error("Failed to create an instance of \"JHybridChildSpec\" - the constructor returned null!");
}
#endif
static DefaultConstructableObject<JHybridChildSpec::javaobject> object("com/margelo/nitro/image/HybridChild");
auto instance = object.create();
auto globalRef = jni::make_global(instance);
return JNISharedPtr::make_shared_from_jni<JHybridChildSpec>(globalRef);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// DefaultConstructableObject.hpp
// react-native-nitro
//
// Created by Marc Rousavy on 11.11.24.
//

#pragma once

#include "NitroDefines.hpp"
#include <fbjni/fbjni.h>

namespace margelo::nitro {

using namespace facebook;

template <typename T>
class DefaultConstructableObject {
public:
explicit DefaultConstructableObject(const char* javaClassDescriptor) {
try {
// Find JNI class and default constructor
_javaClass = jni::findClassStatic(javaClassDescriptor);
_defaultConstructor = _javaClass->getConstructor<T()>();
} catch (const jni::JniException& exc) {
std::string message = exc.what();
std::string descriptor = javaClassDescriptor;
std::string className = findClassName(descriptor);
if (message.find("ClassNotFoundException")) {
// Java class cannot be found
throw std::runtime_error(
"Couldn't find class `" + descriptor +
"`!\n"
"- Make sure the class exists in the specified namespace.\n"
"- Make sure the class is not stripped. If you are using ProGuard, add `@Keep` and `@DoNotStrip` annotations to `" +
className + "`.");
} else if (message.find("NoSuchMethodError")) {
// Default Constructor cannot be found
throw std::runtime_error(
"Couldn't find " + className +
"'s default constructor!\n"
"- If you want to autolink " +
className +
", add a default constructor that takes zero arguments.\n"
"- If you need arguments to create instances of " +
className +
", create a separate HybridObject that acts as a factory for this HybridObject to create instances of it with parameters.\n"
"- If you already have a default constructor, make sure it is not being stripped. If you are using ProGuard, add `@Keep` and "
"`@DoNotStrip` annotations to the default constructor.");
} else {
throw;
}
}
}

public:
jni::local_ref<T> create() const {
// Calls the class's default constructor
auto instance = _javaClass->newObject(_defaultConstructor);
#ifdef NITRO_DEBUG
if (instance == nullptr) [[unlikely]] {
throw std::runtime_error("Failed to create an instance of \"JHybridTestObjectSwiftKotlinSpec\" - the constructor returned null!");
}
#endif
return instance;
}

private:
static std::string findClassName(const std::string& jniDescriptor) {
size_t lastSlash = jniDescriptor.rfind('/');
return jniDescriptor.substr(lastSlash + 1);
}

private:
jni::alias_ref<jni::JClass> _javaClass;
jni::JConstructor<T()> _defaultConstructor;
};

} // namespace margelo::nitro

0 comments on commit cd142c0

Please sign in to comment.