LibJS: Preserve strict unresolvable assignment references
Resolve strict global-looking assignment targets before evaluating their right-hand side instead of lowering them directly to SetGlobal. Preserve the original unresolvable reference for PutValue so a RHS-created global cannot hide the ReferenceError. Add a runtime test for that ordering and update the bytecode expectation for a strict top-level assignment that now uses SetResolvedBinding.
This commit is contained in:
parent
8b64f0079b
commit
19c1cae030
3 changed files with 22 additions and 9 deletions
|
|
@ -2884,10 +2884,12 @@ fn emit_resolve_binding_for_identifier_assignment(
|
|||
) -> Option<ResolvedBinding> {
|
||||
let arena = generator.arena.clone();
|
||||
let ident = &arena.identifiers[id];
|
||||
if ident.is_local() || ident.is_global {
|
||||
if ident.is_local() || (ident.is_global && !generator.strict) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// In strict mode, a global-looking identifier may still be unresolvable. Preserve that reference across RHS
|
||||
// evaluation so PutValue throws even if the RHS creates a global property with the same name.
|
||||
let identifier = generator.intern_identifier_id(ident.name);
|
||||
if generator.environment_coordinate_for_identifier(identifier).is_some() {
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
$75ea5b81 tagged-template-if-register-order.js:2:1
|
||||
Registers: 10
|
||||
$73266a71 tagged-template-if-register-order.js:2:1
|
||||
Registers: 11
|
||||
Blocks: 3
|
||||
Constants:
|
||||
[0] = String("use strict")
|
||||
|
|
@ -20,11 +20,12 @@ block0:
|
|||
[ c0] JumpFalse condition:reg6, target:block2
|
||||
|
||||
block1:
|
||||
[ d0] GetGlobal dst:reg5, `x`
|
||||
[ e0] GetById dst:reg9, base:reg5, `toUpperCase` (x.toUpperCase)
|
||||
[ f8] Call dst:reg7, callee:reg9, this_value:reg5, x.toUpperCase
|
||||
[ 118] SetGlobal `y`, src:reg7
|
||||
[ 128] Mov dst:reg8, src:reg7
|
||||
[ d0] ResolveBinding dst:reg7, `y`
|
||||
[ e0] GetGlobal dst:reg9, `x`
|
||||
[ f0] GetById dst:reg10, base:reg9, `toUpperCase` (x.toUpperCase)
|
||||
[ 108] Call dst:reg5, callee:reg10, this_value:reg9, x.toUpperCase
|
||||
[ 128] SetResolvedBinding environment:reg7, `y`, src:reg5
|
||||
[ 138] Mov dst:reg8, src:reg5
|
||||
|
||||
block2:
|
||||
[ 138] End value:reg8
|
||||
[ 148] End value:reg8
|
||||
|
|
|
|||
|
|
@ -16,6 +16,16 @@ test("assignment to function call in strict mode is a SyntaxError", () => {
|
|||
expect("'use strict'; foo() = 'foo'").not.toEval();
|
||||
});
|
||||
|
||||
test("strict assignment to unresolvable reference preserves initial reference", () => {
|
||||
delete globalThis.__test_unresolvable_assignment;
|
||||
expect(() => {
|
||||
"use strict";
|
||||
__test_unresolvable_assignment = (globalThis.__test_unresolvable_assignment = 5);
|
||||
}).toThrowWithMessage(ReferenceError, "'__test_unresolvable_assignment' is not defined");
|
||||
expect(globalThis.__test_unresolvable_assignment).toBe(5);
|
||||
delete globalThis.__test_unresolvable_assignment;
|
||||
});
|
||||
|
||||
test("assignment to inline function call", () => {
|
||||
expect(() => {
|
||||
(function () {})() = "foo";
|
||||
|
|
|
|||
Loading…
Reference in a new issue