-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[JSC] Align duplicate declaration checks in EvalDeclarationInstantiat…
…ion with the spec https://bugs.webkit.org/show_bug.cgi?id=167837 <rdar://problem/111328974> Reviewed by Yusuke Suzuki. This change is a re-land of 265614@main with getSloppyModeHoistedFunctions() fixed not to set IsSloppyModeHoistingCandidate bit for `var` declarations. This ensures that for a `var` binding, which shadows a lexical declaration from an outer scope, a SyntaxError is raised even if there is Annex B hoisted function by the same identifer. --- For the sloppy-mode eval(), this change: 1. Removes slowish TypeError-throwing logic from executeEval() that also wasn't spec-compliant (SyntaxError should be raised instead), harmonizing error messages. 2. Expands resolveScopeForHoistingFuncDeclInEval() to be called for all declared variables, which currently includes function declarations as well, ensuring SyntaxError is thrown for duplicates with upper yet non-top lexical scopes [1], all while skipping CatchScopeWithSimpleParameter [2]. 3. Introduces emitPutToScopeDynamic(), which circumvents default ResolveType resolution that isn't correct wrt skipping CatchScopeWithSimpleParameter as resolveScopeForHoistingFuncDeclInEval() does. We can't possibly tweak BytecodeGenerator::resolveType() to account for eval(). This fixes both top-level and block-level function declarations to be hoisted correctly from eval() within simple parameter catch block by the same name. 4. Removes isExtensible() check from resolveScopeForHoistingFuncDeclInEval() because for declared variables, CanDeclareGlobalVar [3] is already implemented, while for Annex B hoisted functions, the implementation doesn't appear correct to unconditionally rely on isExtensible() even if the property is already present. Furthermore, performing CanDeclareGlobalVar in resolveScopeForHoistingFuncDeclInEval() is kinda superfluous given we put jsUndefined() variables in executeEval(), and results in incorrect error being thrown (SyntaxError instead of TypeError) if global object is non-extensible. [1]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation (step 3.d.i.2.a.i) [2]: https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks [3]: https://tc39.es/ecma262/#sec-candeclareglobalvar All JSTests changes were proven to align JSC with V8 and SpiderMonkey. * JSTests/ChakraCore/test/Closures/bug_OS_2299723.baseline-jsc: * JSTests/stress/const-not-strict-mode.js: * JSTests/stress/eval-func-decl-by-the-same-name-as-callee.js: Added. * JSTests/stress/eval-func-decl-in-eval-within-catch-scope.js: * JSTests/stress/eval-func-decl-in-global-of-eval.js: * JSTests/stress/eval-func-decl-within-eval-duplicate-declaration.js: Added. * JSTests/stress/eval-func-decl-within-eval-with-reassign-to-var.js: * JSTests/stress/eval-let-const-redeclararion.js: Added. * JSTests/stress/global-lexical-var-injection.js: * JSTests/stress/lexical-let-not-strict-mode.js: * JSTests/test262/expectations.yaml: Mark 160 tests as passing. * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::hoistSloppyModeFunctionIfNecessary): (JSC::BytecodeGenerator::emitPutToScopeDynamic): * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h: * Source/JavaScriptCore/interpreter/Interpreter.cpp: (JSC::Interpreter::executeEval): * Source/JavaScriptCore/parser/Parser.h: (JSC::Scope::getSloppyModeHoistedFunctions): * Source/JavaScriptCore/runtime/JSScope.cpp: (JSC::JSScope::resolveScopeForHoistingFuncDeclInEval): Canonical link: https://commits.webkit.org/266229@main
- Loading branch information
Alexey Shvayka
committed
Jul 22, 2023
1 parent
b567579
commit ab09173
Showing
16 changed files
with
490 additions
and
416 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
eval('var x = 5') threw 'Can't create duplicate variable in eval: 'x'' | ||
x: 5 | ||
eval('var y = 5') threw 'Attempted to assign to readonly property.' | ||
eval('var y = 5') threw 'Can't create duplicate variable in eval: 'y'' | ||
eval('y = 5') threw 'Attempted to assign to readonly property.' | ||
y: 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
JSTests/stress/eval-func-decl-by-the-same-name-as-callee.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
function shouldBe(actual, expected) { | ||
if (actual !== expected) | ||
throw new Error(`Bad value: ${actual}!`); | ||
} | ||
|
||
(function foo(a) { | ||
shouldBe(foo, undefined); | ||
|
||
if (true) { | ||
function foo(b) {} | ||
} | ||
|
||
shouldBe(foo.toString(), "function foo(b) {}"); | ||
})(); | ||
|
||
const bar__ = function bar(a) { | ||
shouldBe(bar, bar__); | ||
|
||
eval(`if (true) { function bar(b) {} }`); | ||
|
||
shouldBe(bar.toString(), "function bar(b) {}"); | ||
}; | ||
|
||
bar__(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
JSTests/stress/eval-func-decl-within-eval-duplicate-declaration.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
function shouldBe(actual, expected) { | ||
if (actual !== expected) | ||
throw new Error(`Bad value: ${actual}!`); | ||
} | ||
|
||
function shouldThrow(func, expectedError) { | ||
let errorThrown = false; | ||
try { | ||
func(); | ||
} catch (error) { | ||
errorThrown = true; | ||
if (error.toString() !== expectedError) | ||
throw new Error(`Bad error: ${error}!`); | ||
} | ||
if (!errorThrown) | ||
throw new Error("Didn't throw!"); | ||
} | ||
|
||
(() => { | ||
eval(` | ||
try { | ||
function foo(a) {} | ||
eval('try { function foo(b) {} } catch {} function foo(c) {}'); | ||
} catch {} | ||
`); | ||
|
||
shouldBe(foo.toString(), "function foo(a) {}"); | ||
})(); | ||
|
||
shouldThrow(() => { | ||
eval(` | ||
if (true) { | ||
function foo(a) {} | ||
eval('if (true) { function foo(b) {} } function foo(c) {}'); | ||
} | ||
`); | ||
}, "SyntaxError: Can't create duplicate variable in eval: 'foo'"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.