LibJS: Fall back to intrinsic generator prototypes
Generator and async generator instances should use OrdinaryCreateFromConstructor with %GeneratorPrototype% and %AsyncGeneratorPrototype%. When `.prototype` was nullish, we created a null-prototype object, and when it was a primitive, we boxed it with ToObject. Use GetPrototypeFromConstructor for the generating function and cover fallbacks for nullish and primitive prototype values.
This commit is contained in:
parent
77bf420654
commit
8fb6fa785a
4 changed files with 32 additions and 18 deletions
|
|
@ -5,6 +5,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/AsyncGenerator.h>
|
||||
#include <LibJS/Runtime/AsyncGeneratorPrototype.h>
|
||||
#include <LibJS/Runtime/AsyncGeneratorRequest.h>
|
||||
|
|
@ -20,14 +21,11 @@ GC_DEFINE_ALLOCATOR(AsyncGenerator);
|
|||
GC::Ref<AsyncGenerator> AsyncGenerator::create(Realm& realm, Variant<GC::Ref<ECMAScriptFunctionObject>, GC::Ref<NativeJavaScriptBackedFunction>> generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
auto generating_function_prototype = MUST(generating_function.visit([&vm](auto function) {
|
||||
static Bytecode::StaticPropertyLookupCache cache;
|
||||
return function->get(vm.names.prototype, cache);
|
||||
// 2. Let _generator_ be ? OrdinaryCreateFromConstructor(_functionObject_, *"%AsyncGeneratorPrototype%"*,
|
||||
// « [[AsyncGeneratorState]], [[AsyncGeneratorContext]], [[AsyncGeneratorQueue]], [[GeneratorBrand]] »).
|
||||
auto* generating_function_prototype_object = MUST(generating_function.visit([&vm](auto function) -> ThrowCompletionOr<Object*> {
|
||||
return get_prototype_from_constructor(vm, *function, &Intrinsics::async_generator_prototype);
|
||||
}));
|
||||
GC::Ptr<Object> generating_function_prototype_object = nullptr;
|
||||
if (!generating_function_prototype.is_nullish())
|
||||
generating_function_prototype_object = MUST(generating_function_prototype.to_object(vm));
|
||||
|
||||
auto generating_executable = generating_function.visit(
|
||||
[](GC::Ref<ECMAScriptFunctionObject> function) -> GC::Ref<Bytecode::Executable> {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/GeneratorObject.h>
|
||||
#include <LibJS/Runtime/GeneratorPrototype.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
|
|
@ -18,31 +19,26 @@ GC_DEFINE_ALLOCATOR(GeneratorObject);
|
|||
GC::Ref<GeneratorObject> GeneratorObject::create(Realm& realm, Variant<GC::Ref<ECMAScriptFunctionObject>, GC::Ref<NativeJavaScriptBackedFunction>> generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
Value generating_function_prototype;
|
||||
|
||||
auto kind = generating_function.visit(
|
||||
[](auto function) {
|
||||
return function->kind();
|
||||
});
|
||||
|
||||
GC::Ptr<Object> generating_function_prototype_object = nullptr;
|
||||
if (kind == FunctionKind::Async) {
|
||||
// We implement async functions by transforming them to generator function in the bytecode
|
||||
// interpreter. However an async function does not have a prototype and should not be
|
||||
// changed thus we hardcode the prototype.
|
||||
generating_function_prototype = realm.intrinsics().generator_prototype();
|
||||
generating_function_prototype_object = realm.intrinsics().generator_prototype();
|
||||
} else {
|
||||
static Bytecode::StaticPropertyLookupCache cache;
|
||||
generating_function_prototype = MUST(generating_function.visit([&vm](auto function) {
|
||||
static Bytecode::StaticPropertyLookupCache cache;
|
||||
return function->get(vm.names.prototype, cache);
|
||||
// 1. Let _generator_ be ? OrdinaryCreateFromConstructor(_functionObject_, *"%GeneratorPrototype%"*,
|
||||
// « [[GeneratorState]], [[GeneratorContext]], [[GeneratorBrand]] »).
|
||||
generating_function_prototype_object = MUST(generating_function.visit([&vm](auto function) -> ThrowCompletionOr<Object*> {
|
||||
return get_prototype_from_constructor(vm, *function, &Intrinsics::generator_prototype);
|
||||
}));
|
||||
}
|
||||
|
||||
GC::Ptr<Object> generating_function_prototype_object = nullptr;
|
||||
if (!generating_function_prototype.is_nullish())
|
||||
generating_function_prototype_object = MUST(generating_function_prototype.to_object(vm));
|
||||
|
||||
auto generating_executable = generating_function.visit(
|
||||
[](GC::Ref<ECMAScriptFunctionObject> function) -> GC::Ref<Bytecode::Executable> {
|
||||
return function->bytecode_executable().as_nonnull();
|
||||
|
|
|
|||
|
|
@ -56,3 +56,13 @@ describe("parsing classes with generator methods", () => {
|
|||
expect(`class Foo { async *constructor() { yield 42; } }`).not.toEval();
|
||||
});
|
||||
});
|
||||
|
||||
test("async generator instances use the intrinsic prototype when function prototype is not an object", () => {
|
||||
async function* generator() {}
|
||||
const AsyncGeneratorPrototype = Object.getPrototypeOf(generator.prototype);
|
||||
|
||||
for (const prototype of [undefined, null, false, "", Symbol(), 1]) {
|
||||
generator.prototype = prototype;
|
||||
expect(Object.getPrototypeOf(generator())).toBe(AsyncGeneratorPrototype);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -63,3 +63,13 @@ test("function expression names equal to 'yield'", () => {
|
|||
expect(`function *foo() { (function yield() {}); }`).toEval();
|
||||
expect(`function *foo() { function yield() {} }`).not.toEval();
|
||||
});
|
||||
|
||||
test("generator instances use the intrinsic prototype when function prototype is not an object", () => {
|
||||
function* generator() {}
|
||||
const GeneratorPrototype = Object.getPrototypeOf(generator).prototype;
|
||||
|
||||
for (const prototype of [undefined, null, false, "", Symbol(), 1]) {
|
||||
generator.prototype = prototype;
|
||||
expect(Object.getPrototypeOf(generator())).toBe(GeneratorPrototype);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue