GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — feat(runtime): AOT direct calls — app-compiled objc_msgSend stubs bound in place of libffi by edusperoni · Pull Request #366 · NativeScript/ios Conversation
|
| access | main before #496 | main (#496) | this PR (stubs) |
|---|---|---|---|
UIScreen.mainScreen |
158 | 134 | 119 |
screen.scale |
173 | 151 | 122 |
screen.nativeScale |
175 | 147 | 111 |
UIScreen.mainScreen.scale |
335 | 282 | 218 |
screen.bounds |
790 | 417 | 402 |
UIScreen.mainScreen.bounds.size.width |
2035 | 1164 | 1090 |
Stubs remove the libffi dispatch and the per-call struct-name lookup; they do not change how a struct is materialized in JS, which is what dominates the struct rows (see "Not in this PR").
A/B on the same Release build: generic path (stub file built from an empty config) vs stubs (TestRunner/AOT/aot-config.json). 20,000 calls per case, ns per op; aotStats() confirms every stubbed call was served (0 declined) in the stub run and none in the generic run. Run-to-run noise is about 3-10%.
| call | generic | stubs | delta |
|---|---|---|---|
arr.count (NSMutableArray) |
83.0 | 52.7 | -36.5% |
str.length (NSString) |
84.3 | 55.8 | -33.8% |
view.setNeedsLayout() |
105.9 | 73.6 | -30.5% |
arr.addObject(o); arr.removeLastObject() |
209.4 | 151.5 | -27.7% |
arr.indexOfObject(o) |
130.6 | 101.9 | -22.0% |
obj.isEqual(o) |
110.1 | 87.3 | -20.7% |
obj.respondsToSelector(sel) |
119.7 | 95.0 | -20.6% |
obj.isKindOfClass(cls) |
184.6 | 148.1 | -19.8% |
NSNumber.numberWithInt(42) |
185.6 | 149.9 | -19.2% |
arr.objectAtIndex(1) |
124.7 | 103.7 | -16.8% |
dict.objectForKey("k") |
154.5 | 129.0 | -16.5% |
UIColor.colorWithRedGreenBlueAlpha(...) |
826.8 | 750.2 | -9.3% |
NSNumber.numberWithDouble(1.5) |
237.1 | 215.2 | -9.2% |
obj.description |
505.0 | 463.2 | -8.3% |
NSProcessInfo.processInfo |
108.0 | 100.4 | -7.0% |
str.isEqualToString("...") |
167.3 | 159.8 | -4.5% |
| JS-only loop (control) | 31.5 | 32.6 | - |
Stubs save a roughly constant 20-35 ns per call (the libffi dispatch and the generic argument/return plumbing), which is 17-37% of cheap calls. Where the call itself or the JS-side conversion dominates (string arguments and returns, wrapper creation for new objects, description), the gain drops to single digits.
TestRunner/AOT/, 52 stubs over TestFixtures, NSObject, Foundation collections/strings/numbers, UIScreen, UIView and UIColor) bound: 1777 tests, 0 failures on the rebased branch (Debug; the AddressSanitizer lane ran clean with the earlier 36-stub subset). The new AOTDirectCallsTests.js (34 specs) asserts served/declined counts per call and covers members, structs, argument kinds, NSException parity, JS-extended overrides calling super, and the decline paths.NS_JSON_METADATA_PATH=<dir> to get the metadata JSON.__native_call_profiler.start(), exercise the hot screens, __native_call_profiler.aotConfig(50) → paste into aot-config.json.python3 scripts/generate-aot.py aot-config.json -m <dir>/arm64 -o App_Resources/iOS/src/NativeScriptAOTStubs.m.___ns_register_aot_calls must stay exported (see TestFixtures/exported-symbols.txt for how the TestRunner does it).An existing NativeScriptAOTStubs.m generated by the previous iteration targets the old bridge API and must be regenerated.
unichar, Protocol*, block returns, and long long as BigInt; property setters are generated but not bound; overloaded jsNames (baseMethod / baseMethod:) bind in metadata order as before.GetResult behavior, same on both paths).{origin:{x,y},size:{width,height}} from a cached shape, no wrapper or finalizer, estimated ~400 ns for the full bounds.size.width chain, at the price of snapshot semantics), or per-struct-type object templates with native accessors in the runtime, which keeps live-view semantics for every path.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
7f5d49b to
4439f99
Compare
04aeb0a to
f612060
Compare
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews: auto_review: drafts: true
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. Comment |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
objc-metadata-generator -output-json <dir> writes one <Module>.json per module with the same information as the YAML output (members, structural type descriptions, flags) plus each enum's underlying type. build-step-metadata-generator.py enables it when NS_JSON_METADATA_PATH is set. YAML and binary outputs are unchanged.
…adata JSON generate-aot.py reads a config of (class, selector, static) entries and the metadata JSON, resolves each member through the class's protocols and Base chain, and emits one C stub per declaring-class member built on the NativeScriptAOT.h bridge: cast objc_msgSend / objc_msgSendSuper with exact C types, struct typedefs derived from metadata field layouts, argument-count guard, @Try around the send, and typed return setters. Entries the bridge cannot model are skipped with a reason.
…spatch NativeScriptAOT.h is a public C bridge for stubs generated per (class, selector): the runtime discovers the app's registrar with dlsym at startup, and when a prototype template is built it binds a registered stub as the V8 callback for the method or property getter, looking the member up by class name (walking the ObjC superclass chain), SEL and static-ness. A stub resolves its receiver, selector (swizzled when the generic path would) and super dispatch through the bridge, converts arguments and results with the bound method's metadata through the same Interop code as the generic path, catches NSException into the same JS error, and declines to the generic callback for anything it does not model (argument-count mismatch, alloc receivers). callSuper is cached per Class in a StateFor slot that clears when ClassPrototypes grows. The id and Class return branches of Interop::GetResult and the NSException-to-JS-Error conversion are extracted into helpers shared by both paths. __native_call_profiler (lazy global) records generic-path calls, emits aot-config entries for the hottest ones, and reports served/declined stub counts while profiling. TestRunner compiles generated stubs for TestFixtures, NSObject and UIScreen (TestRunner/AOT) and AOTDirectCallsTests asserts served/declined counts per call. Release TestRunner, iPhone 16 Pro simulator: screen.scale 173 -> 125 ns, UIScreen.mainScreen 158 -> 119 ns, UIScreen.mainScreen.scale 335 -> 232 ns; struct returns unchanged. Suite 1776/0 incl. the ASan lane; 1742/0 with no stubs registered.
f612060 to
9b26314
Compare
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.