Repository navigation
Conversation
Expose bounded ASCII and UTF-16 JSON buffers through Value and IRuntime, with Runtime fallbacks, decorator forwarding, and recorded tracing fallbacks. Hermes borrows the caller's buffer synchronously and reuses its existing raw parser specializations. Cover JSON roots, bounded buffers, surrogate preservation, error paths, GC movement, decorators, and trace replay. Add a Release benchmark against cached JSON.parse and the native UTF-8 API. Local prototype pending upstream API design feedback.
This branch was successfully deployed
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.
Summary
Add
Value::createFromJsonASCIIandValue::createFromJsonUtf16, with matching runtime methods.Nitro's Android AnyJSON converter receives JSON as UTF-16 from a Java String. It currently creates a JSI string and calls cached
JSON.parse. Converting to UTF-8 first was slower in the earlier emulator benchmark: Hermes's UTF-8 entry point uses a streaming lexer, whileJSON.parseselects the raw ASCII/UTF-16 lexer with SIMD string scanning.The new Hermes methods borrow bounded native buffers synchronously and call those existing raw parser specializations. They avoid creating the JS input string and calling a JS function. Every JSON root returns as a
Value, including primitives, null, and arrays. UTF-16 preserves raw and escaped lone surrogates. Length counts bytes for ASCII and code units for UTF-16; explicit NUL and malformed tails are consumed as input. The buffer stays valid and unchanged until the call returns, and is not retained. ASCII bytes must be below 128, with a debug assertion for misuse. Parsing allocates; JNI callers must not holdGetStringCriticalacross it.Runtimeprovides string-plus-JSON.parsedefaults. Both decorators forward the methods, andWithRuntimeDecoratorwraps each call inAround. Tracing explicitly uses the recorded fallback, matching #2206, so the existing string and function-call records capture the parse and its returned value. No new trace record is needed.This adds virtuals to
IRuntime, following the current development interface. The checkout has no JSI version macro orIRuntime2. It requires matching JSI headers and binaries; directIRuntimeimplementations must add the methods, whileRuntimesubclasses inherit defaults. React Native ships its own JSI copy. The existing UTF-8 API, Hermes C ABI, and bytecode format are unchanged.The Release benchmark is in
benchmarks/jsi-json. Three fresh process runs per plain/thread-safe runtime on an M5 Max, macOS arm64, HEAP_HV_64, no JIT or sanitizers: for a plain runtime, a 4096-unit Unicode string takes about 504 ns through raw UTF-16, 759 ns through cachedJSON.parseincluding input-string creation, and 8432 ns through the native UTF-8 API with pre-encoded input. Paired batch medians give 1.50x and 16.7x speedups. Nested ASCII/Unicode objects improve by about 9%/4%; a 4096-character ASCII string through UTF-16 is roughly unchanged (0.997x). Raw ASCII improves the 4096-character string by 1.48x versus cached parse with ASCII-string creation. The harness also measures cached parse with a prebuilt JS string. These are native C++ microbenchmarks on this Mac, with no JNI or Android-device claim.Refs #2258
Test Plan
Debug + ASan,
-O1, AppleClang 21, macOS arm64, HEAP_HV_64, JSI_UNSTABLE=OFF:cmake --build cmake-build-asan --target check-hermes -j 12: 4164 expected passes, 6 expected failures, 199 unsupported tests. Node-API: 41 passes, 23 skips, no failures.APITests: all 389 tests pass.APILeanTests: all 3 pass.getPropertyAsObject: property 'JSON' is undefined. Removing the two RuntimeDecorator forwards makes both counting/sentinel tests fail. Restoring the implementation passes the full API and lean suites.ParseAcrossAllocationsexplicitly sets SanitizeRate=1 and covers successful and throwing parses across moving collections. Lean API tests pass too.-Werrorsyntax checks. The decorator audit reports no gaps. Changed lines were formatted with clang-format 16, preserving JSI's pointer/reference style.No test262 run was requested. JSC, V8, and Android integration were not tested. The existing shared JSON lexer accepts
1.; this patch keeps that numeric-grammar behavior and does not changeJSONLexer.cpp.