Skip to content

feat: scope worker-created extended class names to their isolate - #428

Merged
NathanWalker merged 2 commits into
mainfrom
feat/worker-scoped-extended-class-names
Aug 4, 2026
Merged

feat: scope worker-created extended class names to their isolate#428
NathanWalker merged 2 commits into
mainfrom
feat/worker-scoped-extended-class-names

Conversation

@edusperoni

@edusperoni edusperoni commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #420 (proposed there as the by-construction hardening after the registration lock in #421).

Problem

Named extends register the requested objc class name verbatim from every isolate, and the objc class namespace is process-global. A worker that extends a shared named class (e.g. TimerTargetImpl via Infrastructure/timers) before the main isolate does wins the registration race: the worker owns the bare name and the main isolate's class is nondeterministically demoted to a collision suffix (TimerTargetImpl1). Any name-based native lookup expecting the main isolate's class (NSClassFromString, storyboard class references, crash symbolication) then resolves the worker's. Cross-isolate contention for fresh same-named classes was also the trigger of the +initialize deadlock (#420) — serialized by #421's lock, now impossible to reach at all.

Change

Named extends from worker isolates get _<isolateId> appended (ScopeClassNameToIsolate, applied on both extend paths — explicit-name .extend() and native __extends). Main-isolate names stay byte-exact — the user-facing contract — and are now deterministic regardless of worker startup order, since workers can no longer contend for them. Anonymous extends were already isolate-scoped.

Behavior note: native code resolving a worker's class by its exact declared name now finds the scoped name instead. Worker-created classes are internal to the worker model, so this is the isolation contract working as intended (and NSStringFromClass round-trips are unaffected — they use the actual registered name).

Tests

New ExtendedClassNamingTests (registered in the runner):

  • main-isolate explicit names stay verbatim;
  • a worker's explicit-name class is scoped and the verbatim name remains claimable by main afterwards (the exact stolen-name defect this prevents);
  • the TypeScript-__extends path scopes too.

Full local suite green (0 failures), including the three new specs.

Summary by CodeRabbit

  • Bug Fixes
    • Prevented naming conflicts for Objective-C classes created in worker runtimes by automatically scoping their names to the worker isolate.
    • Preserved explicitly assigned class names in the main runtime.
  • Tests
    • Added coverage for class naming across main and worker runtimes, including JavaScript- and TypeScript-extended classes.

Named extends used the requested objc class name verbatim from every
isolate, so a worker could win the registration race for a name the main
isolate also uses (e.g. TimerTargetImpl via a shared module) and
nondeterministically demote the main class to a collision suffix -
breaking name-based native lookups that expect the main isolate's class.

Append _<isolateId> to named extends from worker isolates. Main-isolate
names stay byte-exact (the user-facing contract for storyboards and
NSClassFromString), workers can no longer contend for them, and
cross-isolate name collisions on fresh classes - the trigger of the
+initialize deadlock fixed by the registration lock - become impossible
by construction. Anonymous extends were already isolate-scoped.

Behavior note: native code resolving a *worker's* class by its exact
declared name now finds the scoped name instead; worker classes are
internal to the worker model, so this is the isolation contract working
as intended.

Follow-up to #420.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d1f992e-c1d3-47f6-96b8-e00f8e56cd1a

📥 Commits

Reviewing files that changed from the base of the PR and between 5b224bf and 630eb15.

📒 Files selected for processing (3)
  • NativeScript/runtime/ClassBuilder.cpp
  • NativeScript/runtime/ClassBuilder.h
  • NativeScript/runtime/ClassBuilder.mm
🚧 Files skipped from review as they are similar to previous changes (1)
  • NativeScript/runtime/ClassBuilder.mm

📝 Walkthrough

Walkthrough

The runtime now appends worker isolate IDs to explicitly named extended classes. Main-isolate and unnamed classes keep their existing names. The GetExtendedClass API now receives the isolate ID directly. Tests cover JavaScript and TypeScript extension paths.

Changes

Extended class naming

Layer / File(s) Summary
Runtime name scoping
NativeScript/runtime/ClassBuilder.h, NativeScript/runtime/ClassBuilder.cpp, NativeScript/runtime/ClassBuilder.mm
GetExtendedClass now accepts an isolate ID. Worker-created extended classes use isolate-scoped names through both extension paths. Main-isolate and unnamed classes remain unchanged.
Naming behavior validation
TestRunner/app/tests/ExtendedClassNamingTests.js, TestRunner/app/tests/index.js
Tests cover main-isolate names and worker-isolate names. The new suite is registered in the test runner.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • NativeScript/ios#421: Both changes modify ClassBuilder::GetExtendedClass and extended-class naming and allocation logic.

Suggested reviewers: nathanwalker

Poem

A rabbit scopes each worker class,
While main-isolate names stay bright.
JavaScript and TypeScript pass,
Tests check each name is right.
Hop, hop—classes share no fight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: isolating names for extended classes created by worker runtimes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

GetExtendedClass took all three name strings by value and both call
sites built the anonymous-name suffix on every extend even when unused.
Pass const refs plus the isolate id and assemble the anonymous name
inside only when needed (same output format); the worker scoping helper
now appends in place (char + SSO to_string), adding zero heap
allocations on the common path.
@NathanWalker
NathanWalker merged commit 77b1ea3 into main Aug 4, 2026
9 checks passed
@NathanWalker
NathanWalker deleted the feat/worker-scoped-extended-class-names branch August 4, 2026 00:03
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