Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Move JNI lookup code to AutolinkedHybridObject #319

Merged
merged 9 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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