diff --git a/Libraries/LibJS/Bytecode/PropertyAccess.h b/Libraries/LibJS/Bytecode/PropertyAccess.h index 3a7eb77885..02036d9691 100644 --- a/Libraries/LibJS/Bytecode/PropertyAccess.h +++ b/Libraries/LibJS/Bytecode/PropertyAccess.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,15 @@ ALWAYS_INLINE ThrowCompletionOr get_by_id(VM& vm, GetBaseIdentifier get_b } } + auto const& property_name = get_property_name(); + if (base_value.is_string()) { + // https://tc39.es/ecma262/#sec-stringgetownproperty + // String exotic objects expose virtual own properties for canonical string indexes. + auto string_value = TRY(base_value.as_string().get(vm, property_name)); + if (string_value.has_value()) + return *string_value; + } + auto base_obj = TRY(base_object_for_get(vm, base_value, get_base_identifier, get_property_name)); if constexpr (mode == GetByIdMode::Length) { @@ -152,7 +162,7 @@ ALWAYS_INLINE ThrowCompletionOr get_by_id(VM& vm, GetBaseIdentifier get_b prototype_chain_validity = shape.prototype()->shape().prototype_chain_validity(); CacheableGetPropertyMetadata cacheable_metadata; - auto value = TRY(base_obj->internal_get(get_property_name(), this_value, &cacheable_metadata)); + auto value = TRY(base_obj->internal_get(property_name, this_value, &cacheable_metadata)); // If internal_get() caused object's shape change, we can no longer be sure // that collected metadata is valid, e.g. if getter in prototype chain added diff --git a/Libraries/LibJS/Rust/src/bytecode/codegen.rs b/Libraries/LibJS/Rust/src/bytecode/codegen.rs index 8552ac0e19..54ca8938f5 100644 --- a/Libraries/LibJS/Rust/src/bytecode/codegen.rs +++ b/Libraries/LibJS/Rust/src/bytecode/codegen.rs @@ -7012,6 +7012,9 @@ fn assign_to_for_in_of_lhs(generator: &mut Generator, lhs: &ForInOfLhs, value: & // ============================================================================= /// Whether we are initializing a new binding or setting an existing one. +// https://tc39.es/ecma262/#sec-runtime-semantics-bindinginitialization +// https://tc39.es/ecma262/#sec-runtime-semantics-destructuringassignmentevaluation +// Binding patterns initialize lexical declarations, while assignment patterns perform PutValue. #[derive(Clone, Copy)] enum BindingMode { /// `const` or `let` declarations: emit InitializeLexicalBinding. @@ -7063,8 +7066,17 @@ fn emit_set_variable_with_mode( let arena = generator.arena.clone(); let ident = &arena.identifiers[id]; if ident.is_local() { - let local = generator.resolve_local(ident.local_index, ident.local_type.unwrap()); - generator.emit_mov(&local, value); + match mode { + BindingMode::InitializeLexical => { + let local = generator.resolve_local(ident.local_index, ident.local_type.unwrap()); + generator.emit_mov(&local, value); + generator.mark_local_initialized(ident.local_index); + } + BindingMode::Set => { + emit_tdz_check_if_needed(generator, id); + emit_set_variable(generator, id, value); + } + } } else { let id = generator.intern_identifier_id(ident.name); match mode { diff --git a/Libraries/LibJS/Rust/src/parser/declarations.rs b/Libraries/LibJS/Rust/src/parser/declarations.rs index 1e59c2cc6c..e4bada6350 100644 --- a/Libraries/LibJS/Rust/src/parser/declarations.rs +++ b/Libraries/LibJS/Rust/src/parser/declarations.rs @@ -212,8 +212,6 @@ impl Parser<'_> { .zip(bound_names.iter()) .map(|(n, (_, id))| (n.as_slice(), Some(*id))) .collect(); - // NOTE: Binding pattern identifiers don't get declaration_kind, - // matching C++ behavior where only simple identifiers do. let Self { scope_collector, arena, .. } = self; @@ -221,7 +219,7 @@ impl Parser<'_> { &entries, declaration_line, declaration_column, - None, + Some(DeclarationKind::Var), &mut arena.identifiers, &arena.strings, &mut arena.scopes, @@ -230,17 +228,13 @@ impl Parser<'_> { let refs: Vec<&[u16]> = name_strs.iter().map(|n| n.as_slice()).collect(); self.scope_collector .add_lexical_declaration(&refs, declaration_line, declaration_column); - // Register each binding pattern identifier for scope analysis - // so they get is_local() annotations. - // NOTE: C++ does not pass declaration_kind for binding pattern identifiers, - // only for simple identifier declarations. let Self { scope_collector, arena, .. } = self; for (_name, id) in &bound_names { scope_collector.register_identifier( *id, - None, + Some(kind), &mut arena.identifiers, &arena.strings, &mut arena.scopes, diff --git a/Tests/LibJS/AST/expected/binding-pattern-positions.txt b/Tests/LibJS/AST/expected/binding-pattern-positions.txt index f28550361c..cab67b5926 100644 --- a/Tests/LibJS/AST/expected/binding-pattern-positions.txt +++ b/Tests/LibJS/AST/expected/binding-pattern-positions.txt @@ -4,10 +4,10 @@ Program (script) @6:1 │ ├─ BindingPattern (object) │ │ ├─ entry │ │ │ └─ name -│ │ │ └─ Identifier "foo" [global] @6:7 +│ │ │ └─ Identifier "foo" [global] (let) @6:7 │ │ └─ entry │ │ └─ name -│ │ └─ Identifier "bar" [global] @6:12 +│ │ └─ Identifier "bar" [global] (let) @6:12 │ └─ ObjectExpression @6:20 ├─ VariableDeclaration (let) @7:1 │ └─ VariableDeclarator @7:1 @@ -16,23 +16,23 @@ Program (script) @6:1 │ │ │ ├─ name │ │ │ │ └─ Identifier "x" [global] @7:7 │ │ │ └─ alias -│ │ │ └─ Identifier "aliased" [global] @7:10 +│ │ │ └─ Identifier "aliased" [global] (let) @7:10 │ │ └─ entry │ │ ├─ name │ │ │ └─ Identifier "y" [global] @7:19 │ │ └─ alias -│ │ └─ Identifier "also" [global] @7:22 +│ │ └─ Identifier "also" [global] (let) @7:22 │ └─ ObjectExpression @7:31 ├─ VariableDeclaration (let) @8:1 │ └─ VariableDeclarator @8:1 │ ├─ BindingPattern (array) │ │ ├─ entry │ │ │ └─ alias -│ │ │ └─ Identifier "first" [global] @8:7 +│ │ │ └─ Identifier "first" [global] (let) @8:7 │ │ ├─ Elision │ │ └─ entry │ │ └─ alias -│ │ └─ Identifier "third" [global] @8:16 +│ │ └─ Identifier "third" [global] (let) @8:16 │ └─ ArrayExpression @8:26 ├─ VariableDeclaration (let) @9:1 │ └─ VariableDeclarator @9:1 @@ -44,26 +44,26 @@ Program (script) @6:1 │ │ │ └─ BindingPattern (object) │ │ │ └─ entry │ │ │ └─ name -│ │ │ └─ Identifier "inner" [global] @9:17 +│ │ │ └─ Identifier "inner" [global] (let) @9:17 │ │ └─ entry (rest) │ │ └─ name -│ │ └─ Identifier "rest" [global] @9:29 +│ │ └─ Identifier "rest" [global] (let) @9:29 │ └─ ObjectExpression @9:38 ├─ VariableDeclaration (let) @10:1 │ └─ VariableDeclarator @10:1 │ ├─ BindingPattern (array) │ │ ├─ entry │ │ │ └─ alias -│ │ │ └─ Identifier "a" [global] @10:7 +│ │ │ └─ Identifier "a" [global] (let) @10:7 │ │ └─ entry │ │ └─ alias │ │ └─ BindingPattern (array) │ │ ├─ entry │ │ │ └─ alias -│ │ │ └─ Identifier "b" [global] @10:12 +│ │ │ └─ Identifier "b" [global] (let) @10:12 │ │ └─ entry │ │ └─ alias -│ │ └─ Identifier "c" [global] @10:15 +│ │ └─ Identifier "c" [global] (let) @10:15 │ └─ ArrayExpression @10:23 └─ FunctionDeclaration "destructured" @12:1 ├─ parameters diff --git a/Tests/LibJS/AST/expected/binding-pattern-property-keys.txt b/Tests/LibJS/AST/expected/binding-pattern-property-keys.txt index 1045601b63..eb34458a4b 100644 --- a/Tests/LibJS/AST/expected/binding-pattern-property-keys.txt +++ b/Tests/LibJS/AST/expected/binding-pattern-property-keys.txt @@ -6,12 +6,12 @@ Program (script) @1:1 │ │ ├─ name │ │ │ └─ Identifier "foo" [global] @1:7 │ │ └─ alias - │ │ └─ Identifier "a" [global] @1:12 + │ │ └─ Identifier "a" [global] (let) @1:12 │ └─ entry │ ├─ name │ │ └─ Identifier "1" [global] @1:15 │ └─ alias - │ └─ Identifier "b" [global] @1:18 + │ └─ Identifier "b" [global] (let) @1:18 └─ ObjectExpression @1:24 ├─ ObjectProperty @1:24 │ ├─ StringLiteral "foo" @1:26 diff --git a/Tests/LibJS/AST/expected/destructuring.txt b/Tests/LibJS/AST/expected/destructuring.txt index 5279908019..2caf75b566 100644 --- a/Tests/LibJS/AST/expected/destructuring.txt +++ b/Tests/LibJS/AST/expected/destructuring.txt @@ -7,10 +7,10 @@ Program (script) @1:1 │ ├─ BindingPattern (array) │ │ ├─ entry │ │ │ └─ alias - │ │ │ └─ Identifier "a" [variable:0] @2:10 + │ │ │ └─ Identifier "a" [variable:0] (let) @2:10 │ │ └─ entry │ │ └─ alias - │ │ └─ Identifier "b" [variable:1] @2:13 + │ │ └─ Identifier "b" [variable:1] (let) @2:13 │ └─ ArrayExpression @2:18 │ ├─ NumericLiteral 1 @2:19 │ └─ NumericLiteral 2 @2:22 @@ -19,12 +19,12 @@ Program (script) @1:1 │ ├─ BindingPattern (object) │ │ ├─ entry │ │ │ └─ name - │ │ │ └─ Identifier "x" [variable:2] @3:11 + │ │ │ └─ Identifier "x" [variable:2] (let) @3:11 │ │ └─ entry │ │ ├─ name │ │ │ └─ Identifier "y" [global] @3:14 │ │ └─ alias - │ │ └─ Identifier "z" [variable:3] @3:17 + │ │ └─ Identifier "z" [variable:3] (let) @3:17 │ └─ ObjectExpression @3:23 │ ├─ ObjectProperty @3:23 │ │ ├─ StringLiteral "x" @3:25 @@ -36,7 +36,7 @@ Program (script) @1:1 └─ BinaryExpression (+) @4:22 ├─ BinaryExpression (+) @4:18 │ ├─ BinaryExpression (+) @4:14 - │ │ ├─ Identifier "a" [variable:0] @4:12 - │ │ └─ Identifier "b" [variable:1] @4:16 - │ └─ Identifier "x" [variable:2] @4:20 - └─ Identifier "z" [variable:3] @4:24 + │ │ ├─ Identifier "a" [variable:0] (let) @4:12 + │ │ └─ Identifier "b" [variable:1] (let) @4:16 + │ └─ Identifier "x" [variable:2] (let) @4:20 + └─ Identifier "z" [variable:3] (let) @4:24 diff --git a/Tests/LibJS/AST/expected/for-in-of-scoping.txt b/Tests/LibJS/AST/expected/for-in-of-scoping.txt index 4fa1714e3d..4efce0eab7 100644 --- a/Tests/LibJS/AST/expected/for-in-of-scoping.txt +++ b/Tests/LibJS/AST/expected/for-in-of-scoping.txt @@ -61,18 +61,18 @@ Program (script) @2:1 │ │ └─ BindingPattern (array) │ │ ├─ entry │ │ │ └─ alias -│ │ │ └─ Identifier "a" [variable:0] @25:15 +│ │ │ └─ Identifier "a" [variable:0] (let) @25:15 │ │ └─ entry │ │ └─ alias -│ │ └─ Identifier "b" [variable:1] @25:18 +│ │ └─ Identifier "b" [variable:1] (let) @25:18 │ ├─ rhs │ │ └─ Identifier "pairs" [argument:0] @25:24 │ └─ body │ └─ BlockStatement @25:31 │ └─ ExpressionStatement @26:9 │ └─ BinaryExpression (+) @26:11 -│ ├─ Identifier "a" [variable:0] @26:9 -│ └─ Identifier "b" [variable:1] @26:13 +│ ├─ Identifier "a" [variable:0] (let) @26:9 +│ └─ Identifier "b" [variable:1] (let) @26:13 └─ FunctionDeclaration "for_in_eval" [direct-eval] [uses-this] [might-need-arguments] @31:1 ├─ parameters │ └─ Identifier "obj" @31:22 diff --git a/Tests/LibJS/AST/expected/multiple-binding-forms.txt b/Tests/LibJS/AST/expected/multiple-binding-forms.txt index da7bbcdda2..1684d74fed 100644 --- a/Tests/LibJS/AST/expected/multiple-binding-forms.txt +++ b/Tests/LibJS/AST/expected/multiple-binding-forms.txt @@ -87,10 +87,10 @@ Program (script) @4:1 │ │ ├─ BindingPattern (array) │ │ │ ├─ entry │ │ │ │ └─ alias -│ │ │ │ └─ Identifier "a" [variable:0] @28:10 +│ │ │ │ └─ Identifier "a" [variable:0] (let) @28:10 │ │ │ └─ entry │ │ │ └─ alias -│ │ │ └─ Identifier "b" [variable:1] @28:13 +│ │ │ └─ Identifier "b" [variable:1] (let) @28:13 │ │ └─ ArrayExpression @28:18 │ │ ├─ NumericLiteral 1 @28:19 │ │ └─ NumericLiteral 2 @28:22 @@ -99,10 +99,10 @@ Program (script) @4:1 │ │ ├─ BindingPattern (object) │ │ │ ├─ entry │ │ │ │ └─ name -│ │ │ │ └─ Identifier "c" [variable:2] @29:13 +│ │ │ │ └─ Identifier "c" [variable:2] (const) @29:13 │ │ │ └─ entry │ │ │ └─ name -│ │ │ └─ Identifier "d" [variable:3] @29:16 +│ │ │ └─ Identifier "d" [variable:3] (const) @29:16 │ │ └─ ObjectExpression @29:22 │ │ ├─ ObjectProperty @29:22 │ │ │ ├─ StringLiteral "c" @29:24 @@ -115,10 +115,10 @@ Program (script) @4:1 │ │ ├─ BindingPattern (array) │ │ │ ├─ entry │ │ │ │ └─ alias -│ │ │ │ └─ Identifier "e" [variable:4] @30:10 +│ │ │ │ └─ Identifier "e" [variable:4] (var) @30:10 │ │ │ └─ entry (rest) │ │ │ └─ alias -│ │ │ └─ Identifier "f" [variable:5] @30:16 +│ │ │ └─ Identifier "f" [variable:5] (var) @30:16 │ │ └─ ArrayExpression @30:21 │ │ ├─ NumericLiteral 5 @30:22 │ │ ├─ NumericLiteral 6 @30:25 @@ -129,13 +129,13 @@ Program (script) @4:1 │ │ ├─ BinaryExpression (+) @31:22 │ │ │ ├─ BinaryExpression (+) @31:18 │ │ │ │ ├─ BinaryExpression (+) @31:14 -│ │ │ │ │ ├─ Identifier "a" [variable:0] @31:12 -│ │ │ │ │ └─ Identifier "b" [variable:1] @31:16 -│ │ │ │ └─ Identifier "c" [variable:2] @31:20 -│ │ │ └─ Identifier "d" [variable:3] @31:24 -│ │ └─ Identifier "e" [variable:4] @31:28 +│ │ │ │ │ ├─ Identifier "a" [variable:0] (let) @31:12 +│ │ │ │ │ └─ Identifier "b" [variable:1] (let) @31:16 +│ │ │ │ └─ Identifier "c" [variable:2] (const) @31:20 +│ │ │ └─ Identifier "d" [variable:3] (const) @31:24 +│ │ └─ Identifier "e" [variable:4] (var) @31:28 │ └─ MemberExpression [computed] @31:33 -│ ├─ Identifier "f" [variable:5] @31:32 +│ ├─ Identifier "f" [variable:5] (var) @31:32 │ └─ NumericLiteral 0 @31:34 └─ FunctionDeclaration "multi_declarator" @35:1 └─ body diff --git a/Tests/LibJS/AST/expected/property-key-arguments.txt b/Tests/LibJS/AST/expected/property-key-arguments.txt index e54633410b..470a3b02b0 100644 --- a/Tests/LibJS/AST/expected/property-key-arguments.txt +++ b/Tests/LibJS/AST/expected/property-key-arguments.txt @@ -49,13 +49,13 @@ Program (script) @3:1 │ │ │ ├─ name │ │ │ │ └─ Identifier "arguments" [variable:0] @37:11 │ │ │ └─ alias -│ │ │ └─ Identifier "x" [variable:1] @37:22 +│ │ │ └─ Identifier "x" [variable:1] (let) @37:22 │ │ └─ ObjectExpression @37:28 │ │ └─ ObjectProperty @37:28 │ │ ├─ StringLiteral "arguments" @37:30 │ │ └─ NumericLiteral 1 @37:41 │ └─ ReturnStatement @38:5 -│ └─ Identifier "x" [variable:1] @38:12 +│ └─ Identifier "x" [variable:1] (let) @38:12 └─ FunctionDeclaration "method_named_arguments" @42:1 └─ body └─ FunctionBody @43:5 diff --git a/Tests/LibJS/Bytecode/expected/assign-to-let-variable-tdz.txt b/Tests/LibJS/Bytecode/expected/assign-to-let-variable-tdz.txt index beb7b2dc77..9189704d88 100644 --- a/Tests/LibJS/Bytecode/expected/assign-to-let-variable-tdz.txt +++ b/Tests/LibJS/Bytecode/expected/assign-to-let-variable-tdz.txt @@ -14,7 +14,7 @@ block0: [ 70] End value:reg5 -f$ce57f4e1 assign-to-let-variable-tdz.js:7:5 +f$d3dfd701 assign-to-let-variable-tdz.js:7:5 Registers: 10 Blocks: 3 Locals: a~0 @@ -26,23 +26,19 @@ block0: [ 0] ThrowIfNullish src:arg0 [ 8] GetById dst:reg5, base:arg0, `location` [ 20] Mov dst:a~0, src:reg5 - [ 30] ThrowIfTDZ src:a~0 - [ 38] Typeof dst:reg5, src:a~0 - [ 48] LooselyEquals dst:reg6, lhs:String("string"), rhs:reg5 - [ 58] Mov dst:reg5, src:reg6 - [ 68] JumpFalse condition:reg6, target:block2 + [ 30] Typeof dst:reg5, src:a~0 + [ 40] LooselyEquals dst:reg6, lhs:String("string"), rhs:reg5 + [ 50] Mov dst:reg5, src:reg6 + [ 60] JumpFalse condition:reg6, target:block2 block1: - [ 78] GetGlobal dst:reg8, `g` - [ 88] ThrowIfTDZ src:a~0 - [ 90] Mov dst:reg9, src:a~0 - [ a0] Call dst:reg7, callee:reg8, this_value:Undefined, g, arguments:[reg9] - [ c8] ThrowIfTDZ src:a~0 - [ d0] Mov2 dst1:a~0, src1:reg7, dst2:reg5, src2:reg7 + [ 70] GetGlobal dst:reg8, `g` + [ 80] Mov dst:reg9, src:a~0 + [ 90] Call dst:reg7, callee:reg8, this_value:Undefined, g, arguments:[reg9] + [ b8] Mov2 dst1:a~0, src1:reg7, dst2:reg5, src2:reg7 block2: - [ e8] ThrowIfTDZ src:a~0 - [ f0] Return value:a~0 + [ d0] Return value:a~0 g$f452072f assign-to-let-variable-tdz.js:4:17 diff --git a/Tests/LibJS/Bytecode/expected/destructure-array-rest.txt b/Tests/LibJS/Bytecode/expected/destructure-array-rest.txt index 459e9ace6a..0f7db15208 100644 --- a/Tests/LibJS/Bytecode/expected/destructure-array-rest.txt +++ b/Tests/LibJS/Bytecode/expected/destructure-array-rest.txt @@ -10,7 +10,7 @@ block0: [ 30] End value:reg5 -f$c7e5aa3e destructure-array-rest.js:6:5 +f$26085a1b destructure-array-rest.js:6:5 Registers: 14 Blocks: 19 Locals: a~0, b~1 @@ -34,65 +34,64 @@ block1: [ 80] JumpIf condition:reg9, true_target:block14, false_target:block13 block2: - [ 90] ThrowIfTDZ src:b~1 - [ 98] Return value:b~1 + [ 90] Return value:b~1 block3: - [ a0] Jump target:block5 + [ 98] Jump target:block5 block4: - [ a8] Mov dst:reg12, src:Undefined - [ b8] Jump target:block6 + [ a0] Mov dst:reg12, src:Undefined + [ b0] Jump target:block6 block5: - [ c0] IteratorNextUnpack dst_value:reg12, dst_done:reg6, iterator_object:reg7, iterator_next:reg8, iterator_done:reg9 - [ d8] JumpTrue condition:reg6, target:block4 + [ b8] IteratorNextUnpack dst_value:reg12, dst_done:reg6, iterator_object:reg7, iterator_next:reg8, iterator_done:reg9 + [ d0] JumpTrue condition:reg6, target:block4 block6: - [ e8] Mov dst:a~0, src:reg12 - [ f8] JumpFalse condition:reg6, target:block8 + [ e0] Mov dst:a~0, src:reg12 + [ f0] JumpFalse condition:reg6, target:block8 block7: - [ 108] NewArray dst:reg13 - [ 118] Jump target:block9 + [ 100] NewArray dst:reg13 + [ 110] Jump target:block9 block8: - [ 120] IteratorToArray dst:reg13, iterator_object:reg7, iterator_next_method:reg8, iterator_done_property:reg9 + [ 118] IteratorToArray dst:reg13, iterator_object:reg7, iterator_next_method:reg8, iterator_done_property:reg9 block9: - [ 138] Mov dst:b~1, src:reg13 + [ 130] Mov dst:b~1, src:reg13 block10: - [ 148] JumpFalse condition:reg9, target:block12 + [ 140] JumpFalse condition:reg9, target:block12 block11: - [ 158] Jump target:block2 + [ 150] Return value:b~1 block12: - [ 160] IteratorClose iterator_object:reg7, iterator_next:reg8, iterator_done:reg9, completion_value:Undefined - [ 178] Jump target:block11 + [ 158] IteratorClose iterator_object:reg7, iterator_next:reg8, iterator_done:reg9, completion_value:Undefined + [ 170] Jump target:block11 block13: - [ 180] JumpStrictlyEquals lhs:reg10, rhs:Int32(1), true_target:block15, false_target:block16 + [ 178] JumpStrictlyEquals lhs:reg10, rhs:Int32(1), true_target:block15, false_target:block16 block14: - [ 198] JumpStrictlyEquals lhs:reg10, rhs:Int32(2), true_target:block17, false_target:block18 + [ 190] JumpStrictlyEquals lhs:reg10, rhs:Int32(2), true_target:block17, false_target:block18 block15: - [ 1b0] IteratorClose iterator_object:reg7, iterator_next:reg8, iterator_done:reg9, completion_value:reg11 - [ 1c8] Throw src:reg11 + [ 1a8] IteratorClose iterator_object:reg7, iterator_next:reg8, iterator_done:reg9, completion_value:reg11 + [ 1c0] Throw src:reg11 block16: - [ 1d0] IteratorClose iterator_object:reg7, iterator_next:reg8, iterator_done:reg9, completion_value:Undefined - [ 1e8] Jump target:block14 + [ 1c8] IteratorClose iterator_object:reg7, iterator_next:reg8, iterator_done:reg9, completion_value:Undefined + [ 1e0] Jump target:block14 block17: - [ 1f0] Return value:reg11 + [ 1e8] Return value:reg11 block18: - [ 1f8] Throw src:reg11 + [ 1f0] Throw src:reg11 Exception handlers: - [ a0 .. c0] => handler block1 - [ e8 .. 120] => handler block1 - [ 138 .. 148] => handler block1 + [ 98 .. b8] => handler block1 + [ e0 .. 118] => handler block1 + [ 130 .. 140] => handler block1 diff --git a/Tests/LibJS/Bytecode/expected/destructuring-let-compound-assign.txt b/Tests/LibJS/Bytecode/expected/destructuring-let-compound-assign.txt index 5db908a8a4..7c5d24ed57 100644 --- a/Tests/LibJS/Bytecode/expected/destructuring-let-compound-assign.txt +++ b/Tests/LibJS/Bytecode/expected/destructuring-let-compound-assign.txt @@ -12,7 +12,7 @@ block0: [ 58] End value:reg5 -f$f789224c destructuring-let-compound-assign.js:2:5 +f$ffd4f57c destructuring-let-compound-assign.js:2:5 Registers: 8 Blocks: 1 Locals: a~0 @@ -23,11 +23,9 @@ f$f789224c destructuring-let-compound-assign.js:2:5 block0: [ 0] ThrowIfNullish src:arg0 [ 8] GetById dst:reg5, base:arg0, `patchFlag` - [ 20] Mov dst:a~0, src:reg5 - [ 30] ThrowIfTDZ src:a~0 - [ 38] Mov2 dst1:reg5, src1:a~0, dst2:reg6, src2:arg1 - [ 50] GetById dst:reg7, base:reg6, `patchFlag` (e.patchFlag) - [ 68] BitwiseAnd dst:reg6, lhs:Int32(16), rhs:reg7 - [ 78] BitwiseOr dst:reg7, lhs:reg5, rhs:reg6 - [ 88] Mov dst:a~0, src:reg7 - [ 98] End value:Undefined + [ 20] Mov3 dst1:a~0, src1:reg5, dst2:reg5, src2:a~0, dst3:reg6, src3:arg1 + [ 40] GetById dst:reg7, base:reg6, `patchFlag` (e.patchFlag) + [ 58] BitwiseAnd dst:reg6, lhs:Int32(16), rhs:reg7 + [ 68] BitwiseOr dst:reg7, lhs:reg5, rhs:reg6 + [ 78] Mov dst:a~0, src:reg7 + [ 88] End value:Undefined diff --git a/Tests/LibJS/Bytecode/expected/for-in-object-property-iterator-next.txt b/Tests/LibJS/Bytecode/expected/for-in-object-property-iterator-next.txt index f8c4bcdf12..43af2ffc18 100644 --- a/Tests/LibJS/Bytecode/expected/for-in-object-property-iterator-next.txt +++ b/Tests/LibJS/Bytecode/expected/for-in-object-property-iterator-next.txt @@ -21,7 +21,7 @@ block0: [ b8] End value:reg5 -collect$aa10966b for-in-object-property-iterator-next.js:2:18 +collect$a1c4c33b for-in-object-property-iterator-next.js:2:18 Registers: 12 Blocks: 6 Locals: key~0, keys~1 @@ -47,8 +47,6 @@ block4: block5: [ 68] Mov dst:key~0, src:reg6 [ 78] GetById dst:reg9, base:keys~1, `push` (keys.push) - [ 90] Mov dst:reg10, src:keys~1 - [ a0] ThrowIfTDZ src:key~0 - [ a8] Mov dst:reg11, src:key~0 - [ b8] Call dst:reg8, callee:reg9, this_value:reg10, keys.push, arguments:[reg11] - [ e0] Jump target:block2 + [ 90] Mov2 dst1:reg10, src1:keys~1, dst2:reg11, src2:key~0 + [ a8] Call dst:reg8, callee:reg9, this_value:reg10, keys.push, arguments:[reg11] + [ d0] Jump target:block2 diff --git a/Tests/LibJS/Bytecode/expected/for-of-cond-rhs-block-order.txt b/Tests/LibJS/Bytecode/expected/for-of-cond-rhs-block-order.txt index b087f30577..df9ef92add 100644 --- a/Tests/LibJS/Bytecode/expected/for-of-cond-rhs-block-order.txt +++ b/Tests/LibJS/Bytecode/expected/for-of-cond-rhs-block-order.txt @@ -11,7 +11,7 @@ block0: [ 38] End value:reg5 -f$08f135b5 for-of-cond-rhs-block-order.js:2:21 +f$8a532e3d for-of-cond-rhs-block-order.js:2:21 Registers: 19 Blocks: 31 Locals: r~0, n~1 @@ -69,85 +69,84 @@ block9: [ 1a0] JumpIf condition:reg15, true_target:block22, false_target:block21 block10: - [ 1b0] ThrowIfTDZ src:r~0 - [ 1b8] Jump target:block2 + [ 1b0] Jump target:block2 block11: - [ 1c0] Jump target:block13 + [ 1b8] Jump target:block13 block12: - [ 1c8] Mov dst:reg18, src:Undefined - [ 1d8] Jump target:block14 + [ 1c0] Mov dst:reg18, src:Undefined + [ 1d0] Jump target:block14 block13: - [ 1e0] IteratorNextUnpack dst_value:reg18, dst_done:reg12, iterator_object:reg13, iterator_next:reg14, iterator_done:reg15 - [ 1f8] JumpTrue condition:reg12, target:block12 + [ 1d8] IteratorNextUnpack dst_value:reg18, dst_done:reg12, iterator_object:reg13, iterator_next:reg14, iterator_done:reg15 + [ 1f0] JumpTrue condition:reg12, target:block12 block14: - [ 208] Mov dst:r~0, src:reg18 - [ 218] JumpFalse condition:reg12, target:block16 + [ 200] Mov dst:r~0, src:reg18 + [ 210] JumpFalse condition:reg12, target:block16 block15: - [ 228] Mov dst:reg18, src:Undefined - [ 238] Jump target:block17 + [ 220] Mov dst:reg18, src:Undefined + [ 230] Jump target:block17 block16: - [ 240] IteratorNextUnpack dst_value:reg18, dst_done:reg12, iterator_object:reg13, iterator_next:reg14, iterator_done:reg15 - [ 258] JumpTrue condition:reg12, target:block15 + [ 238] IteratorNextUnpack dst_value:reg18, dst_done:reg12, iterator_object:reg13, iterator_next:reg14, iterator_done:reg15 + [ 250] JumpTrue condition:reg12, target:block15 block17: - [ 268] Mov dst:n~1, src:reg18 + [ 260] Mov dst:n~1, src:reg18 block18: - [ 278] JumpFalse condition:reg15, target:block20 + [ 270] JumpFalse condition:reg15, target:block20 block19: - [ 288] Jump target:block10 + [ 280] Jump target:block10 block20: - [ 290] IteratorClose iterator_object:reg13, iterator_next:reg14, iterator_done:reg15, completion_value:Undefined - [ 2a8] Jump target:block19 + [ 288] IteratorClose iterator_object:reg13, iterator_next:reg14, iterator_done:reg15, completion_value:Undefined + [ 2a0] Jump target:block19 block21: - [ 2b0] JumpStrictlyEquals lhs:reg16, rhs:Int32(1), true_target:block23, false_target:block24 + [ 2a8] JumpStrictlyEquals lhs:reg16, rhs:Int32(1), true_target:block23, false_target:block24 block22: - [ 2c8] JumpStrictlyEquals lhs:reg16, rhs:Int32(2), true_target:block25, false_target:block26 + [ 2c0] JumpStrictlyEquals lhs:reg16, rhs:Int32(2), true_target:block25, false_target:block26 block23: - [ 2e0] IteratorClose iterator_object:reg13, iterator_next:reg14, iterator_done:reg15, completion_value:reg17 - [ 2f8] Throw src:reg17 + [ 2d8] IteratorClose iterator_object:reg13, iterator_next:reg14, iterator_done:reg15, completion_value:reg17 + [ 2f0] Throw src:reg17 block24: - [ 300] IteratorClose iterator_object:reg13, iterator_next:reg14, iterator_done:reg15, completion_value:Undefined - [ 318] Jump target:block22 + [ 2f8] IteratorClose iterator_object:reg13, iterator_next:reg14, iterator_done:reg15, completion_value:Undefined + [ 310] Jump target:block22 block25: - [ 320] Mov2 dst1:reg7, src1:reg16, dst2:reg9, src2:reg17 - [ 338] Jump target:block7 + [ 318] Mov2 dst1:reg7, src1:reg16, dst2:reg9, src2:reg17 + [ 330] Jump target:block7 block26: - [ 340] Throw src:reg17 + [ 338] Throw src:reg17 block27: - [ 348] IteratorClose iterator_object:reg5, iterator_next:reg8, iterator_done:reg6, completion_value:reg9 - [ 360] Throw src:reg9 + [ 340] IteratorClose iterator_object:reg5, iterator_next:reg8, iterator_done:reg6, completion_value:reg9 + [ 358] Throw src:reg9 block28: - [ 368] IteratorClose iterator_object:reg5, iterator_next:reg8, iterator_done:reg6, completion_value:Undefined - [ 380] JumpStrictlyEquals lhs:reg7, rhs:Int32(2), true_target:block29, false_target:block30 + [ 360] IteratorClose iterator_object:reg5, iterator_next:reg8, iterator_done:reg6, completion_value:Undefined + [ 378] JumpStrictlyEquals lhs:reg7, rhs:Int32(2), true_target:block29, false_target:block30 block29: - [ 398] Return value:reg9 + [ 390] Return value:reg9 block30: - [ 3a0] Throw src:reg9 + [ 398] Throw src:reg9 Exception handlers: - [ 150 .. 1c0] => handler block6 - [ 1c0 .. 1e0] => handler block9 - [ 1e0 .. 208] => handler block6 - [ 208 .. 240] => handler block9 - [ 240 .. 268] => handler block6 - [ 268 .. 278] => handler block9 - [ 278 .. 348] => handler block6 + [ 150 .. 1b8] => handler block6 + [ 1b8 .. 1d8] => handler block9 + [ 1d8 .. 200] => handler block6 + [ 200 .. 238] => handler block9 + [ 238 .. 260] => handler block6 + [ 260 .. 270] => handler block9 + [ 270 .. 340] => handler block6 diff --git a/Tests/LibJS/Bytecode/expected/for-of-iteration-env-capacity.txt b/Tests/LibJS/Bytecode/expected/for-of-iteration-env-capacity.txt index bfa1d57d5d..e424d699c0 100644 --- a/Tests/LibJS/Bytecode/expected/for-of-iteration-env-capacity.txt +++ b/Tests/LibJS/Bytecode/expected/for-of-iteration-env-capacity.txt @@ -1,4 +1,4 @@ -$0a509d5d for-of-iteration-env-capacity.js:1:14 +$078cac4d for-of-iteration-env-capacity.js:1:14 Registers: 13 Blocks: 9 Locals: x~0 @@ -28,24 +28,22 @@ block3: [ b0] JumpStrictlyEquals lhs:reg9, rhs:Int32(1), true_target:block5, false_target:block6 block4: - [ c8] Mov dst:x~0, src:reg11 - [ d8] ThrowIfTDZ src:x~0 - [ e0] Mov dst:reg5, src:x~0 - [ f0] Jump target:block2 + [ c8] Mov2 dst1:x~0, src1:reg11, dst2:reg5, src2:x~0 + [ e0] Jump target:block2 block5: - [ f8] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg10 - [ 110] Throw src:reg10 + [ e8] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg10 + [ 100] Throw src:reg10 block6: - [ 118] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined - [ 130] JumpStrictlyEquals lhs:reg9, rhs:Int32(2), true_target:block7, false_target:block8 + [ 108] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined + [ 120] JumpStrictlyEquals lhs:reg9, rhs:Int32(2), true_target:block7, false_target:block8 block7: - [ 148] Return value:reg10 + [ 138] Return value:reg10 block8: - [ 150] Throw src:reg10 + [ 140] Throw src:reg10 Exception handlers: - [ c8 .. f8] => handler block3 + [ c8 .. e8] => handler block3 diff --git a/Tests/LibJS/Bytecode/expected/for-of-iterator-close.txt b/Tests/LibJS/Bytecode/expected/for-of-iterator-close.txt index 8f2d6d5cf0..cfbf077273 100644 --- a/Tests/LibJS/Bytecode/expected/for-of-iterator-close.txt +++ b/Tests/LibJS/Bytecode/expected/for-of-iterator-close.txt @@ -15,7 +15,7 @@ block0: [ c0] End value:reg5 -forOfBreak$0dacb993 for-of-iterator-close.js:2:18 +forOfBreak$7367477b for-of-iterator-close.js:2:18 Registers: 13 Blocks: 13 Locals: x~0 @@ -48,38 +48,37 @@ block4: block5: [ b8] Mov dst:x~0, src:reg10 - [ c8] ThrowIfTDZ src:x~0 - [ d0] JumpStrictlyEquals lhs:x~0, rhs:Int32(2), true_target:block6, false_target:block7 + [ c8] JumpStrictlyEquals lhs:x~0, rhs:Int32(2), true_target:block6, false_target:block7 block6: - [ e8] Mov dst:reg5, src:Int32(3) - [ f8] Jump target:block4 + [ e0] Mov dst:reg5, src:Int32(3) + [ f0] Jump target:block4 block7: - [ 100] Jump target:block2 + [ f8] Jump target:block2 block8: - [ 108] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg9 - [ 120] Throw src:reg9 + [ 100] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg9 + [ 118] Throw src:reg9 block9: - [ 128] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined - [ 140] JumpStrictlyEquals lhs:reg5, rhs:Int32(3), true_target:block1, false_target:block10 + [ 120] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined + [ 138] JumpStrictlyEquals lhs:reg5, rhs:Int32(3), true_target:block1, false_target:block10 block10: - [ 158] JumpStrictlyEquals lhs:reg5, rhs:Int32(2), true_target:block11, false_target:block12 + [ 150] JumpStrictlyEquals lhs:reg5, rhs:Int32(2), true_target:block11, false_target:block12 block11: - [ 170] Return value:reg9 + [ 168] Return value:reg9 block12: - [ 178] Throw src:reg9 + [ 170] Throw src:reg9 Exception handlers: - [ b8 .. 108] => handler block3 + [ b8 .. 100] => handler block3 -forOfReturn$3e676e69 for-of-iterator-close.js:9:18 +forOfReturn$3ba37d59 for-of-iterator-close.js:9:18 Registers: 12 Blocks: 10 Locals: x~0 @@ -110,30 +109,28 @@ block4: [ a0] JumpStrictlyEquals lhs:reg5, rhs:Int32(1), true_target:block6, false_target:block7 block5: - [ b8] Mov dst:x~0, src:reg10 - [ c8] ThrowIfTDZ src:x~0 - [ d0] Mov2 dst1:reg9, src1:x~0, dst2:reg5, src2:Int32(2) - [ e8] Jump target:block4 + [ b8] Mov3 dst1:x~0, src1:reg10, dst2:reg9, src2:x~0, dst3:reg5, src3:Int32(2) + [ d8] Jump target:block4 block6: - [ f0] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg9 - [ 108] Throw src:reg9 + [ e0] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg9 + [ f8] Throw src:reg9 block7: - [ 110] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined - [ 128] JumpStrictlyEquals lhs:reg5, rhs:Int32(2), true_target:block8, false_target:block9 + [ 100] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined + [ 118] JumpStrictlyEquals lhs:reg5, rhs:Int32(2), true_target:block8, false_target:block9 block8: - [ 140] Return value:reg9 + [ 130] Return value:reg9 block9: - [ 148] Throw src:reg9 + [ 138] Throw src:reg9 Exception handlers: - [ b8 .. f0] => handler block3 + [ b8 .. e0] => handler block3 -forAwaitOfBreak$16089337 for-of-iterator-close.js:16:24 +forAwaitOfBreak$976a8bbf for-of-iterator-close.js:16:24 Registers: 18 Blocks: 30 Locals: x~0 @@ -186,77 +183,76 @@ block9: block10: [ 150] Mov dst:x~0, src:reg10 - [ 160] ThrowIfTDZ src:x~0 - [ 168] JumpStrictlyEquals lhs:x~0, rhs:Int32(2), true_target:block11, false_target:block12 + [ 160] JumpStrictlyEquals lhs:x~0, rhs:Int32(2), true_target:block11, false_target:block12 block11: - [ 180] Mov dst:reg8, src:Int32(3) - [ 190] Jump target:block5 + [ 178] Mov dst:reg8, src:Int32(3) + [ 188] Jump target:block5 block12: - [ 198] Jump target:block3 + [ 190] Jump target:block3 block13: - [ 1a0] Jump target:block25 + [ 198] Jump target:block25 block14: - [ 1a8] GetMethod dst:reg12, object:reg5, `return` - [ 1b8] JumpUndefined condition:reg12, true_target:block15, false_target:block16 + [ 1a0] GetMethod dst:reg12, object:reg5, `return` + [ 1b0] JumpUndefined condition:reg12, true_target:block15, false_target:block16 block15: - [ 1c8] JumpStrictlyEquals lhs:reg8, rhs:Int32(3), true_target:block2, false_target:block20 + [ 1c0] JumpStrictlyEquals lhs:reg8, rhs:Int32(3), true_target:block2, false_target:block20 block16: - [ 1e0] Call dst:reg13, callee:reg12, this_value:reg5 - [ 200] Await continuation_label:block17, argument:reg13 + [ 1d8] Call dst:reg13, callee:reg12, this_value:reg5 + [ 1f8] Await continuation_label:block17, argument:reg13 block17: - [ 210] Mov dst:reg14, src:reg0 - [ 220] GetCompletionFields type_dst:reg15, value_dst:reg16, completion:reg14 - [ 230] JumpStrictlyEquals lhs:reg15, rhs:Int32(1), true_target:block18, false_target:block19 + [ 208] Mov dst:reg14, src:reg0 + [ 218] GetCompletionFields type_dst:reg15, value_dst:reg16, completion:reg14 + [ 228] JumpStrictlyEquals lhs:reg15, rhs:Int32(1), true_target:block18, false_target:block19 block18: - [ 248] ThrowIfNotObject src:reg16 - [ 250] Jump target:block15 + [ 240] ThrowIfNotObject src:reg16 + [ 248] Jump target:block15 block19: - [ 258] Throw src:reg16 + [ 250] Throw src:reg16 block20: - [ 260] JumpStrictlyEquals lhs:reg8, rhs:Int32(2), true_target:block21, false_target:block22 + [ 258] JumpStrictlyEquals lhs:reg8, rhs:Int32(2), true_target:block21, false_target:block22 block21: - [ 278] Return value:reg9 + [ 270] Return value:reg9 block22: - [ 280] Throw src:reg9 + [ 278] Throw src:reg9 block23: - [ 288] Throw src:reg9 + [ 280] Throw src:reg9 block24: - [ 290] Catch dst:reg12 - [ 298] Jump target:block23 + [ 288] Catch dst:reg12 + [ 290] Jump target:block23 block25: - [ 2a0] GetMethod dst:reg12, object:reg5, `return` - [ 2b0] JumpUndefined condition:reg12, true_target:block23, false_target:block26 + [ 298] GetMethod dst:reg12, object:reg5, `return` + [ 2a8] JumpUndefined condition:reg12, true_target:block23, false_target:block26 block26: - [ 2c0] Call dst:reg13, callee:reg12, this_value:reg5 - [ 2e0] Await continuation_label:block27, argument:reg13 + [ 2b8] Call dst:reg13, callee:reg12, this_value:reg5 + [ 2d8] Await continuation_label:block27, argument:reg13 block27: - [ 2f0] Mov dst:reg14, src:reg0 - [ 300] GetCompletionFields type_dst:reg15, value_dst:reg16, completion:reg14 - [ 310] JumpStrictlyEquals lhs:reg15, rhs:Int32(1), true_target:block28, false_target:block29 + [ 2e8] Mov dst:reg14, src:reg0 + [ 2f8] GetCompletionFields type_dst:reg15, value_dst:reg16, completion:reg14 + [ 308] JumpStrictlyEquals lhs:reg15, rhs:Int32(1), true_target:block28, false_target:block29 block28: - [ 328] Jump target:block23 + [ 320] Jump target:block23 block29: - [ 330] Throw src:reg16 + [ 328] Throw src:reg16 Exception handlers: - [ 150 .. 1a0] => handler block4 - [ 2a0 .. 338] => handler block24 + [ 150 .. 198] => handler block4 + [ 298 .. 330] => handler block24 diff --git a/Tests/LibJS/Bytecode/expected/lexical-env-teardown.txt b/Tests/LibJS/Bytecode/expected/lexical-env-teardown.txt index 42b10268c5..849246a873 100644 --- a/Tests/LibJS/Bytecode/expected/lexical-env-teardown.txt +++ b/Tests/LibJS/Bytecode/expected/lexical-env-teardown.txt @@ -84,7 +84,7 @@ block0: [ 18] Return value:outer~1 -forInTeardown$7c859710 lexical-env-teardown.js:28:5 +forInTeardown$fde78f98 lexical-env-teardown.js:28:5 Registers: 8 Blocks: 6 Locals: k~0, outer~1 @@ -117,11 +117,10 @@ block4: block5: [ b8] Mov dst:k~0, src:reg5 - [ c8] ThrowIfTDZ src:k~0 - [ d0] Jump target:block2 + [ c8] Jump target:block2 -forOfTeardown$94a2ae97 lexical-env-teardown.js:38:5 +forOfTeardown$269c4d7f lexical-env-teardown.js:38:5 Registers: 12 Blocks: 9 Locals: v~0, outer~1 @@ -153,25 +152,24 @@ block3: block4: [ c0] Mov dst:v~0, src:reg10 - [ d0] ThrowIfTDZ src:v~0 - [ d8] Jump target:block2 + [ d0] Jump target:block2 block5: - [ e0] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg9 - [ f8] Throw src:reg9 + [ d8] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:reg9 + [ f0] Throw src:reg9 block6: - [ 100] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined - [ 118] JumpStrictlyEquals lhs:reg5, rhs:Int32(2), true_target:block7, false_target:block8 + [ f8] IteratorClose iterator_object:reg6, iterator_next:reg7, iterator_done:reg8, completion_value:Undefined + [ 110] JumpStrictlyEquals lhs:reg5, rhs:Int32(2), true_target:block7, false_target:block8 block7: - [ 130] Return value:reg9 + [ 128] Return value:reg9 block8: - [ 138] Throw src:reg9 + [ 130] Throw src:reg9 Exception handlers: - [ c0 .. e0] => handler block3 + [ c0 .. d8] => handler block3 catchTeardown$6b833e8d lexical-env-teardown.js:48:5 diff --git a/Tests/LibJS/Runtime/const-reassignment.js b/Tests/LibJS/Runtime/const-reassignment.js index 1410394443..03add9a8d4 100644 --- a/Tests/LibJS/Runtime/const-reassignment.js +++ b/Tests/LibJS/Runtime/const-reassignment.js @@ -80,3 +80,19 @@ test("const creation in inner scope", () => { } while (false); expect(constantValue).toBe(1); }); + +test("reassignment to destructured const in function", () => { + expect(() => { + (() => { + const { value } = { value: 1 }; + value = 2; + })(); + }).toThrowWithMessage(TypeError, "Invalid assignment to const variable"); + + expect(() => { + (() => { + const [value] = [1]; + [value] = [2]; + })(); + }).toThrowWithMessage(TypeError, "Invalid assignment to const variable"); +}); diff --git a/Tests/LibJS/Runtime/loops/for-in-basic.js b/Tests/LibJS/Runtime/loops/for-in-basic.js index 43dda78eb5..30ab90ea74 100644 --- a/Tests/LibJS/Runtime/loops/for-in-basic.js +++ b/Tests/LibJS/Runtime/loops/for-in-basic.js @@ -217,6 +217,18 @@ describe("special left hand sides", () => { expect(vals).toEqual(["0", "1"]); }); + + test("Cannot change destructured constant declaration in body", () => { + const vals = []; + for (const { 0: value } in "ab") { + expect(() => { + value = "x"; + }).toThrowWithMessage(TypeError, "Invalid assignment to const variable"); + vals.push(value); + } + + expect(vals).toEqual(["0", "1"]); + }); }); test("remove properties while iterating", () => { diff --git a/Tests/LibJS/Runtime/loops/for-of-basic.js b/Tests/LibJS/Runtime/loops/for-of-basic.js index f3916cc6ff..33734f812e 100644 --- a/Tests/LibJS/Runtime/loops/for-of-basic.js +++ b/Tests/LibJS/Runtime/loops/for-of-basic.js @@ -150,4 +150,16 @@ describe("special left hand sides", () => { expect(vals).toEqual([1, 2]); }); + + test("Cannot change destructured constant declaration in body", () => { + const vals = []; + for (const [value] of [[1], [2]]) { + expect(() => { + value = 3; + }).toThrowWithMessage(TypeError, "Invalid assignment to const variable"); + vals.push(value); + } + + expect(vals).toEqual([1, 2]); + }); }); diff --git a/Tests/LibJS/Runtime/string-basic.js b/Tests/LibJS/Runtime/string-basic.js index 47ffe6f61c..abcdc36f69 100644 --- a/Tests/LibJS/Runtime/string-basic.js +++ b/Tests/LibJS/Runtime/string-basic.js @@ -269,6 +269,19 @@ test("string object: string property indexing", () => { expect(lastSetThisValue).toBeNull(); lastSetThisValue = null; }); + +test("primitive string object destructuring sees virtual indexed properties", () => { + const { 0: first, 1: second, length } = "ab"; + expect(first).toBe("a"); + expect(second).toBe("b"); + expect(length).toBe(2); + + const values = []; + for (const { 0: c } of "ab") { + values.push(c); + } + expect(values).toEqual(["a", "b"]); +}); test("string object: string property name access", () => { expect(new String("").length).toBe(0); expect(new String("foo").length).toBe(3);