Skip to content
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
22 changes: 19 additions & 3 deletions NativeScript/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ set(CMAKE_CXX_STANDARD 20)

set(BUILD_FRAMEWORK TRUE)

set(HERMES_XCFRAMEWORK "${CMAKE_SOURCE_DIR}/../Frameworks/hermes.xcframework")

set(COMMON_FLAGS "-O3 -Wno-shorten-64-to-32")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAGS}")
Expand Down Expand Up @@ -48,6 +50,9 @@ elseif(TARGET_PLATFORM STREQUAL "ios-sim")
set(SDK_NAME "iphonesimulator")
set(CMAKE_OSX_ARCHITECTURES "arm64")
set(TARGET_PLATFORM_SPEC "ios-arm64-simulator")
if(EXISTS "${HERMES_XCFRAMEWORK}/ios-arm64_x86_64-simulator")
set(TARGET_PLATFORM_SPEC "ios-arm64_x86_64-simulator")
endif()

elseif(TARGET_PLATFORM STREQUAL "macos")
set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "13.3")
Expand Down Expand Up @@ -184,13 +189,24 @@ if(ENABLE_JS_RUNTIME)
)

elseif(TARGET_ENGINE_HERMES)
set(HERMES_HEADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Frameworks/hermes-headers")

include_directories(
napi/hermes
napi/hermes/include
napi/hermes/include/hermes
napi/hermes/include/jsi
)

if(EXISTS "${HERMES_HEADERS_DIR}/jsi/jsi.h" AND EXISTS "${HERMES_HEADERS_DIR}/hermes/hermes.h")
include_directories(
${HERMES_HEADERS_DIR}
)
else()
include_directories(
napi/hermes/include
napi/hermes/include/hermes
napi/hermes/include/jsi
)
endif()

set(SOURCE_FILES
${SOURCE_FILES}
napi/hermes/jsr.cpp
Expand Down
8 changes: 5 additions & 3 deletions NativeScript/ffi/Block.mm
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) {
if (napiSupportsThreadsafeFunctions(bridgeState->self_dl)) {
napi_value workName;
napi_create_string_utf8(env, "Block", NAPI_AUTO_LENGTH, &workName);
napi_create_threadsafe_function(env, callback, nullptr, workName, 0, 1, nullptr, nullptr,
napi_create_threadsafe_function(env, nullptr, nullptr, workName, 0, 1, nullptr, nullptr,
closure, Closure::callBlockFromMainThread, &closure->tsfn);
if (closure->tsfn) napi_unref_threadsafe_function(env, closure->tsfn);
}
Expand Down Expand Up @@ -301,8 +301,7 @@ bool isObjCBlockObject(id obj) {

napi_value callback = argv[1];

auto closure = new Closure(enc, true);
closure->env = env;
auto closure = new Closure(env, enc, true);
registerBlock(env, closure, callback);

return callback;
Expand Down Expand Up @@ -412,6 +411,9 @@ bool isObjCBlockObject(id obj) {

void FunctionPointer::finalize(napi_env env, void* finalize_data, void* finalize_hint) {
auto ref = (FunctionPointer*)finalize_data;
if (ref == nullptr) {
return;
}
if (ref->ownsCif && ref->cif != nullptr) {
delete ref->cif;
ref->cif = nullptr;
Expand Down
3 changes: 1 addition & 2 deletions NativeScript/ffi/CFunction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ inline napi_value tryCallCompatLibdispatchFunction(napi_env env, napi_callback_i
return nullptr;
}

auto closure = new Closure(std::string("v"), true);
closure->env = env;
auto closure = new Closure(env, std::string("v"), true);
id block = registerBlock(env, closure, argv[1]);
dispatch_block_t dispatchBlock = (dispatch_block_t)block;

Expand Down
2 changes: 2 additions & 0 deletions NativeScript/ffi/Cif.mm
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ inline bool typeRequiresSlowGeneratedNapiDispatch(const std::shared_ptr<TypeConv
switch (type->kind) {
case mdTypeUChar:
case mdTypeUInt8:
case mdTypeBlock:
case mdTypeFunctionPointer:
return true;
default:
return false;
Expand Down
194 changes: 189 additions & 5 deletions NativeScript/ffi/Class.mm
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,13 @@ void setupObjCClassDecorator(napi_env env) {
napi_get_cb_info(env, cbinfo, nullptr, nullptr, &jsThis, nullptr);

Class currentClass = nil;
napi_unwrap(env, jsThis, (void**)&currentClass);
auto bridgeState = ObjCBridgeState::InstanceData(env);
if (bridgeState != nullptr && jsThis != nullptr) {
bridgeState->tryResolveBridgedClassConstructor(env, jsThis, &currentClass);
}
if (currentClass == nil) {
napi_unwrap(env, jsThis, (void**)&currentClass);
}
if (currentClass == nil) {
return nullptr;
}
Expand All @@ -256,7 +262,6 @@ void setupObjCClassDecorator(napi_env env) {
return nullptr;
}

auto bridgeState = ObjCBridgeState::InstanceData(env);
auto find = bridgeState->classesByPointer.find(superClass);
if (find != bridgeState->classesByPointer.end()) {
return get_ref_value(env, find->second->constructor);
Expand All @@ -268,12 +273,103 @@ void setupObjCClassDecorator(napi_env env) {
return get_ref_value(env, bridgedClass->constructor);
}

const char* runtimeName = class_getName(superClass);
if (runtimeName != nullptr && runtimeName[0] != '\0') {
napi_value global = nullptr;
napi_value constructor = nullptr;
bool hasGlobal = false;
napi_get_global(env, &global);
if (napi_has_named_property(env, global, runtimeName, &hasGlobal) == napi_ok && hasGlobal &&
napi_get_named_property(env, global, runtimeName, &constructor) == napi_ok &&
constructor != nullptr) {
return constructor;
}
}

return bridgeState->getObject(env, (id)superClass, kUnownedObject, 0, nullptr);
}

NAPI_FUNCTION(classHasInstance) {
size_t argc = 1;
napi_value argv[1] = {nullptr};
napi_value jsThis = nullptr;
napi_get_cb_info(env, cbinfo, &argc, argv, &jsThis, nullptr);

Class expectedClass = nil;
auto bridgeState = ObjCBridgeState::InstanceData(env);
if (bridgeState != nullptr && jsThis != nullptr) {
bridgeState->tryResolveBridgedClassConstructor(env, jsThis, &expectedClass);
}
if (expectedClass == nil) {
napi_unwrap(env, jsThis, (void**)&expectedClass);
}

bool isInstance = false;
if (expectedClass != nil && argc > 0 && argv[0] != nullptr) {
napi_valuetype valueType = napi_undefined;
if (napi_typeof(env, argv[0], &valueType) == napi_ok &&
(valueType == napi_object || valueType == napi_function)) {
id instance = nil;
if (napi_unwrap(env, argv[0], (void**)&instance) != napi_ok || instance == nil) {
napi_value nativePointer = nullptr;
if (napi_get_named_property(env, argv[0], "__ns_native_ptr", &nativePointer) == napi_ok &&
Pointer::isInstance(env, nativePointer)) {
Pointer* pointer = Pointer::unwrap(env, nativePointer);
instance = pointer != nullptr ? static_cast<id>(pointer->data) : nil;
}
}

if (instance != nil) {
Class currentClass = object_getClass(instance);
while (currentClass != nil) {
if (currentClass == expectedClass) {
isInstance = true;
break;
}
currentClass = class_getSuperclass(currentClass);
}
}
}
}

napi_value result = nullptr;
napi_get_boolean(env, isInstance, &result);
return result;
}

NAPI_FUNCTION(BridgedConstructor) {
NAPI_CALLBACK_BEGIN(16)

napi_value newTarget = nullptr;
napi_get_new_target(env, cbinfo, &newTarget);

napi_valuetype thisType = napi_undefined;
if (jsThis == nullptr || napi_typeof(env, jsThis, &thisType) != napi_ok ||
(thisType != napi_object && thisType != napi_function)) {
napi_create_object(env, &jsThis);

napi_value prototypeOwner = newTarget;
if (prototypeOwner == nullptr || napi_typeof(env, prototypeOwner, &thisType) != napi_ok ||
(thisType != napi_function && thisType != napi_object)) {
prototypeOwner = nullptr;
}

if (prototypeOwner != nullptr) {
napi_value prototype = nullptr;
if (napi_get_named_property(env, prototypeOwner, "prototype", &prototype) == napi_ok &&
prototype != nullptr) {
napi_value global = nullptr;
napi_value objectCtor = nullptr;
napi_value setPrototypeOf = nullptr;
napi_get_global(env, &global);
napi_get_named_property(env, global, "Object", &objectCtor);
napi_get_named_property(env, objectCtor, "setPrototypeOf", &setPrototypeOf);
napi_value setPrototypeArgs[2] = {jsThis, prototype};
napi_call_function(env, objectCtor, setPrototypeOf, 2, setPrototypeArgs, nullptr);
}
}
}

napi_valuetype jsType = napi_undefined;
if (argc > 0) {
napi_typeof(env, argv[0], &jsType);
Expand All @@ -282,14 +378,32 @@ void setupObjCClassDecorator(napi_env env) {
id object = nil;

ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env);
auto ensureWrappedThis = [&](id nativeObject) {
if (jsThis == nullptr || nativeObject == nil) {
return;
}

void* existingWrapped = nullptr;
if (napi_unwrap(env, jsThis, &existingWrapped) == napi_ok && existingWrapped != nullptr) {
return;
}

napi_wrap(env, jsThis, nativeObject, nullptr, nullptr, nullptr);
};

Class cls = (Class)data;

if (jsThis != nullptr) {
napi_value constructor;
napi_value constructor = newTarget;
if (constructor == nullptr && jsThis != nullptr) {
napi_get_named_property(env, jsThis, "constructor", &constructor);
}

if (constructor != nullptr) {
Class newTargetCls = nil;
napi_unwrap(env, constructor, (void**)&newTargetCls);
if (!(bridgeState != nullptr &&
bridgeState->tryResolveBridgedClassConstructor(env, constructor, &newTargetCls))) {
napi_unwrap(env, constructor, (void**)&newTargetCls);
}

if (newTargetCls != nil) {
cls = newTargetCls;
Expand All @@ -315,6 +429,7 @@ void setupObjCClassDecorator(napi_env env) {
return existing;
}

ensureWrappedThis(object);
jsThis = bridgeState->proxyNativeObject(env, jsThis, object);
napi_wrap(env, jsThis, object, nullptr, nullptr, nullptr);
return jsThis;
Expand All @@ -332,6 +447,7 @@ void setupObjCClassDecorator(napi_env env) {
// JS "init" method so constructor arguments participate in selector
// matching (including Swift-style token objects).
object = [cls alloc];
ensureWrappedThis(object);
jsThis = bridgeState->proxyNativeObject(env, jsThis, object);
}

Expand Down Expand Up @@ -642,6 +758,20 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value
superclass = nullptr;
}

napi_value constructorNameValue = nullptr;
napi_create_string_utf8(env, jsConstructorName.c_str(), jsConstructorName.length(),
&constructorNameValue);
napi_property_descriptor constructorNameProp = {
.utf8name = "name",
.method = nullptr,
.getter = nullptr,
.setter = nullptr,
.value = constructorNameValue,
.attributes = napi_default,
.data = nullptr,
};
napi_define_properties(env, constructor, 1, &constructorNameProp);

this->constructor = make_ref(env, constructor);
this->prototype = make_ref(env, prototype);

Expand Down Expand Up @@ -752,6 +882,38 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value
return hasOwn;
};

if (!hasOwnNamedProperty(constructor, "name")) {
napi_value classNameValue = nullptr;
napi_create_string_utf8(env, jsConstructorName.c_str(), NAPI_AUTO_LENGTH, &classNameValue);
napi_property_descriptor nameProperty = {
.utf8name = "name",
.name = nil,
.method = nil,
.getter = nil,
.setter = nil,
.value = classNameValue,
.attributes = (napi_property_attributes)(napi_configurable),
.data = nil,
};
napi_define_properties(env, constructor, 1, &nameProperty);
}

if (!hasOwnNamedProperty(constructor, "length")) {
napi_value zeroValue = nullptr;
napi_create_int32(env, 0, &zeroValue);
napi_property_descriptor lengthProperty = {
.utf8name = "length",
.name = nil,
.method = nil,
.getter = nil,
.setter = nil,
.value = zeroValue,
.attributes = (napi_property_attributes)(napi_configurable),
.data = nil,
};
napi_define_properties(env, constructor, 1, &lengthProperty);
}

if (!hasOwnNamedProperty(constructor, "alloc")) {
napi_property_descriptor allocProperty = {
.utf8name = "alloc",
Expand Down Expand Up @@ -796,6 +958,28 @@ void defineProtocolMembers(napi_env env, ObjCClassMemberMap& members, napi_value
napi_define_properties(env, constructor, 2, slots);
}

napi_value global = nullptr;
napi_value symbolCtor = nullptr;
napi_value hasInstanceSymbol = nullptr;
napi_get_global(env, &global);
napi_get_named_property(env, global, "Symbol", &symbolCtor);
napi_get_named_property(env, symbolCtor, "hasInstance", &hasInstanceSymbol);
bool hasOwnHasInstance = false;
napi_has_own_property(env, constructor, hasInstanceSymbol, &hasOwnHasInstance);
if (!hasOwnHasInstance) {
napi_property_descriptor hasInstanceProperty = {
.utf8name = nil,
.name = hasInstanceSymbol,
.method = JS_classHasInstance,
.getter = nil,
.setter = nil,
.value = nil,
.attributes = (napi_property_attributes)(napi_configurable),
.data = nil,
};
napi_define_properties(env, constructor, 1, &hasInstanceProperty);
}

if (!hasOwnNamedProperty(prototype, "toString")) {
napi_property_descriptor toStringProperty = {
.utf8name = "toString",
Expand Down
2 changes: 1 addition & 1 deletion NativeScript/ffi/ClassBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ObjCProtocol;

class ClassBuilder : public ObjCClass {
public:
ClassBuilder(napi_env env, napi_value constructor);
ClassBuilder(napi_env env, napi_value constructor, Class explicitSuperClass = nullptr);
~ClassBuilder();

void addProtocol(ObjCProtocol* protocol);
Expand Down
Loading
Loading