← 返回首页
9.0.0 — Host objects across all engines, C++ workers, DevTools & build modernization by ammarahm-ed · Pull Request #43 · NativeScript/napi-android · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus

9.0.0 — Host objects across all engines, C++ workers, DevTools & build modernization#43

Open
ammarahm-ed wants to merge 54 commits into
mainfrom
updates
Open

9.0.0 — Host objects across all engines, C++ workers, DevTools & build modernization#43
ammarahm-ed wants to merge 54 commits into
mainfrom
updates

Conversation

ammarahm-ed commented Jul 8, 2026
edited
Loading

Copy link
Copy Markdown
Collaborator
  1. Port all commits and updates from https://github.com/NativeScript/android/ excluding ESM support since hermes does not have support for esm yet and node-api doesn't expose a unified API to load esm modules.
  2. Update JSC, Hermes, QuickJS, PrimJS to latest versions available
  3. Remove Old Hermes engine and make Hermes V1 the default when selecting Hermes
  4. Updated host objects API and added support for host objects in all JS engines including Hermes and JSC
  5. Many small but important performance improvements making the runtime 2x faster in many cases.

ammarahm-ed and others added 30 commits July 1, 2026 20:23
Port of upstream nativescript/android-runtime #1826 (2bab8f5). URL::New unconditionally string-read argv[1] whenever argc > 1, which threw for new URL(x, undefined) / new URL(x, null). Gate the base-URL branch on argv[1] being neither undefined nor null (napi_util::is_of_type), falling through to the no-base parse otherwise. Ports the expanded testURLImpl.js suite (engine-agnostic JS) and adds the migration ledger. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1820 (94ddb15). Use _Exit(0) instead of exit(0) on the locked-screen/direct-boot metadata path so we skip atexit handlers and destructors (a destroyed mutex during exit(0) was causing an ANR). In the napi runtime this error path lives in MetadataBuilder.cpp rather than MetadataNode.cpp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1825 (a983931). compileSdk/targetSdk provided via -P arrive as String; cast them to int in computeCompileSdkVersion/computeTargetSdkVersion so the Android DSL accepts them instead of erroring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1818 (3423e6f). Add -Wl,-z,max-page-size=16384 link option for arm64-v8a/x86_64 so the runtime .so is compatible with 16 KB page-size devices. Also bump default compileSdk and build-tools to 35. Skipped from the upstream commit: the gradle 8.4->8.7 wrapper and AGP 8.3.2->8.5.0 bumps (this fork is already on gradle 8.14.3 / AGP 8.12.1), and the c++_shared->c++_static STL switch (the napi multi-engine build relies on shared libc++ alongside the engine .so files). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime Ada v3 chain (#1830, #1832, #1835, #1884: b31fc5f, 3633aed, 83f611b, 3513ce7, 45fb275). Ada is a vendored single-file library, so the intermediate version bumps collapse into one upgrade to the final 3.3.0. - Upgrade vendored ada.{h,cpp} 2.9.0 -> 3.3.0 (no API breaks in URL/URLSearchParams). - Port the URLPattern API from V8 to Node-API: new modules/url/URLPattern.{cpp,h} backed by ada's url_pattern with a napi RegExp provider (RegExp built via the global constructor and driven through .exec; a move-only NapiRegex wraps the napi_ref; a thread-local env lets create_instance reach the current env). Wire URLPattern::Init into NSRuntimeModules so it is installed as a global. - Register the property as the spec-correct hasRegExpGroups (the original V8 impl exposed a typo'd hasRegexpGroups). - Port testURLPattern.js and register it in mainpage.js. Verified: native build across all ABIs (V8-13) exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of the portable parts of upstream nativescript/android-runtime #1819 (bec401c, 'NDK 27 and Support for Java 21'). - NanoWSD.java: explicit (byte) casts on compound-assignment (Java 21 rejects the implicit narrowing). - NativeScriptAbstractMap: bound SimpleEntry/SimpleImmutableEntry type params with 'extends Serializable' to satisfy Java 21's serializable checks. - android-metadata-generator / runtime-binding-generator: use JavaVersion.VERSION_17 instead of the '17' string. - runtime: add explicit compileOptions VERSION_17. Skipped from upstream: CI workflows, gradlew/wrapper and root package.json version bumps (this fork is already on newer gradle/NDK), and the NDK-27 android_support/FLEXIBLE_PAGE_SIZES bits (already present here). Verified: :runtime:compileDebugJavaWithJavac under JDK 21, exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1845 (87f7f9c). Replace the vendored libzip headers (cpp/zip/include/{zip.h,zipconf.h}) and the four prebuilt static libs (libs/common/<abi>/libzip.a) with 1.11.4. The only consumer, AssetExtractor.cpp, uses stable libzip API unaffected by the bump. Verified: native compile+link across all ABIs (V8-13) exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1848 (78589fd). Bump ns_default_kotlin_version / ns_default_kotlinx_metadata_jvm_version to 2.2.20 and adapt KotlinClassDescriptor to the kotlinx-metadata 2.2.x API: metaClass.enumEntries was replaced by kmEnumEntries (a list of entries), so map it to names before passing to getEnumEntriesAsFields. Verified: metadata-generator compiles under kotlin 2.2.20 and :app:buildMetadata runs it successfully. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1868 (f033061). Register a global queueMicrotask(callback). The V8 original called isolate->EnqueueMicrotask; Node-API has no equivalent, so schedule the callback via Promise.resolve().then(callback), which enqueues a real microtask on every engine the runtime targets and preserves ordering with Promise microtasks (running before timers). Throws TypeError when the argument is not a function. Ports testQueueMicrotask.js and registers it in mainpage.js. Verified: native compile across ABIs exit 0 (on-device ordering asserts not run). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1881 (5cb66ee). A weak global ref cached in the ObjectManager LRUCache can end up pointing to a collected Java object; reusing it crashes. Add an optional cache-validity callback to LRUCache that is consulted on lookup — ObjectManager supplies ValidateWeakGlobalRefCallback (IsSameObject(obj, NULL)) so stale weak refs are evicted and re-created instead of returned. Adds JEnv::isSameObject and LRUCache::evictKey. Verified: native compile across ABIs exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1886 (c05e283). Make ObjectManager::GetClassName (and the JAVA_LANG_CLASS / GET_NAME_METHOD_ID JNI ids it uses) static, so NativeScriptException can resolve a Java class name during a rethrow without going through Runtime::GetRuntime(env)->GetObjectManager(). That retrieval can throw when the env/runtime is unavailable mid-exception, which is exactly when rethrow runs. GetJavaExceptionFromEnv keeps the instance (it needs GetOrCreateObjectId/CreateJSWrapper). The runtime-lookup robustness from the upstream commit is already present in the napi GetRuntime(env). Verified: native compile+link across ABIs exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1887 (3ecd707). - Stop finalizing buildMetadata by the R8 minify task (the ordering caused proguard build failures). - Ship consumer proguard rules from the runtime library (consumerProguardFiles + consumer-rules.pro) that keep com.tns.*, generated bindings (com.tns.gen.**), internal classes, and RuntimeVisibleAnnotations so reflection/metadata survive minification. Verified: gradle config evaluation exit 0 (full effect requires a release R8 build). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of the remaining gap from upstream nativescript/android-runtime #1895 (3e61cef). The napi ForEach already used the spec callback order (value, key, searchParams), the optional thisArg, and stopped on a thrown callback. It still iterated via get_keys()+get(key), which returns the first value for a repeated key, so ?a=1&a=2 reported (1) twice. Switch to get_entries() so each entry yields its own value. Adds the forEach unit tests. Verified: native compile across ABIs exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Partial port of upstream nativescript/android-runtime #1908 (e924542). Install a std::terminate handler (LogAndAbortUncaught) in Runtime::Init that logs the in-flight native exception's message before _Exit, so an uncaught NativeScriptException/std::exception is diagnosable instead of a bare abort. Adds NativeScriptException::what() to expose the stored message. The upstream commit's larger NativeScriptException logging rewrite is V8-stack specific and does not map onto this fork's napi exception path (which already builds its own messages/stack traces); not ported. Verified: native compile across ABIs exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1920 (1fd144f). When multithreaded JS is enabled the runtime's instance maps are accessed from multiple threads, so back them with thread-safe maps. Widen the field types to the Map<> interface (only Map methods are used) and, when getEnableMultithreadedJavascript() is set, initialize them as ConcurrentHashMap / Collections.synchronizedMap. loadedJavaScriptExtends stays a synchronized HashMap because it stores null values. Adds ConcurrentAccessTest.java + testConcurrentAccess.js. Verified: :runtime: and :app: Java compile exit 0 (concurrency behavior is device-only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record 085bc4f + 3c956cf as deferred — a JNI perf optimization that must be ported together and device-validated across Android 8-11+ (ART calling-convention crash risk), which can't be verified in this environment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1906 (df4e81b). In callJSMethod, re-resolve the owning runtime not just when the runtime id misses the cache but also when the resolved runtime does not actually own the target object (getJavaObjectID == null). This happens when a worker fires a JS method on an object created in the main thread or another worker. Also surface a clearer error from ObjectManager::GetJavaObjectByJsObject when GetJavaObjectByID fails (includes the object id), which usually indicates the id belongs to a different runtime/thread. Verified: native + Java compile exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dentifier .extend keys Batches three upstream jsparser fixes (build/ is gitignored, rebuilt on demand): - 54185ff (#1796): webpack devtool:false (we don't use the sourcemap). - e963d6c (#1797): traverseFiles throws when no files are found, so a broken webpack build fails loudly instead of silently succeeding. - dd2984b (#1950): _getIdentifierKeyName guards the .extend({}) property walk against spreads, computed keys, and non-Identifier keys, which can't map to a Java binding and previously crashed the parser (e.g. bundled Zod schemas). Verified: jsparser 'npm run build' (webpack) compiles cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1910 (49da71b). Drop the buildMetadata task's dependsOn on compile*ArtProfile and optimize*Resources — under proguard/R8 these created a circular task dependency that broke the build. Verified: :app: config evaluation exit 0 (full effect requires a release R8 build). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1938 (2b6cb03). Harden dex directory setup: create code_cache/secondary-dexes if missing and fall back to files/secondary-dexes when it is unavailable/unwritable (RuntimeHelper). Defensively create the parent directory in ProxyGenerator.saveProxy before createNewFile to avoid ENOENT crashes on newer Android versions. Verified: app + generator Java compile exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…968) Record c9d41e6 + fce8e29 as deferred — reflection into hidden BaseDexClassLoader internals, API-level-branched, with a release-only crash the second commit fixes. Must be ported as the combined end-state and validated on real devices across Android versions + a release build, which isn't possible here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… inspector, timers, URLSearchParams spec) Complete the 2bab8f5..HEAD triage: defer the ESM-dependent module-path fixes (92c2654/45ed1f6), the workers->C++ refactor (a84d3c7), the two inspector features (55da2da/4b5ab0a), the timers rewrite (bfd7650), and the URLSearchParams spec overhaul (89893ae), each with rationale. Add a status summary to the ledger. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tration Port of upstream nativescript/android-runtime #1921 + #1942 (085bc4f + 3c956cf) as their combined end-state. Hot native calls (generateNewObjectId, getCurrentRuntimeId, getPointerSize as @CriticalNative; notifyGc, SetManualInstrumentationMode as @fastNative) use Android's optimized JNI calling conventions. Because dynamic JNI lookup of these annotated methods is unimplemented on Android 8-10 and buggy on 11, JNI_OnLoad binds the annotated variants via RegisterNatives, and the Java side dispatches to auto-bound *Legacy variants when SDK_INT < 26. generateNewObjectId becomes static and is called with (nullptr, nullptr) since its impl ignores env/obj; getCurrentRuntimeId uses Runtime::Current() so it needs no JNIEnv. notifyGc keeps this fork's (int, int[]) signature -> "(I[I)Z". Verified: native + Java compile across ABIs exit 0. Calling-convention correctness must be validated on-device (Android 8-11, 12+, and a release build). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ammarahm-ed and others added 24 commits July 4, 2026 23:39
…or Class.forName Port of upstream nativescript/android-runtime #1951 + #1968 (c9d41e6 + fce8e29) as their combined end-state. Runtime-generated proxy classes normally live in isolated DexClassLoaders that Class.forName() can't see, so framework components (e.g. FragmentFactory) fail to instantiate them. On the main thread, inject the generated dex into the app's own BaseDexClassLoader: via addDexPath on API 24+, or by splicing dexElements built with DexPathList.makePathElements/makeDexElements on older versions. Building the element through the target loader keeps the DexFile single-owned, avoiding ART's "registered with multiple class loaders" crash on release builds; injection failures fall back to the isolated DexClassLoader. Adds testClassForNameDiscovery.js. Verified: Java compile exit 0. Hidden-API reflection is Android-version-sensitive and must be validated on-device across versions and a release build (a try/catch degrades gracefully if access is blocked). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…3 total) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er fds Port of upstream nativescript/android-runtime #bfd7650 as the napi combined end-state. Timers previously rode a pipe fd registered with ALooper_addFd plus a background watcher thread, so timer callbacks and Handler messages lived in two queues with no mutual ordering. They now ride the runtime thread's Java MessageQueue via a per-runtime com.tns.TimerHandler: - each scheduled timer enqueues one anonymous "due token" (sendMessageAtTime), so timers share a single queue with Handler.post/postDelayed and fire in exact MessageQueue order; - native FireTimer() picks the earliest-due entry of sortedTimers_ (sorted by exact sub-millisecond dueTime) per token, preserving JS relative order despite the ms-quantized Java queue; - due-now timers post at (long)now (FIFO tie with postDelayed(0)); future timers post at ceil(dueTime) so they never fire early. Removes the pipe/ALooper/watcher-thread/mutex/condvar machinery, and fixes the pre-existing callback napi_ref leak by releasing all refs in TimerTask::Unschedule. Adds testNativeTimers.js. Verified: native + Java compile exit 0; ordering semantics must be validated on-device. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of upstream nativescript/android-runtime #1970 (89893ae) to napi. Constructor now implements the WebIDL union: string, record<USVString,USVString>, sequence<sequence<USVString>>, and primitive->USVString, dispatched via napi_typeof and @@iterator detection (napi_get_all_property_names for records; an ES-iterator driver with IteratorClose for sequences; napi_coerce_to_string for coercion). keys()/values()/entries() now return live spec iterators (a { next } object walking ada operator[], duplicate-key correct, iterable via Symbol.iterator, tagged with Symbol.toStringTag, backed by a napi_ref freed in a finalizer); the default prototype[@@iterator] aliases entries. delete()/has() accept an optional value; get() returns null (not undefined) for a missing name; every method brand-checks (Illegal invocation) and coerces args to USVString. Ports the full spec test suite (testURLSearchParamsImpl.js). Verified: native compile across ABIs exit 0; spec behavior is exercised by the ported tests on-device. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of the engine-agnostic core of upstream nativescript/android-runtime #1972 (a84d3c7), scoped to napi/multi-engine. Worker threading and messaging move from Java (HandlerThread + Handlers) into C++: new runtime/workers/{WorkerWrapper,ConcurrentQueue,LooperTasks,WorkerMessage}. WorkerWrapper owns the worker std::thread (JVM attach -> Java initWorkerRuntime/ runWorkerLoop/detachWorkerRuntime -> C++ inbox pump), with a parent->worker ConcurrentQueue (eventfd+ALooper) inbox and a worker->parent LooperTasks queue, nested-worker cascade, and a workerId/env registry. Removes the Java WorkerThread/ WorkerThreadHandler/MainThreadHandler worker branches, initWorker, the 6 worker native decls + JNI entrypoints, and MessageType.java / JavaScriptErrorMessage.java. This removes two JNI transitions plus a Java Message/Handler allocation and dispatch per postMessage. Scoped out (V8-only, no Node-API surface): v8::ValueSerializer structured clone and SharedArrayBuffer transfer -> payloads stay JSON on every engine (same semantics as before). terminate() is cooperative (no v8::TerminateExecution equivalent) -- a worker in a synchronous busy-loop is not preempted; it stops once the current turn unwinds and the looper quits. Verified: native + Java compile across ABIs exit 0. Worker threading/lifecycle correctness is device-only -- run the worker test suite on a device. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the ada 2.9.0 -> 3.3.0 upgrade (8109ff9). ada 3.3.0 uses C++20 (std::endian, concepts) and is compiled for every engine via the URL module, but CMakeLists only passed -std=c++20 for V8_13 (and the empty `elseif ()` set nothing for the rest), so QuickJS/Hermes/JSC/PrimJS/V8-10/V8-11 builds failed to compile ada.h. Pass -std=c++20 for all engines. Verified: QUICKJS_NG native build now compiles across ABIs (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eb Worker isolates Port of upstream nativescript/android-runtime #1969 (55da2da) + #1973 (4b5ab0a), adapted to the fork's napi+v8 hybrid inspector and the new napi WorkerWrapper. #1969: serve source maps to Chrome DevTools via Network.loadNetworkResource / IO.read / IO.close (disk-served), rewrite sourceMapURL to nsruntime:// (with a disableSourceMapURLRewrite opt-out), a socket-thread message fast path, and resource-stream cleanup on disconnect. #1973: attach DevTools to Web Worker isolates. New WorkerInspectorClient (per-worker V8 inspector Target with a flat CDP session) + Target-domain worker-session routing and console history in JsV8InspectorClient. Hooked into the new napi WorkerWrapper: CreateInspector(napi_env)/DestroyInspector() in BackgroundLooper around RunWorker, NotifyTerminating() on the cooperative terminate, and consoleLogCallback routed via WorkerWrapper::FromEnv. All inspector code is gated on #if defined(__V8__) && defined(APPLICATION_IN_DEBUG), so non-V8 and release builds are unaffected (verified: QuickJS build green). Verified: V8-13 debug + QuickJS native + Java compile exit 0. Actual DevTools attach, worker breakpoints, source-map fetch and pause-interrupt are device+DevTools-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A Kotlin companion object that declares a nested class caused the tree builder to create a shadow "Companion" child node, hiding the real package-level companion sibling and dropping its members. Descend into the companion sibling during the outer-class walk and reuse any pre-created sibling at the leaf, order-independently
…jects in JSC This only supports x86_64 and arm64_v8a abis.
… adapter for primjs Switchs to a simple adapter that is abi stable with primjs updates
Copilot AI review requested due to automatic review settings July 8, 2026 14:54

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 46b1ee93-e364-4d59-978c-4f5afa4fe652

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches 🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch updates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

Copy link
Copy Markdown
Collaborator

@ammarahm-ed awesome. Will only be able to test in two weeks

Copy link
Copy Markdown
Collaborator Author

@ammarahm-ed awesome. Will only be able to test in two weeks

Doesn't work still on nativescript 9.x, all runtime tests pass yet ns 9 project doesn't work.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Footer

© 2026 GitHub, Inc.