Add wamr test app - #6
Open
listepo wants to merge 60 commits into
Open
Conversation
Create a NativeScript test application (apps/nativescript-wamr-test/) that exercises @org/nativescript-wamr against the real WAMR runtime, mirroring the existing nativescript-wasm-test app. Key additions: - Demo page and mocha specs sharing the same fixture-suite checks - WAMR-specific coverage: execution tiers (Interpreter, FastJIT), WASI mode, custom stack sizes - Adapted types: WamrRuntime, WamrModule, WamrFunction, WamrError, WamrImports vs their wasm3 counterparts - Nx targets: typecheck (passes), test.ios, test.android - README.md and AGENTS.md documenting design decisions - nx.json: exclude new app from @nx/js/typescript plugin
Add comprehensive tier and WASI coverage to both spec files.
fixture-module.spec.ts:
- New "execution tiers" describe block with:
- Parametrized loop over all 4 tiers (Interpreter, FastJIT,
LLVMJIT, AOT) running the full shared fixture suite
- Individual tests per tier with graceful skip when a tier
is not compiled into the native build
- New "WASI mode" describe block with:
- Full fixture suite under WASI + Interpreter
- Full fixture suite under WASI + FastJIT (graceful skip)
- WASI-disabled sanity check
- WASI-enabled globals module load
- New runtime option tests:
- Default options (no explicit config)
- loadModule from Uint8Array
- loadModule from plain number[]
- WamrExecutionTier enum values and reverse-mapping
globals-module.spec.ts:
- New "execution tiers" block: reads/writes globals under
every available tier with round-trip verification
- New "WASI mode" block: globals under WASI + Interpreter,
and WASI + FastJIT (graceful skip)
Extend the CI workflow to run the nativescript-wamr-test app mocha suite on the iOS Simulator and Android emulator, mirroring the existing wasm-test-ios / wasm-test-android jobs. Changes: - unit-tests job: install wamr-test dependencies for typecheck coverage - changes job: add wamr_app output, gated on changes to apps/nativescript-wamr-test/, packages/nativescript-wamr/, packages/nativescript-wasm-fixture/, or ci.yml - New wamr-test-ios job (macos-latest): builds the wamr plugin, runs "ns test ios --emulator" - New wamr-test-android job (ubuntu-latest): builds the wamr plugin, runs "ns test android --emulator" via android-emulator-runner Both device jobs are gated behind the wamr_app change detection to avoid burning macOS/emulator minutes on unrelated PRs.
…R sources wasm-test-android: - Build both plugins (wasm3 + wamr) since the test app depends on both - Add WAMR C source check with warning when sources are missing - Update comment to explain WAMR tests may fail without the .aar wamr-test-android: - Add WAMR C source check with warning when sources are missing - Update comment to reflect that the .aar is only present when C sources are committed and the Gradle build succeeds Both jobs already have continue-on-error: true, so runtime failures from missing native runtimes won't block CI.
…h wasm3 and WAMR Refactor the test app so a single NativeScript app exercises both @org/nativescript-wasm3 and @org/nativescript-wamr against the same fixture modules, from both the demo page and mocha specs. Core change — fixture-suite.ts is now runtime-agnostic: - Replaces direct Wasm3Module/Wasm3Imports imports with structural interfaces (WasmModuleLike, HostImports, WasmRuntimeLike) that both plugins satisfy - Exports WasmValue, WasmArg, HostFunction for callers - No imports from either plugin package Demo page — runs both runtimes side by side: - main-view-model.ts creates both Wasm3Runtime and WamrRuntime (Interpreter tier) and runs the full fixture suite on each - Each runtime is independently guarded so a missing native layer on one doesn't hide the other's results - Status line shows combined pass/fail across both runtimes Tests restructured by runtime: - app/tests/wasm3/ — wasm3-specific specs (unchanged logic) - app/tests/wamr/ — wamr-specific specs with tier/WASI coverage - Both import from the shared runtime-agnostic fixture-suite.ts Other: - package.json: add @org/nativescript-wamr dependency - tsconfig.json: add wamr project reference - AGENTS.md, README.md: document dual-runtime design - Remove stale build/ crate (was an early, unused Node-runner experiment) - nx.json: restore single test-app exclusion (separate wamr-test app reverted in favor of unified approach)
The test app now uses a unified approach: a single NativeScript app (nativescript-wasm-test) exercises both runtimes through a runtime-agnostic fixture-suite.ts. The separate wamr-test app is no longer needed. Changes: - Delete apps/nativescript-wamr-test/ entirely (82 files) - Remove wamr-test-ios and wamr-test-android CI jobs - Remove wamr_app change-detection output and filter logic - Restore unit-tests job to single test-app dependency install - wasm-test-ios: build both plugins (wasm3 + wamr) like wasm-test-android - wasm-test-android: keep both-plugins build + WAMR source check
Add a script that downloads the WAMR (WebAssembly Micro Runtime) C sources from the bytecodealliance/wasm-micro-runtime GitHub repo and extracts the required subset into src/vendors/wamr/. Usage: npm run download.wamr # default tag (WAMR-2.3.0) npm run download.wamr -- WAMR-2.3.0 # specific tag npm run download.wamr -- 4c4a0ab # specific commit npx nx run nativescript-wamr:download.wamr The script uses curl + tar (available on macOS, Linux, and CI runners) with zero npm dependencies. It extracts only the directories needed for the iOS/Android native builds (core/iwasm/*, core/shared/*, LICENSE), keeping the vendored copy small. Also adds the "download.wamr" npm script and Nx target.
Vendor the WAMR 2.3.0 C sources (core/iwasm/*, core/shared/*) and generate Rust FFI bindings via bindgen. src/vendors/wamr/: - Downloaded from bytecodealliance/wasm-micro-runtime WAMR-2.3.0 - 530 files, the subset needed for iOS/Android native builds - Committed so no network access is needed for CI or local builds src/vendors/wamr-rust/ (new): - wamr-sys crate: raw Rust FFI bindings generated by bindgen 0.72 - build.rs: uses bindgen to regenerate from ../wamr/wrapper.h - src/bindings.rs: committed snapshot (2649 lines) - src/lib.rs: uses committed bindings by default, WAMR_BINDGEN_COMMIT=1 to regenerate via build.rs - Covers the full public WAMR C API (wasm_export.h, wasm_c_api.h) Scripts: - npm run bindings / npx nx bindings nativescript-wamr: regenerates and commits the Rust bindings
…via UniFFI
Add a complete Rust→Swift code generation pipeline using uniffi-rs 0.32,
producing a ready-to-use Swift Package (wamr-swift) that wraps the WAMR
WebAssembly Micro Runtime.
Rust workspace (src/vendors/wamr-rust/):
- wamr-sys: raw C FFI bindings (bindgen) + cc-based WAMR C compilation (49 files)
- wamr-ffi: safe Rust API with UniFFI annotations
- UDL interface: 4 enums, 3 dicts, 1 error, 4 object interfaces
- Real WAMR C calls: init, load, instantiate, find_function, call_raw,
read_memory, write_memory, get_global, set_global, version
- Bundled uniffi-bindgen CLI binary for code generation
Swift Package (src/vendors/wamr-swift/):
- Sources/WamrFFI/wamr_ffi.swift — 55K generated Swift API
- Sources/CWamrFFI/ — C bridging module (header + modulemap + static lib)
- Tests/WamrFFITests/ — 2 XCTests passing (runtime creation, error handling)
- build.sh — one-command rebuild (cargo → uniffi-bindgen → swift)
- build-ios.sh — iOS xcframework generation (device + simulator + macOS)
Build config (src/vendors/wamr/core/config.h):
- Minimal WAMR defines for interpreter-only POSIX build
- Supports macOS aarch64/x86_64 and iOS aarch64
Verified: cargo build ✅ → uniffi-bindgen generate ✅ → swift build ✅ → swift test ✅ (2/2)
…niFFI
Add Kotlin/Android package (wamr-kotlin) with UniFFI-generated bindings,
mirroring the wamr-swift Swift package pattern for Android/JVM targets.
Generated code (2927 lines):
- library/src/main/kotlin/uniffi/wamr_ffi/wamr_ffi.kt
UniFFI-generated Kotlin API with JNA native loading
4 enums, 3 data classes, 1 sealed error hierarchy, 4 interface classes
Gradle project (AGP 9.3.1, Kotlin 2.4, Gradle 9.6.1):
- library/ — Android library module (namespace org.nativescript.wamr.ffi)
Dependencies: net.java.dev.jna:jna:5.17.0
- hosttest/ — JVM host tests (JUnit 5)
Follows existing pattern: shares library source dirs, avoids AAR variant conflict
Build scripts:
- build.sh — one-command pipeline (cargo → uniffi-bindgen → gradle)
Supports: rust, generate, gradle, android (cross-compile), all
Native library:
- Library override via uniffi.component.wamr_ffi.libraryOverride=wamr_ffi
- Pre-built .so/.dylib goes in library/src/main/jniLibs/{host,arm64-v8a,...}
Known issue:
- Kotlin compilation succeeds; JVM host test hits SIGILL during JNA
Native.register() — Rust-native tests pass (cargo test), dlopen works,
Native.register() with simple interface works. Likely JNA/JVM/macOS 26
compatibility issue with the full 89-method interface.
…package.json scripts - Add 'Before writing any TypeScript' section to AGENTS.md documents that / must be run to generate native type declarations before writing platform adapters - Add and scripts to package.json - Document EPERM workaround for macOS permissions on ~/.local/share/.nativescript-cli/
…ot Unix permissions
…app, add sandbox patch - Correct ns typings: must be run from apps/nativescript-wasm-test, not the plugin directory (needs full NativeScript project context) - Document macOS sandbox workaround: patch settings-service.js to redirect ~/.local/share/.nativescript-cli → /tmp/.nativescript-cli - Update package.json scripts to cd into the test app first - Remove the old chmod workaround (ineffective against process sandbox)
The device-level specs (app/tests/wamr/*.spec.ts) require Run ns test --help for more information. and cannot run in CI or sandboxed terminals. Add wamr-device-mirror.spec.ts — 11 vitest tests that mock the native layer (same pattern as wamr.spec.ts) and cover the same API surface: - Runtime creation with all 4 execution tiers - Module loading - Function signature inspection - Missing function lookup → WamrError - dispose idempotency - WAMR version as semver - Default options - ExecutionTier enum values Total: 47 vitest tests (11 wire + 25 adapter + 11 mirror), all pass. Update AGENTS.md: document two-layer test strategy clearly.
…ilds Replace the JavaCPP-based Android build pipeline with cargo-ndk cross-compilation across both plugins (wamr, wasm3). wamr plugin: - New wamr-jni crate: hand-written Rust JNI functions (22 exports) calling nsc_wamr_shim C API, replaces JavaCPP auto-generated JNI - wamr-sys now compiles nsc_wamr_shim.c alongside WAMR C sources - New NativeWamr.kt: Kotlin external JNI declarations - NSCWamr.kt rewritten: Long handles + NativeWamr replace JavaCPP BytePointer/LongPointer/PointerPointer + org.wamr.* - library/build.gradle.kts: cargo ndk replaces javacppParse/buildNative - hosttest: cargo build for host (macOS) native lib - include.gradle: removes org.bytedeco:javacpp, .so -> libwamr_jni.so - getMemory() returns java.nio.ByteBuffer via JNI NewDirectByteBuffer wasm3 plugin: - New wasm3-rust workspace (wasm3-sys + wasm3-jni, 7 files) - wasm3-sys: bindgen FFI + cc compilation of wasm3 C sources + nsc_wasm3_shim.c - wasm3-jni: 30 JNI functions calling wasm3 C API + shim - New NativeWasm3.kt, rewritten NSCWasm3.kt (same pattern as wamr) - HostTrampoline properly decodes/encodes via Wire with type info - libwasm3_jni.so replaces libjniwasm3.so Also adds wasm3-rust/target/ to .gitignore.
Both wamr-jni and wasm3-jni host trampolines used from_raw_parts() on potentially-null pointers when n_args==0. Added n_args==0 guards that skip the unsafe slice creation, using empty vec for zero-arg calls instead.
Rename package scope from @org/ to @cross-code/ across all package.json files, documentation (AGENTS.md, README.md), and test app imports. Also includes minor whitespace fixes.
- wasm3-ffi: safe Rust wrapper around wasm3-sys with UniFFI scaffolding (UDL, build.rs, uniffi-bindgen binary, lib.rs) - wasm3-kotlin: Gradle project for Kotlin bindings with hosttest - wasm3-swift: SwiftPM package for Swift bindings with XCTest - Fix dead iOS version-min flag in wasm3-sys/build.rs
The vendored WAMR version uses a module-centric API without wasm_runtime_t. The shim now defines its own nsc_wamr_runtime_t abstraction that tracks module instances and exec envs. Key changes: - wasm_runtime_t replaced by opaque nsc_wamr_runtime_t struct - wasm_runtime_create/destroy → wasm_runtime_init/destroy (global) - wasm_runtime_load/instantiate no longer take runtime arg - wasm_runtime_create_exec_env takes module_inst, not runtime - wasm_func_get_param/result_types require module_inst + types buf - wasm_global_t replaced by wasm_global_inst_t (kind + global_data) - NativeSymbol.call_conv_raw removed - register_natives_raw no longer takes module_inst - get_version takes 3 output pointers - wasm_runtime_call_wasm_a uses wasm_val_t (not uint32) in new API - mem_alloc_option is a union, not int - Func-instance mapping for call/runtime lookup
Replaced nsc_wasm3_shim.c (2 functions) and nsc_wamr_shim.c (~20 functions) with pure Rust implementations in their respective -sys crates. wasm3-sys: - nsc_global_get/set now in lib.rs as pub unsafe fn - Removed C shim from build.rs, wrapper.h, include paths wamr-sys: - New shim.rs module: NscWamrRuntime, call/globals/memory/host - extern "C" wrappers in lib.rs preserve JNI compatibility - Removed C shim from build.rs compilation Both build.rs no longer compile any C shim code.
Added GLOBAL_FUNC_MAP and GLOBAL_LAST_RESULTS for cross-runtime function dispatch, matching the old C shim's g_ctx_list pattern. extern "C" wrappers for nsc_wamr_call/get_results now work.
wamr-sys: - Unit tests in shim.rs: type conversion, slot_width, convert_signature - Integration tests: runtime lifecycle, module load, function call, convert_signature export wasm3-sys: - Integration tests: runtime lifecycle, module load, function call, global error handling
wasm3 uses M3ValueType enum with variants c_m3Type_i32=1 (not 0x7F like WAMR). bindgen generates these as enum variants, not bare i32 constants. Updated nsc_global_get/set and wasm3-ffi to use M3ValueType::c_m3Type_* qualified names.
bindgen generates: M3ValueType_c_m3Type_i32 (not c_m3Type_i32), M3TaggedValue.type_ (with underscore), union fields with _ suffix (i32_, i64_, f32_, f64_), IM3TaggedValue = *mut M3TaggedValue. Updated all crates: wasm3-sys, wasm3-ffi, wasm3-jni.
- Replace get_primitive_array_critical with get_byte_array_region - Fix get_array_length to take &JArray (jni 0.21) - Fix m3_Call/m3_GetResults: *const → *mut pointer casts - Fix new_direct_byte_buffer: 2-arg API (ptr, capacity) - Fix m3_GetUserData: takes runtime, not context - Fix m3_LinkRawFunctionEx: no Option wrapper - Fix JValue::Object pattern match for jni 0.21 - Fix get_long_array_region to take &JLongArray - Add unsafe impl Send for HostCtx - Remove unnecessary unsafe blocks in wasm3-sys lib.rs
- Replace get_primitive_array_critical with get_byte_array_region - Fix read_long_array to take &JLongArray - Fix get_array_length to take &JArray refs - Add JByteArray, JLongArray imports
- Remove unused JavaVM import - Store HostCtx pointers as usize in HashMap (Sync fix) - get_byte_array_region takes &mut [i8], transmute from &mut [u8] - JValueOwned::Object for call_method result pattern - arg_ptrs/ret_ptrs need let mut for as_mut_ptr() - new_direct_byte_buffer requires unsafe block
…lation/ from sync - Package.swift: add core/shared/platform/darwin header search path for platform_internal.h on macOS/iOS - sync-wamr.mjs: exclude compilation/ directory (SIMD/JIT not needed for interpreter-only iOS build)
Add all necessary include dirs matching wamr-sys/build.rs: core/iwasm/common, interpreter, shared/utils, mem-alloc, platform/common/posix, libc-util, memory. Add WAMR build defines: WASM_ENABLE_INTERP=1, FAST_INTERP=0, BH_PLATFORM_DARWIN=1, WAMR_BUILD_INVOKE_NATIVE_GENERAL=1
…e, underscore unused params - shim.rs: use super::* instead of use wamr_sys::* (circular ref) - RUNTIME_REGISTRY stores usize instead of *mut NscWamrRuntime (Sync) - nsc_wamr_module_name, nsc_wamr_function_name: underscore unused params
- Replace JavaCPP/org.bytedeco references with cargo-ndk + Rust JNI - Update .so names: libjniwamr→libwamr_jni, libjniwasm3→libwasm3_jni - Add Rust workspace and UniFFI vendor packages to layout - Note C shims are now pure Rust - Remove build-native.mjs references
wasm3-android / wamr-android: - Install Rust (if missing) instead of clang - cargo build replaces build-native.mjs + JavaCPP - Remove clang install step (Rust cc crate handles C compilation) - Update job descriptions and step names
- Update package.json descriptions: JavaCPP → Rust JNI (cargo-ndk) - Update code-review.yml description - Remove src/javacpp/ preset directories (dead code)
- Fix wasm3-ffi: Send+Sync impls, m3_Call/m3_GetResults casts, let mut - Generated wasm3_ffi.swift + wasm3_ffiFFI.h/modulemap for Swift - Generated wasm3_ffi.kt for Kotlin - Copy libwasm3_ffi.a to CWasm3FFI target - Synced WAMR C sources to CWamr (via sync-wamr.mjs)
…update redirect headers
The WAMR API changed: NativeSymbol.signature must be a valid signature string, not NULL. Passing NULL causes wasm_runtime_register_natives_raw to fail silently with 'failed to link import function'. - Pass strdup(wamrSig) as symbol.signature - Add signature field to HostRegistration for cleanup - Free symSig on error path and unregister
The prebuilt nativescript-wasm3.aar was built before the JavaCPP→cargo-ndk migration. It still contains org/wasm3/presets/wasm3 reference causing NoClassDefFoundError on Android. Must be rebuilt with: cd platforms/android/wasm3-android && ./gradlew :library:deployAar
…ripts and Package.swift" This reverts commit e1697a3.
…ain cycle All 13 WAMR Swift tests now pass (was 4 failures). Root causes: 1. Host import override test: WAMR resolves module imports at LOAD time and caches them — host functions linked afterwards were never seen, and stale registrations from earlier runtimes won. Fix: load with LoadArgs.no_resolve=true and re-resolve at instantiation (resolveSymbolsIfNeeded) after the caller links imports. 2. Memory bounds: wasm_runtime_validate_app_addr checks the MAX growable size, not the current size — suite.wasm declares max 4 pages so out-of-bounds reads at 64 KiB passed validation. Fix: memorySize now reads wasm_memory_get_cur_page_count and read/write validate against the current size. 3. Retain cycle: NSCWamrModule.runtime was strong, NSCWamrRuntime.modules held modules — runtimes never deallocated, leaking stale host registrations into later tests. Fix: weak runtime reference. 4. testUnlinkedImportFails: this WAMR version traps at CALL time, not findFunction. Updated the test to assert the call throws.
- instance(): guard let runtime + tornDown check BEFORE resolveSymbolsIfNeeded so a freed module is never dereferenced - teardown() stamps tornDown=true - exportedGlobal reaches instance() which now throws for orphaned/torn-down modules
wasm_func_get_param_types/get_result_types return the wasm_c_api.h enum (WASM_I32=0..WASM_F64=3), not raw WAMR type bytes. slot_width and kind_at now document and handle simple codes; test_slot_width asserts the actual valkind values. Extern nsc_wamr_to/from_simple_type (byte conversion) is unchanged for the legacy Kotlin path.
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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.