Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b16346f
refactor: move objc ffi under platform namespace
DjDeveloperr Jun 6, 2026
055964e
refactor: move android jni napi ffi into shared tree
DjDeveloperr Jun 6, 2026
a0a2ed8
refactor: move android runtime sources into shared tree
DjDeveloperr Jun 6, 2026
8d214e2
refactor: move android napi engines into shared tree
DjDeveloperr Jun 6, 2026
04c486b
refactor: wire android build to unified runtime layout
DjDeveloperr Jun 6, 2026
3aab3c3
fix(apple): update objc ffi generated paths
DjDeveloperr Jun 7, 2026
4def1b9
fix(android): align hermes napi adapters
DjDeveloperr Jun 7, 2026
86dec82
fix(android): preserve unified runtime behavior
DjDeveloperr Jun 7, 2026
da00c3a
fix(android): stabilize runtime test harness
DjDeveloperr Jun 7, 2026
fd62fdb
test(android): gate engine-specific runtime specs
DjDeveloperr Jun 7, 2026
6bff08e
fix: clean up runtime refactor paths
DjDeveloperr Jun 7, 2026
15d7c2a
refactor(android): unify static hermes backend
DjDeveloperr Jun 8, 2026
9891875
fix(jni): handle static hermes constructor receivers
DjDeveloperr Jun 8, 2026
5bc3504
fix(objc): preserve bridge object ownership
DjDeveloperr Jun 8, 2026
1668c88
fix(quickjs): clear weak refs before forced gc
DjDeveloperr Jun 8, 2026
6ba820c
test(ios): skip app main in scripted runner
DjDeveloperr Jun 8, 2026
a0e0967
revert(android): restore runtime test parity
DjDeveloperr Jun 14, 2026
1dae9f7
fix(android): clear quickjs weak refs before gc
DjDeveloperr Jun 14, 2026
6d556df
chore(android): refresh native vscode paths
DjDeveloperr Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ packages/*/types
SwiftBindgen

# Generated Objective-C/C dispatch wrappers
NativeScript/ffi/*/GeneratedSignatureDispatch.inc
NativeScript/ffi/*/GeneratedSignatureDispatch.inc.stamp
NativeScript/ffi/*/GeneratedGsdSignatureDispatch.inc
NativeScript/ffi/*/GeneratedGsdSignatureDispatch.inc.stamp
NativeScript/ffi/**/GeneratedSignatureDispatch.inc
NativeScript/ffi/**/GeneratedSignatureDispatch.inc.stamp
NativeScript/ffi/**/GeneratedGsdSignatureDispatch.inc
NativeScript/ffi/**/GeneratedGsdSignatureDispatch.inc.stamp

