Skip to content

fix(ffi): answer instanceof for TypeScript-derived classes against the derived class - #65

Merged
DjDeveloperr merged 2 commits into
mainfrom
fix/ffi-derived-class-hasinstance
Aug 10, 2026
Merged

fix(ffi): answer instanceof for TypeScript-derived classes against the derived class#65
DjDeveloperr merged 2 commits into
mainfrom
fix/ffi-derived-class-hasinstance

Conversation

@NathanWalker

Copy link
Copy Markdown
Contributor

What

On the QuickJS (jsi/ffi) runtime flavor, x instanceof Derived for a @NativeClass-derived class ran the base class's membership check until the derived Objective-C class materialized. A TypeScript-derived constructor inherits the base wrapper's Symbol.hasInstance through the wrapper prototype chain; the closure captures the base native class, so any instance of the base kind tested true (UINavigationControllerImpl instanceof IOSHelper.UILayoutViewControllertrue).

Impact

@nativescript/core's setViewControllerView gates root-view mounting on exactly that check. For an app whose root view is an imperative Frame (Application.run({ create: () => new Frame() })), the misfire sent core down the layout-controller branch, marshalled the iOSFrame JS helper object into addSubview: (as __NSDictionaryM), and UIKit threw NSInvalidArgumentException: -[__NSDictionaryM superview]: unrecognized selector at boot — a hard crash before first render.

Fix

Symbol.hasInstance is called with the constructor instanceof was invoked on as this (spec: Call(instOfHandler, target, «V»)). In the base wrapper's implementation, detect the inherited invocation (this is neither this constructable nor its proxy and carries __nativeApiTypeScriptState):

  • materialized → delegate to the derived wrapper's own hasInstance (correct isKindOfClass: against the derived class)
  • not materialized → return false: no instance of an unmaterialized class can exist

After materialization the TS constructor's prototype is already reset to the derived wrapper, whose own hasInstance answers directly, so the new branch only governs the pre-materialization window.

Validation

Reproduced deterministically with a root-Frame NativeScript app on the iOS 26.5 simulator (ios-quickjs build): crash at boot before the fix; app boots, renders, and runs its display-link loop after. A dominative/document-rooted app (no root Frame) was unaffected before and after.

Sibling of #63: that fix marshalled the real Class for the wrapper's own check; this one covers constructors that inherit the check.

🤖 Generated with Claude Code

…e derived class

A @nativeclass constructor inherits the base wrapper's Symbol.hasInstance
through the wrapper prototype chain until its Objective-C class materializes,
so `x instanceof Derived` ran the BASE class's membership check: any
UIViewController subclass instance tested true for instanceof
UILayoutViewController. @nativescript/core's setViewControllerView gates on
exactly that check for root views, so a root Frame's iOSFrame helper object
was marshalled (as NSDictionary) into addSubview: and UIKit threw
NSInvalidArgumentException at boot. Detect the inherited invocation via
hasInstance's `this` (the spec passes the constructor instanceof was invoked
on): delegate to the materialized derived wrapper when one exists, and report
false before materialization — no instance of an unmaterialized class can
exist. After materialization the constructor's prototype is already the
derived wrapper, whose own hasInstance answers directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 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 Plus

Run ID: bdeaa5d6-0ea8-4e88-b97a-31fd02fd4442

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

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.

@DjDeveloperr
DjDeveloperr merged commit f136c86 into main Aug 10, 2026
2 checks passed
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.

2 participants