summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authordavidot <davidot@serenityos.org>2022-11-30 01:45:35 +0100
committerAndreas Kling <kling@serenityos.org>2022-11-30 08:05:37 +0100
commit8319d7ac06256bcfacaeca1ef322dc930fdea963 (patch)
tree959c9567254bd632b72ac1ca108351cdd4b3b262 /Userland
parent714f0c3dce22dc7218567b925b4af78e6b0f3928 (diff)
downloadserenity-8319d7ac06256bcfacaeca1ef322dc930fdea963.zip
LibJS: Fix that constant declaration in for loop was mutable in body
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibJS/AST.cpp18
-rw-r--r--Userland/Libraries/LibJS/Tests/loops/for-in-basic.js10
-rw-r--r--Userland/Libraries/LibJS/Tests/loops/for-of-basic.js10
3 files changed, 34 insertions, 4 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp
index 313a5d6071..51d37625be 100644
--- a/Userland/Libraries/LibJS/AST.cpp
+++ b/Userland/Libraries/LibJS/AST.cpp
@@ -921,12 +921,22 @@ struct ForInOfHeadState {
else {
VERIFY(expression_lhs && is<VariableDeclaration>(*expression_lhs));
iteration_environment = new_declarative_environment(*interpreter.lexical_environment());
+
auto& for_declaration = static_cast<VariableDeclaration const&>(*expression_lhs);
+
+ // 14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation, https://tc39.es/ecma262/#sec-runtime-semantics-fordeclarationbindinginstantiation
+ // 1. For each element name of the BoundNames of ForBinding, do
for_declaration.for_each_bound_name([&](auto const& name) {
- if (for_declaration.declaration_kind() == DeclarationKind::Const)
- MUST(iteration_environment->create_immutable_binding(vm, name, false));
- else
- MUST(iteration_environment->create_mutable_binding(vm, name, true));
+ // a. If IsConstantDeclaration of LetOrConst is true, then
+ if (for_declaration.is_constant_declaration()) {
+ // i. Perform ! environment.CreateImmutableBinding(name, true).
+ MUST(iteration_environment->create_immutable_binding(vm, name, true));
+ }
+ // b. Else,
+ else {
+ // i. Perform ! environment.CreateMutableBinding(name, false).
+ MUST(iteration_environment->create_mutable_binding(vm, name, false));
+ }
});
interpreter.vm().running_execution_context().lexical_environment = iteration_environment;
diff --git a/Userland/Libraries/LibJS/Tests/loops/for-in-basic.js b/Userland/Libraries/LibJS/Tests/loops/for-in-basic.js
index f7b018ca38..f25c43725d 100644
--- a/Userland/Libraries/LibJS/Tests/loops/for-in-basic.js
+++ b/Userland/Libraries/LibJS/Tests/loops/for-in-basic.js
@@ -98,6 +98,16 @@ describe("special left hand sides", () => {
eval("for (f() in [0]) { expect().fail() }");
}).toThrowWithMessage(ReferenceError, "Invalid left-hand side in assignment");
});
+
+ test("Cannot change constant declaration in body", () => {
+ const vals = [];
+ for (const v in [1, 2]) {
+ expect(() => v++).toThrowWithMessage(TypeError, "Invalid assignment to const variable");
+ vals.push(v);
+ }
+
+ expect(vals).toEqual(["0", "1"]);
+ });
});
test("remove properties while iterating", () => {
diff --git a/Userland/Libraries/LibJS/Tests/loops/for-of-basic.js b/Userland/Libraries/LibJS/Tests/loops/for-of-basic.js
index 29fcb5d2b9..b5753d76dc 100644
--- a/Userland/Libraries/LibJS/Tests/loops/for-of-basic.js
+++ b/Userland/Libraries/LibJS/Tests/loops/for-of-basic.js
@@ -142,4 +142,14 @@ describe("special left hand sides", () => {
eval("for (f() of [0]) { expect().fail() }");
}).toThrowWithMessage(ReferenceError, "Invalid left-hand side in assignment");
});
+
+ test("Cannot change constant declaration in body", () => {
+ const vals = [];
+ for (const v of [1, 2]) {
+ expect(() => v++).toThrowWithMessage(TypeError, "Invalid assignment to const variable");
+ vals.push(v);
+ }
+
+ expect(vals).toEqual([1, 2]);
+ });
});