# Packaged native framework artifacts
packages/*/NativeScript.xcframework/
Expand Down
70 changes: 35 additions & 35 deletions NativeScript/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -228,63 +228,63 @@ include_directories(
)

set(FFI_SHARED_SOURCE_FILES
ffi/shared/Tasks.cpp
ffi/objc/shared/Tasks.cpp
)

set(FFI_NAPI_SOURCE_FILES
ffi/napi/AutoreleasePool.mm
ffi/napi/Protocol.mm
ffi/napi/ObjCBridge.mm
ffi/napi/Block.mm
ffi/napi/Class.mm
ffi/napi/Closure.mm
ffi/napi/ClassMember.mm
ffi/napi/Cif.mm
ffi/napi/TypeConv.mm
ffi/napi/Util.mm
ffi/napi/Struct.mm
ffi/napi/ObjectRef.mm
ffi/napi/JSObject.mm
ffi/napi/Enum.mm
ffi/napi/Variable.mm
ffi/napi/Object.mm
ffi/napi/CFunction.mm
ffi/napi/Interop.mm
ffi/napi/InlineFunctions.mm
ffi/napi/ClassBuilder.mm
ffi/objc/napi/AutoreleasePool.mm
ffi/objc/napi/Protocol.mm
ffi/objc/napi/ObjCBridge.mm
ffi/objc/napi/Block.mm
ffi/objc/napi/Class.mm
ffi/objc/napi/Closure.mm
ffi/objc/napi/ClassMember.mm
ffi/objc/napi/Cif.mm
ffi/objc/napi/TypeConv.mm
ffi/objc/napi/Util.mm
ffi/objc/napi/Struct.mm
ffi/objc/napi/ObjectRef.mm
ffi/objc/napi/JSObject.mm
ffi/objc/napi/Enum.mm
ffi/objc/napi/Variable.mm
ffi/objc/napi/Object.mm
ffi/objc/napi/CFunction.mm
ffi/objc/napi/Interop.mm
ffi/objc/napi/InlineFunctions.mm
ffi/objc/napi/ClassBuilder.mm
)

set(FFI_ENGINE_SHARED_SOURCE_FILES
ffi/shared/MetadataState.mm
ffi/objc/shared/MetadataState.mm
)

set(FFI_HERMES_ENGINE_SOURCE_FILES
${FFI_ENGINE_SHARED_SOURCE_FILES}
ffi/hermes/NativeApiJsi.mm
ffi/objc/hermes/NativeApiJsi.mm
)

set(FFI_V8_ENGINE_SOURCE_FILES
${FFI_ENGINE_SHARED_SOURCE_FILES}
ffi/v8/NativeApiV8.mm
ffi/v8/NativeApiV8HostObjects.mm
ffi/v8/NativeApiV8Runtime.mm
ffi/v8/NativeApiV8Value.mm
ffi/objc/v8/NativeApiV8.mm
ffi/objc/v8/NativeApiV8HostObjects.mm
ffi/objc/v8/NativeApiV8Runtime.mm
ffi/objc/v8/NativeApiV8Value.mm
)

set(FFI_JSC_ENGINE_SOURCE_FILES
${FFI_ENGINE_SHARED_SOURCE_FILES}
ffi/jsc/NativeApiJSC.mm
ffi/jsc/NativeApiJSCHostObjects.mm
ffi/jsc/NativeApiJSCRuntime.mm
ffi/jsc/NativeApiJSCValue.mm
ffi/objc/jsc/NativeApiJSC.mm
ffi/objc/jsc/NativeApiJSCHostObjects.mm
ffi/objc/jsc/NativeApiJSCRuntime.mm
ffi/objc/jsc/NativeApiJSCValue.mm
)

set(FFI_QUICKJS_ENGINE_SOURCE_FILES
${FFI_ENGINE_SHARED_SOURCE_FILES}
ffi/quickjs/NativeApiQuickJSHostObjects.mm
ffi/quickjs/NativeApiQuickJS.mm
ffi/quickjs/NativeApiQuickJSRuntime.mm
ffi/quickjs/NativeApiQuickJSValue.mm
ffi/objc/quickjs/NativeApiQuickJSHostObjects.mm
ffi/objc/quickjs/NativeApiQuickJS.mm
ffi/objc/quickjs/NativeApiQuickJSRuntime.mm
ffi/objc/quickjs/NativeApiQuickJSValue.mm
)

set(SOURCE_FILES
Expand Down
2 changes: 1 addition & 1 deletion NativeScript/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include "runtime/Runtime.h"
#include "runtime/RuntimeConfig.h"
#include "segappend.h"
#include "ffi/shared/Tasks.h"
#include "ffi/objc/shared/Tasks.h"
#include "BundleLoader.h"

using namespace nativescript;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@
using namespace std;
using namespace tns;

namespace {
napi_value EnsurePlainConstructorThis(napi_env env, napi_value jsThis, napi_value prototype) {
if (!napi_util::is_null_or_undefined(env, jsThis)) {
return jsThis;
}

napi_value receiver = nullptr;
if (napi_create_object(env, &receiver) != napi_ok || receiver == nullptr) {
return nullptr;
}

if (!napi_util::is_null_or_undefined(env, prototype)) {
napi_util::setPrototypeOf(env, receiver, prototype);
}

return receiver;
}
}

void ArgConverter::Init(napi_env env) {
auto cache = GetTypeLongCache(env);

Expand Down Expand Up @@ -98,15 +117,24 @@ napi_value ArgConverter::NativeScriptLongToStringFunctionCallback(napi_env env,
napi_value ArgConverter::NativeScriptLongFunctionCallback(napi_env env, napi_callback_info info) {
try {
NAPI_CALLBACK_BEGIN(1);
napi_value newTarget;
napi_get_new_target(env, info, &newTarget);
napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget)
? napi_util::get_prototype(env, newTarget)
: nullptr;
napi_value receiver = EnsurePlainConstructorThis(env, jsThis, receiverPrototype);
if (receiver == nullptr) {
return nullptr;
}
auto cache = GetTypeLongCache(env);
napi_value javaLong;
napi_get_boolean(env, true, &javaLong);
napi_set_named_property(env, jsThis, "javaLong", javaLong);
napi_set_named_property(env, receiver, "javaLong", javaLong);

NumericCasts::MarkAsLong(env, jsThis, argv[0]);
NumericCasts::MarkAsLong(env, receiver, argv[0]);

napi_set_named_property(env, jsThis, "prototype", napi_util::get_ref_value(env, cache->NanNumberObject));
return jsThis;
napi_set_named_property(env, receiver, "prototype", napi_util::get_ref_value(env, cache->NanNumberObject));
return receiver;

} catch (NativeScriptException &e) {
e.ReThrowToNapi(env);
Expand Down Expand Up @@ -262,4 +290,4 @@ void ArgConverter::onDisposeEnv(napi_env env) {
}
}

robin_hood::unordered_map<napi_env, ArgConverter::TypeLongOperationsCache *> ArgConverter::s_type_long_operations_cache;
robin_hood::unordered_map<napi_env, ArgConverter::TypeLongOperationsCache *> ArgConverter::s_type_long_operations_cache;
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@

using namespace std;

namespace {
napi_value EnsureConstructorThis(napi_env env, napi_value jsThis, napi_value prototype) {
if (!napi_util::is_null_or_undefined(env, jsThis)) {
return jsThis;
}

auto runtime = Runtime::GetRuntime(env);
auto receiver = runtime->GetObjectManager()->GetEmptyObject();
if (!napi_util::is_null_or_undefined(env, receiver) &&
!napi_util::is_null_or_undefined(env, prototype)) {
napi_util::setPrototypeOf(env, receiver, prototype);
}

return receiver;
}
}

void MetadataNode::Init(napi_env env) {
auto cache = GetMetadataNodeCache(env);
}
Expand All @@ -41,7 +58,12 @@ napi_value MetadataNode::CreateArrayObjectConstructor(napi_env env) {
napi_define_class(env, name, strlen(name),
[](napi_env env, napi_callback_info info) -> napi_value {
NAPI_CALLBACK_BEGIN(0)
return jsThis;
napi_value newTarget;
napi_get_new_target(env, info, &newTarget);
napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget)
? napi_util::get_prototype(env, newTarget)
: nullptr;
return EnsureConstructorThis(env, jsThis, receiverPrototype);
}, nullptr, 0, nullptr, &arrayConstructor);
napi_value proto = napi_util::get_prototype(env, arrayConstructor);
ObjectManager::MarkObject(env, proto);
Expand All @@ -67,11 +89,14 @@ napi_value MetadataNode::CreateExtendedJSWrapper(napi_env env, ObjectManager *ob

if (cacheData.node != nullptr) {
extInstance = objectManager->GetEmptyObject();
if (napi_util::is_null_or_undefined(env, extInstance)) {
return nullptr;
}
ObjectManager::MarkSuperCall(env, extInstance);
napi_value extendedCtorFunc = napi_util::get_ref_value(env,
cacheData.extendedCtorFunction);
napi_util::setPrototypeOf(env, extInstance,
napi_util::get_prototype(env, extendedCtorFunc));
napi_value extendedPrototype = napi_util::get_prototype(env, extendedCtorFunc);
napi_util::setPrototypeOf(env, extInstance, extendedPrototype);

napi_set_named_property(env, extInstance, CONSTRUCTOR, extendedCtorFunc);

Expand Down Expand Up @@ -323,19 +348,21 @@ napi_value MetadataNode::ExtendedClassConstructorCallback(napi_env env, napi_cal
napi_value newTarget;
napi_get_new_target(env, info, &newTarget);
if (napi_util::is_null_or_undefined(env, newTarget)) return nullptr;
napi_value receiver = EnsureConstructorThis(env, jsThis, napi_util::get_prototype(env, newTarget));
if (napi_util::is_null_or_undefined(env, receiver)) return nullptr;

auto extData = reinterpret_cast<ExtendedClassCallbackData *>(data);
SetInstanceMetadata(env, jsThis, extData->node);
SetInstanceMetadata(env, receiver, extData->node);

napi_value implementationObject = napi_util::get_ref_value(env,
extData->implementationObject);
ObjectManager::MarkSuperCall(env, jsThis);
ObjectManager::MarkSuperCall(env, receiver);

string fullClassName = extData->fullClassName;

ArgsWrapper argWrapper(argv.data(), argc, ArgType::Class);
napi_value jsThisProxy;
bool success = CallbackHandlers::RegisterInstance(env, jsThis, fullClassName, argWrapper,
bool success = CallbackHandlers::RegisterInstance(env, receiver, fullClassName, argWrapper,
implementationObject, false,
&jsThisProxy, extData->node->m_name);

Expand Down Expand Up @@ -403,23 +430,30 @@ napi_value MetadataNode::InterfaceConstructorCallback(napi_env env, napi_callbac
auto node = reinterpret_cast<MetadataNode *>(data);

auto className = node->m_implType;
napi_value newTarget;
napi_get_new_target(env, info, &newTarget);
napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget)
? napi_util::get_prototype(env, newTarget)
: nullptr;
napi_value receiver = EnsureConstructorThis(env, jsThis, receiverPrototype);
if (napi_util::is_null_or_undefined(env, receiver)) return nullptr;

SetInstanceMetadata(env, jsThis, node);
SetInstanceMetadata(env, receiver, node);

ObjectManager::MarkSuperCall(env, jsThis);
ObjectManager::MarkSuperCall(env, receiver);


napi_util::setPrototypeOf(env, implementationObject,
napi_util::getPrototypeOf(env, jsThis));
napi_util::getPrototypeOf(env, receiver));

napi_util::setPrototypeOf(env, jsThis, implementationObject);
napi_util::setPrototypeOf(env, receiver, implementationObject);

napi_set_named_property(env, jsThis, CLASS_IMPLEMENTATION_OBJECT, implementationObject);
napi_set_named_property(env, receiver, CLASS_IMPLEMENTATION_OBJECT, implementationObject);

ArgsWrapper argsWrapper(argv.data(), argc, ArgType::Interface);

napi_value jsThisProxy;
auto success = CallbackHandlers::RegisterInstance(env, jsThis, className, argsWrapper,
auto success = CallbackHandlers::RegisterInstance(env, receiver, className, argsWrapper,
implementationObject, true, &jsThisProxy);
return jsThisProxy;

Expand All @@ -444,8 +478,15 @@ napi_value MetadataNode::ClassConstructorCallback(napi_env env, napi_callback_in
try {

auto node = reinterpret_cast<MetadataNode *>(data);
napi_value newTarget;
napi_get_new_target(env, info, &newTarget);
napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget)
? napi_util::get_prototype(env, newTarget)
: nullptr;
napi_value receiver = EnsureConstructorThis(env, jsThis, receiverPrototype);
if (napi_util::is_null_or_undefined(env, receiver)) return nullptr;

SetInstanceMetadata(env, jsThis, node);
SetInstanceMetadata(env, receiver, node);

string extendName;
auto className = node->m_name;
Expand All @@ -454,7 +495,7 @@ napi_value MetadataNode::ClassConstructorCallback(napi_env env, napi_callback_in

ArgsWrapper argsWrapper(argv.data(), argc, ArgType::Class);
napi_value jsThisProxy;
bool success = CallbackHandlers::RegisterInstance(env, jsThis, fullClassName, argsWrapper,
bool success = CallbackHandlers::RegisterInstance(env, receiver, fullClassName, argsWrapper,
nullptr, false, &jsThisProxy, className);

return jsThisProxy;
Expand Down Expand Up @@ -901,6 +942,7 @@ napi_value MetadataNode::PackageGetterCallback(napi_env env, napi_callback_info
NativeScriptException nsEx(std::string("Error: c++ exception!"));
nsEx.ReThrowToNapi(env);
}
return nullptr;
}

void MetadataNode::RegisterSymbolHasInstanceCallback(napi_env env, const MetadataTreeNode *treeNode,
Expand Down Expand Up @@ -2160,4 +2202,4 @@ robin_hood::unordered_map<MetadataTreeNode *, MetadataNode *> MetadataNode::s_tr
tns::ConcurrentMap<napi_env, MetadataNode::MetadataNodeCache *> MetadataNode::s_metadata_node_cache;
robin_hood::unordered_map<napi_env, napi_ref> MetadataNode::s_arrayObjects;

bool MetadataNode::s_profilerEnabled = false;
bool MetadataNode::s_profilerEnabled = false;
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,12 @@ ObjectManager::GetJSInstanceInfoFromRuntimeObject(napi_value object) {
}

bool ObjectManager::IsRuntimeJsObject(napi_value object) {
bool result;
napi_has_named_property(m_env, object, PRIVATE_IS_NAPI, &result);
if (object == nullptr) return false;

bool result = false;
if (napi_has_named_property(m_env, object, PRIVATE_IS_NAPI, &result) != napi_ok) {
return false;
}
return result;
}

Expand Down Expand Up @@ -513,8 +517,21 @@ napi_value ObjectManager::GetEmptyObject() {
napi_get_and_clear_last_exception(m_env, &ex);

napi_value jsWrapper = nullptr;
auto status = napi_new_instance(m_env, emptyObjCtorFunc, 0, nullptr, &jsWrapper);
if (status == napi_ok && !napi_util::is_null_or_undefined(m_env, jsWrapper)) {
return jsWrapper;
}

napi_get_and_clear_last_exception(m_env, &ex);

status = napi_create_object(m_env, &jsWrapper);
if (status != napi_ok || jsWrapper == nullptr) return nullptr;

napi_new_instance(m_env, emptyObjCtorFunc, 0, nullptr, &jsWrapper);
MarkObject(m_env, jsWrapper);
auto prototype = napi_util::get_prototype(m_env, emptyObjCtorFunc);
if (!napi_util::is_null_or_undefined(m_env, prototype)) {
napi_util::setPrototypeOf(m_env, jsWrapper, prototype);
}

if (napi_util::is_null_or_undefined(m_env, jsWrapper)) {
return nullptr;
Expand Down Expand Up @@ -610,4 +627,4 @@ void ObjectManager::OnGarbageCollected(JNIEnv *jEnv, jintArray object_ids) {
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include <jsi/jsi.h>

#include "ffi/shared/NativeApiBackendConfig.h"
#include "ffi/objc/shared/NativeApiBackendConfig.h"

namespace nativescript {

Expand Down
Loading