Skip to content

Commit

Permalink
[JSC] Create (async) generator object after performing FunctionDeclar…
Browse files Browse the repository at this point in the history
…ationInstantiation

https://bugs.webkit.org/show_bug.cgi?id=263617
<rdar://problem/117439419>

Reviewed by Justin Michaud.

initializeDefaultParameterValuesAndSetupFunctionScopeStack(), which implements steps 25-26 of
FunctionDeclarationInstantiation [1], may call into arbitrary userland code when evaluating
default parameters, affecting the [[Prototype]] of created & returned (async) generator object.

With this change, (async) generator object creation happens after parameter initialization,
aligning JSC with the spec [1][2] and SpiderMonkey.

[1]: https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
[2]: https://tc39.es/ecma262/#sec-runtime-semantics-evaluategeneratorbody (step 2)
[3]: https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody (step 2)

* JSTests/stress/regress-263617.js: Added.
* JSTests/test262/expectations.yaml: Mark 8 tests as passing.
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):

Canonical link: https://commits.webkit.org/269823@main
  • Loading branch information
Alexey Shvayka committed Oct 26, 2023
1 parent 8ba72bd commit d9e5846
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 24 deletions.
15 changes: 15 additions & 0 deletions JSTests/stress/regress-263617.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function assert(x) {
if (!x)
throw new Error("Bad assertion!");
}

class Foo {
static * staticGen(arg = (Foo.staticGen.prototype = 1)) {}
async * asyncGen(arg = (Foo.prototype.asyncGen.prototype = true)) {}
}

const staticGenOriginalPrototype = Foo.staticGen.prototype;
const asyncGenOriginalPrototype = Foo.prototype.asyncGen.prototype;

assert(Foo.staticGen().__proto__ !== staticGenOriginalPrototype);
assert(Foo.prototype.asyncGen().__proto__ !== asyncGenOriginalPrototype);
12 changes: 0 additions & 12 deletions JSTests/test262/expectations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1154,9 +1154,6 @@ test/language/expressions/assignmenttargettype/direct-callexpression-arguments.j
default: 'Test262: This statement should not be evaluated.'
test/language/expressions/assignmenttargettype/parenthesized-callexpression-arguments.js:
default: 'Test262: This statement should not be evaluated.'
test/language/expressions/async-generator/generator-created-after-decl-inst.js:
default: 'Test262Error: Expected SameValue(«[object AsyncGenerator]», «[object AsyncGenerator]») to be false'
strict mode: 'Test262Error: Expected SameValue(«[object AsyncGenerator]», «[object AsyncGenerator]») to be false'
test/language/expressions/call/eval-realm-indirect.js:
default: 'Test262Error: Expected SameValue(«inside», «outside») to be true'
test/language/expressions/call/eval-spread-empty-leading.js:
Expand Down Expand Up @@ -1250,9 +1247,6 @@ test/language/expressions/dynamic-import/import-assertions/2nd-param-get-assert-
strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected an error, but observed no error'
test/language/expressions/function/scope-param-rest-elem-var-open.js:
default: 'Test262Error: Expected SameValue(«outside», «inside») to be true'
test/language/expressions/generators/generator-created-after-decl-inst.js:
default: 'Test262Error: Expected SameValue(«[object Generator]», «[object Generator]») to be false'
strict mode: 'Test262Error: Expected SameValue(«[object Generator]», «[object Generator]») to be false'
test/language/expressions/generators/scope-param-rest-elem-var-open.js:
default: 'Test262Error: Expected SameValue(«outside», «inside») to be true'
test/language/expressions/instanceof/prototype-getter-with-primitive.js:
Expand Down Expand Up @@ -1360,9 +1354,6 @@ test/language/module-code/import-assertions/eval-gtbndng-indirect-faux-assertion
raw: "SyntaxError: Unexpected token '*'. import call expects one or two arguments."
test/language/module-code/import-assertions/import-assertion-empty.js:
module: "SyntaxError: Unexpected identifier 'assert'. Expected a ';' following a targeted import declaration."
test/language/statements/async-generator/generator-created-after-decl-inst.js:
default: 'Test262Error: Expected SameValue(«[object AsyncGenerator]», «[object AsyncGenerator]») to be false'
strict mode: 'Test262Error: Expected SameValue(«[object AsyncGenerator]», «[object AsyncGenerator]») to be false'
test/language/statements/async-generator/return-undefined-implicit-and-explicit.js:
default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected [tick 1, g1 ret, tick 2, g2 ret, g3 ret, g4 ret] and [tick 1, g1 ret, g2 ret, tick 2, g3 ret, g4 ret] to have the same contents. Ticks for implicit and explicit return undefined'
strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected [tick 1, g1 ret, tick 2, g2 ret, g3 ret, g4 ret] and [tick 1, g1 ret, g2 ret, tick 2, g3 ret, g4 ret] to have the same contents. Ticks for implicit and explicit return undefined'
Expand Down Expand Up @@ -1518,9 +1509,6 @@ test/language/statements/for/head-lhs-let.js:
default: "SyntaxError: Unexpected token ';'. Expected a parameter pattern or a ')' in parameter list."
test/language/statements/function/scope-param-rest-elem-var-open.js:
default: 'Test262Error: Expected SameValue(«outside», «inside») to be true'
test/language/statements/generators/generator-created-after-decl-inst.js:
default: 'Test262Error: Expected SameValue(«[object Generator]», «[object Generator]») to be false'
strict mode: 'Test262Error: Expected SameValue(«[object Generator]», «[object Generator]») to be false'
test/language/statements/generators/scope-param-rest-elem-var-open.js:
default: 'Test262Error: Expected SameValue(«outside», «inside») to be true'
test/language/statements/let/dstr/ary-init-iter-get-err-array-prototype.js:
Expand Down
18 changes: 6 additions & 12 deletions Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,17 +726,7 @@ IGNORE_GCC_WARNINGS_END

switch (parseMode) {
case SourceParseMode::GeneratorWrapperFunctionMode:
case SourceParseMode::GeneratorWrapperMethodMode: {
m_generatorRegister = addVar();

// FIXME: Emit to_this only when Generator uses it.
// https://bugs.webkit.org/show_bug.cgi?id=151586
emitToThis();

emitCreateGenerator(m_generatorRegister, &m_calleeRegister);
break;
}

case SourceParseMode::GeneratorWrapperMethodMode:
case SourceParseMode::AsyncGeneratorWrapperMethodMode:
case SourceParseMode::AsyncGeneratorWrapperFunctionMode: {
m_generatorRegister = addVar();
Expand All @@ -745,7 +735,6 @@ IGNORE_GCC_WARNINGS_END
// https://bugs.webkit.org/show_bug.cgi?id=151586
emitToThis();

emitCreateAsyncGenerator(m_generatorRegister, &m_calleeRegister);
break;
}

Expand Down Expand Up @@ -878,6 +867,11 @@ IGNORE_GCC_WARNINGS_END
if (m_scopeNode->needsNewTargetRegisterForThisScope())
emitLoadNewTargetFromArrowFunctionLexicalEnvironment();
}

if (isGeneratorWrapperParseMode(parseMode))
emitCreateGenerator(m_generatorRegister, &m_calleeRegister);
else if (isAsyncGeneratorWrapperParseMode(parseMode))
emitCreateAsyncGenerator(m_generatorRegister, &m_calleeRegister);

// Set up the lexical environment scope as the generator frame. We store the saved and resumed generator registers into this scope with the symbol keys.
// Since they are symbol keyed, these variables cannot be reached from the usual code.
Expand Down

0 comments on commit d9e5846

Please sign in to comment.