Skip to content

fix(js): follow constructor import aliases and explicit superclass calls - #2505

Closed
Puma7 wants to merge 4 commits into
colbymchenry:mainfrom
Puma7:codex/js-constructor-super-resolution
Closed

Puma7 wants to merge 4 commits into
colbymchenry:mainfrom
Puma7:codex/js-constructor-super-resolution

Conversation

@Puma7

@Puma7 Puma7 commented Oct 11, 2026 •

Copy link
Copy Markdown

Method calls through imported JS constructor aliases can lose their class target, and an override's explicit super call can remain unresolved. Resolve the actual import/class identity and runtime extends parent, preserving recursion and static/instance distinctions.

Closes #2497.

Ordinary uniquely resolved unaliased receivers retain the existing inference, including returned/destructured values. Imported aliases remain conservative around shadowing/reassignment. Source preprocessing is deferred until needed, raw lines are reused, and resolved superclass lookups share the generation-tagged cache.

Validation: all 112 constructor/super tests pass with an active native kernel. Comparing 893 public TS files against main 80e4b459 preserves all 35,465 call-site tuples (source, target, line, column), with no additions or losses. This corrects the two pool-site losses found after the earlier comparison, which had not distinguished call positions.

Typed/CJS alias expansion, arbitrary dataflow and other-OS validation are outside this patch. Re-index after upgrading.

Combined validation against main 80e4b459 (Windows, Node 24, WASM, two workers): 6,752 passed, zero failed, 406 skipped under existing platform/kernel gates; standard timeouts unchanged. Separate active-native checks passed 152 class/parity and 112 constructor/super cases. These are local results, not GitHub CI or other-OS claims.

Performance (three serial interleaved rounds, fresh fixtures, no concurrent tests): the 4,000-super-call fixture has median index/resolve time 1,233 ms on main and 1,202 ms here, resolving 3,801 versus all 4,000 parent calls. On the same 893 public TS files, medians are 15,623 / 15,542 ms; durations are effectively comparable in this small sample. Median peak RSS remains 2,154 / 2,265 MiB (about +111 MiB / +5.2%). Unnecessary preprocessing is removed, but remaining memory overhead is not claimed eliminated. These are fixture-specific Windows/WASM results, not universal performance factors.

Puma7 commented Oct 12, 2026 •

Copy link
Copy Markdown
Author

Updated validation after the supplied review:

The review's two lost pool calls are confirmed and fixed (parse-pool.test.ts:130/132). My earlier comparison omitted the actual top-level edge positions, so its no-loss wording was too broad. The revised position-aware 893-file comparison preserves all 35,465 calls against current main. Ordinary calls no longer trigger full-source preprocessing; the source and superclass caches are lazy/generation-aware. All 112 native constructor/super cases pass.

The proposed global alias-name fallback does add 207 calls in this corpus, mostly real ambient parser methods. It also creates false targets for external aliases, same-named classes and shadowed bindings. Those broader alias forms need module-aware work; they are not solved by this constructor-only patch. The shared super fallback also breaks recursion beside super and may follow implements, so it is not adopted.

The baseline also incorrectly resolves new Known() when a parameter shadows that import. This patch guards the added method recovery; that pre-existing constructor-instantiation lookup remains separate work.

Fresh three-round timing is effectively comparable with current main in both the 4,000-super fixture and the 893-file corpus. The corrected corpus still uses about +111 MiB / +5.2% median peak RSS; that remaining cost is explicit in the description. The complete four-PR Windows integration passed 6,752 tests with zero failures and 406 existing-gate skips.

@inth3shadows

Copy link
Copy Markdown
Contributor

Thanks for working on this. Following super.m() to the parent class's method is a real gain. I tried a few shapes on the PR head (e91e3fc) and found two where it now links a wrong method. Both give no wrong edge of that kind on its base (80e4b45) or on current main (847f1df).

1. super inside an object-literal method is resolved against the enclosing class.

class B { m() {} }
class A extends B {
  make() {
    const proto = {};
    const o = { __proto__: proto, m() { return super.m(); } };
    return o;
  }
}
  • This branch: A::make::o::m → B::m
  • Base and main: no edge
  • Expected: no edge. In an object-literal method, super refers to the object's prototype (proto here), not to A's parent.

The enclosing class seems to be picked by line span, so an object literal inside a method inherits it. Skipping object-literal methods when looking for the class would avoid this.

2. super.s() in an arrow inside a static method picks the instance method.

class B {
  static s() {}
  s() {}
}
class A extends B {
  static s() {
    const f = () => super.s();
    return f;
  }
}
  • This branch: A::s::f → B::s at line 3, the instance method.
  • main: A::s::f → A::s, a self-edge, also wrong.
  • Expected: B::s at line 2, the static one. The arrow takes super from the static method around it.

Static-ness seems to be read from the arrow, not from the nearest enclosing non-arrow method.

Separately, the branch is 171 commits behind and conflicts in src/resolution/name-matcher.ts, so it will need a rebase. I'm happy to share the test file I used.

@colbymchenry

Copy link
Copy Markdown
Owner

Thanks @Puma7 for #2497 and this PR. super.method() calls now link to the parent class's method through #2818, and calls through an aliased constructor import like import { Left as Known }; new Known().show() reach the right class on current main. Both ship in the next release, and every case from #2497 passes on current main. Closing as superseded. Thank you!

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.

JavaScript: aliased constructor receivers and overridden super calls remain unresolved

3 participants