From 9e3a9a7f4b4d77b72bc5e69eba373ddbe9433356 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 18 Apr 2025 08:08:41 -0700 Subject: [PATCH 1/3] Don't latebind global JS property assignments Late binding assumes a parent symbol. Currently, the binder does not check whether an assignment declaration has a parent symbol exists before creating a symbol for late binding. The easiest place to encounter this is with `window` assignments, because global (script) files have no symbol, unlike module files: ```js const X = "X" window[X] = () => 1 ``` It is possible to special-case `window` for late binding similar to the way that it's done for normal binding. But I don't think there's enough value. For now, this PR prevents binding entirely for a dynamic name when there is no parent symbol. --- src/compiler/binder.ts | 3 +++ .../lateboundWindowToplevelAssignment.symbols | 12 ++++++++++ .../lateboundWindowToplevelAssignment.types | 22 +++++++++++++++++++ .../lateboundWindowToplevelAssignment.ts | 7 ++++++ 4 files changed, 44 insertions(+) create mode 100644 tests/baselines/reference/lateboundWindowToplevelAssignment.symbols create mode 100644 tests/baselines/reference/lateboundWindowToplevelAssignment.types create mode 100644 tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 27ec079f614ab..8b04eb6226461 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3432,6 +3432,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); } else if (hasDynamicName(node)) { + if (!parentSymbol) { + return; + } bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed); const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototypeProperty*/ false, /*containerIsClass*/ false); addLateBoundAssignmentDeclarationToSymbol(node, sym); diff --git a/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols b/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols new file mode 100644 index 0000000000000..45b0811450ea3 --- /dev/null +++ b/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols @@ -0,0 +1,12 @@ +//// [tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts] //// + +=== lateboundWindowToplevelAssignment.js === +const UPDATE_MARKER_FUNC = 'updateMarkerPosition'; +>UPDATE_MARKER_FUNC : Symbol(UPDATE_MARKER_FUNC, Decl(lateboundWindowToplevelAssignment.js, 0, 5)) + +window[UPDATE_MARKER_FUNC] = () => {}; +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>UPDATE_MARKER_FUNC : Symbol(UPDATE_MARKER_FUNC, Decl(lateboundWindowToplevelAssignment.js, 0, 5)) + +// class C { [UPDATE_MARKER_FUNC] = 1 } + diff --git a/tests/baselines/reference/lateboundWindowToplevelAssignment.types b/tests/baselines/reference/lateboundWindowToplevelAssignment.types new file mode 100644 index 0000000000000..04e19923fcbb3 --- /dev/null +++ b/tests/baselines/reference/lateboundWindowToplevelAssignment.types @@ -0,0 +1,22 @@ +//// [tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts] //// + +=== lateboundWindowToplevelAssignment.js === +const UPDATE_MARKER_FUNC = 'updateMarkerPosition'; +>UPDATE_MARKER_FUNC : "updateMarkerPosition" +> : ^^^^^^^^^^^^^^^^^^^^^^ +>'updateMarkerPosition' : "updateMarkerPosition" +> : ^^^^^^^^^^^^^^^^^^^^^^ + +window[UPDATE_MARKER_FUNC] = () => {}; +>window[UPDATE_MARKER_FUNC] = () => {} : () => void +> : ^^^^^^^^^^ +>window[UPDATE_MARKER_FUNC] : error +>window : Window & typeof globalThis +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>UPDATE_MARKER_FUNC : "updateMarkerPosition" +> : ^^^^^^^^^^^^^^^^^^^^^^ +>() => {} : () => void +> : ^^^^^^^^^^ + +// class C { [UPDATE_MARKER_FUNC] = 1 } + diff --git a/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts b/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts new file mode 100644 index 0000000000000..bc80679d81e27 --- /dev/null +++ b/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts @@ -0,0 +1,7 @@ +// @filename: lateboundWindowToplevelAssignment.js +// @allowJs: true +// @checkJs: true +// @noEmit: true +const UPDATE_MARKER_FUNC = 'updateMarkerPosition'; +window[UPDATE_MARKER_FUNC] = () => {}; +// class C { [UPDATE_MARKER_FUNC] = 1 } From 8cddd4b9953288088f5541c505dd7046243c854f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 18 Apr 2025 08:15:30 -0700 Subject: [PATCH 2/3] clean up test --- .../reference/lateboundWindowToplevelAssignment.symbols | 2 -- .../baselines/reference/lateboundWindowToplevelAssignment.types | 2 -- .../conformance/salsa/lateboundWindowToplevelAssignment.ts | 1 - 3 files changed, 5 deletions(-) diff --git a/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols b/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols index 45b0811450ea3..eacc9aa312feb 100644 --- a/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols +++ b/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols @@ -8,5 +8,3 @@ window[UPDATE_MARKER_FUNC] = () => {}; >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) >UPDATE_MARKER_FUNC : Symbol(UPDATE_MARKER_FUNC, Decl(lateboundWindowToplevelAssignment.js, 0, 5)) -// class C { [UPDATE_MARKER_FUNC] = 1 } - diff --git a/tests/baselines/reference/lateboundWindowToplevelAssignment.types b/tests/baselines/reference/lateboundWindowToplevelAssignment.types index 04e19923fcbb3..5c2d54db3d7c8 100644 --- a/tests/baselines/reference/lateboundWindowToplevelAssignment.types +++ b/tests/baselines/reference/lateboundWindowToplevelAssignment.types @@ -18,5 +18,3 @@ window[UPDATE_MARKER_FUNC] = () => {}; >() => {} : () => void > : ^^^^^^^^^^ -// class C { [UPDATE_MARKER_FUNC] = 1 } - diff --git a/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts b/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts index bc80679d81e27..933eeaeb3da2c 100644 --- a/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts +++ b/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts @@ -4,4 +4,3 @@ // @noEmit: true const UPDATE_MARKER_FUNC = 'updateMarkerPosition'; window[UPDATE_MARKER_FUNC] = () => {}; -// class C { [UPDATE_MARKER_FUNC] = 1 } From b06fb645d710f513451dee1a65f3e353710de3c1 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 18 Apr 2025 08:17:35 -0700 Subject: [PATCH 3/3] add usage to test --- .../reference/lateboundWindowToplevelAssignment.symbols | 4 ++++ .../reference/lateboundWindowToplevelAssignment.types | 8 ++++++++ .../salsa/lateboundWindowToplevelAssignment.ts | 1 + 3 files changed, 13 insertions(+) diff --git a/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols b/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols index eacc9aa312feb..f1779520e44c5 100644 --- a/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols +++ b/tests/baselines/reference/lateboundWindowToplevelAssignment.symbols @@ -8,3 +8,7 @@ window[UPDATE_MARKER_FUNC] = () => {}; >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) >UPDATE_MARKER_FUNC : Symbol(UPDATE_MARKER_FUNC, Decl(lateboundWindowToplevelAssignment.js, 0, 5)) +window[UPDATE_MARKER_FUNC](); +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>UPDATE_MARKER_FUNC : Symbol(UPDATE_MARKER_FUNC, Decl(lateboundWindowToplevelAssignment.js, 0, 5)) + diff --git a/tests/baselines/reference/lateboundWindowToplevelAssignment.types b/tests/baselines/reference/lateboundWindowToplevelAssignment.types index 5c2d54db3d7c8..1f9ab09aaebe8 100644 --- a/tests/baselines/reference/lateboundWindowToplevelAssignment.types +++ b/tests/baselines/reference/lateboundWindowToplevelAssignment.types @@ -18,3 +18,11 @@ window[UPDATE_MARKER_FUNC] = () => {}; >() => {} : () => void > : ^^^^^^^^^^ +window[UPDATE_MARKER_FUNC](); +>window[UPDATE_MARKER_FUNC]() : error +>window[UPDATE_MARKER_FUNC] : error +>window : Window & typeof globalThis +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>UPDATE_MARKER_FUNC : "updateMarkerPosition" +> : ^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts b/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts index 933eeaeb3da2c..afbdb3bef273e 100644 --- a/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts +++ b/tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts @@ -4,3 +4,4 @@ // @noEmit: true const UPDATE_MARKER_FUNC = 'updateMarkerPosition'; window[UPDATE_MARKER_FUNC] = () => {}; +window[UPDATE_MARKER_FUNC]();