1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:17:44 +00:00

LibJS: Add the remaining generator objects

- %GeneratorFunction%
- %GeneratorFunction.prototype%
- %GeneratorFunction.prototype.prototype%
- %Generator%.prototype
This commit is contained in:
Matthew Olsson 2021-06-15 00:04:08 -07:00 committed by Linus Groh
parent b205c9814a
commit 22b17219ff
14 changed files with 302 additions and 67 deletions

View file

@ -59,7 +59,10 @@ set(SOURCES
Runtime/FunctionConstructor.cpp
Runtime/Function.cpp
Runtime/FunctionPrototype.cpp
Runtime/GeneratorFunctionConstructor.cpp
Runtime/GeneratorFunctionPrototype.cpp
Runtime/GeneratorObject.cpp
Runtime/GeneratorObjectPrototype.cpp
Runtime/GlobalObject.cpp
Runtime/IndexedProperties.cpp
Runtime/IteratorOperations.cpp

View file

@ -36,6 +36,7 @@
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
__JS_ENUMERATE(FinalizationRegistry, finalization_registry, FinalizationRegistryPrototype, FinalizationRegistryConstructor, void) \
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
__JS_ENUMERATE(GeneratorFunction, generator_function, GeneratorFunctionPrototype, GeneratorFunctionConstructor, void) \
__JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
@ -159,6 +160,9 @@ struct PromiseCapability;
class ProxyObject;
class ProxyConstructor;
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
class GeneratorObjectPrototype;
class TypedArrayConstructor;
class TypedArrayPrototype;

View file

@ -31,11 +31,13 @@ FunctionConstructor::~FunctionConstructor()
{
}
// 20.2.1.1 Function ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
Value FunctionConstructor::call()
{
return construct(*this);
}
// 20.2.1.1 Function ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
Value FunctionConstructor::construct(Function&)
{
auto& vm = this->vm();

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/GeneratorFunctionConstructor.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
GeneratorFunctionConstructor::GeneratorFunctionConstructor(GlobalObject& global_object)
: NativeFunction(*static_cast<Object*>(global_object.function_constructor()))
{
}
void GeneratorFunctionConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
// 27.3.2.1 GeneratorFunction.length, https://tc39.es/ecma262/#sec-generatorfunction.length
define_property(vm.names.length, Value(1), Attribute::Configurable);
// 27.3.2.2 GeneratorFunction.prototype, https://tc39.es/ecma262/#sec-generatorfunction.length
define_property(vm.names.prototype, global_object.generator_function_prototype(), 0);
}
GeneratorFunctionConstructor::~GeneratorFunctionConstructor()
{
}
// 27.3.1.1 GeneratorFunction ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-generatorfunction
Value GeneratorFunctionConstructor::call()
{
return construct(*this);
}
// 27.3.1.1 GeneratorFunction ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-generatorfunction
Value GeneratorFunctionConstructor::construct(Function&)
{
TODO();
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS {
// 27.3.1 %GeneratorFunction%, https://tc39.es/ecma262/#sec-generatorfunction-constructor
class GeneratorFunctionConstructor final : public NativeFunction {
JS_OBJECT(GeneratorFunctionConstructor, NativeFunction);
public:
explicit GeneratorFunctionConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~GeneratorFunctionConstructor() override;
virtual Value call() override;
virtual Value construct(Function& new_target) override;
};
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GeneratorFunctionConstructor.h>
#include <LibJS/Runtime/GeneratorFunctionPrototype.h>
#include <LibJS/Runtime/GeneratorObjectPrototype.h>
namespace JS {
GeneratorFunctionPrototype::GeneratorFunctionPrototype(GlobalObject& global_object)
: Object(*global_object.function_prototype())
{
}
void GeneratorFunctionPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
// 27.3.3.2 %GeneratorFunction.prototype% prototype, https://tc39.es/ecma262/#sec-generatorfunction.prototype.prototype
define_property(vm.names.prototype, global_object.generator_object_prototype(), Attribute::Configurable);
// 27.3.3.3 %GeneratorFunction.prototype% [ @@toStringTag ], https://tc39.es/ecma262/#sec-generatorfunction.prototype-@@tostringtag
define_property(vm.well_known_symbol_to_string_tag(), js_string(vm, "GeneratorFunction"), Attribute::Configurable);
}
GeneratorFunctionPrototype::~GeneratorFunctionPrototype()
{
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
// 27.3.3 %GeneratorFunction.prototype%, https://tc39.es/ecma262/#sec-properties-of-the-generatorfunction-prototype-object
class GeneratorFunctionPrototype final : public Object {
JS_OBJECT(GeneratorFunctionPrototype, Object);
public:
explicit GeneratorFunctionPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~GeneratorFunctionPrototype() override;
};
}

View file

@ -8,13 +8,19 @@
#include <LibJS/Bytecode/Generator.h>
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Runtime/GeneratorObject.h>
#include <LibJS/Runtime/GeneratorObjectPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
GeneratorObject* GeneratorObject::create(GlobalObject& global_object, Value initial_value, ScriptFunction* generating_function, ScopeObject* generating_scope, Bytecode::RegisterWindow frame)
{
auto object = global_object.heap().allocate<GeneratorObject>(global_object, global_object);
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
auto generating_function_proto_property = generating_function->get(global_object.vm().names.prototype).to_object(global_object);
if (!generating_function_proto_property)
return {};
auto object = global_object.heap().allocate<GeneratorObject>(global_object, global_object, *generating_function_proto_property);
object->m_generating_function = generating_function;
object->m_scope = generating_scope;
object->m_frame = move(frame);
@ -22,21 +28,13 @@ GeneratorObject* GeneratorObject::create(GlobalObject& global_object, Value init
return object;
}
GeneratorObject::GeneratorObject(GlobalObject& global_object)
: Object(*global_object.object_prototype())
GeneratorObject::GeneratorObject(GlobalObject&, Object& prototype)
: Object(prototype)
{
}
void GeneratorObject::initialize(GlobalObject& global_object)
void GeneratorObject::initialize(GlobalObject&)
{
// FIXME: These should be on a separate Generator prototype object!
// https://tc39.es/ecma262/#sec-generator-objects
auto& vm = this->vm();
Object::initialize(global_object);
define_native_function(vm.names.next, next);
define_native_function(vm.names.return_, return_);
define_native_function(vm.names.throw_, throw_);
}
GeneratorObject::~GeneratorObject()
@ -52,18 +50,6 @@ void GeneratorObject::visit_edges(Cell::Visitor& visitor)
visitor.visit(&m_previous_value.as_object());
}
GeneratorObject* GeneratorObject::typed_this(VM& vm, GlobalObject& global_object)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!is<GeneratorObject>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Generator");
return nullptr;
}
return static_cast<GeneratorObject*>(this_object);
}
Value GeneratorObject::next_impl(VM& vm, GlobalObject& global_object, Optional<Value> value_to_throw)
{
auto bytecode_interpreter = Bytecode::Interpreter::current();
@ -138,29 +124,4 @@ Value GeneratorObject::next_impl(VM& vm, GlobalObject& global_object, Optional<V
return result;
}
JS_DEFINE_NATIVE_FUNCTION(GeneratorObject::next)
{
auto object = typed_this(vm, global_object);
if (!object)
return {};
return object->next_impl(vm, global_object, {});
}
JS_DEFINE_NATIVE_FUNCTION(GeneratorObject::return_)
{
auto object = typed_this(vm, global_object);
if (!object)
return {};
object->m_done = true;
return object->next_impl(vm, global_object, {});
}
JS_DEFINE_NATIVE_FUNCTION(GeneratorObject::throw_)
{
auto object = typed_this(vm, global_object);
if (!object)
return {};
return object->next_impl(vm, global_object, vm.argument(0));
}
}

View file

@ -16,20 +16,15 @@ class GeneratorObject final : public Object {
public:
static GeneratorObject* create(GlobalObject&, Value, ScriptFunction*, ScopeObject*, Bytecode::RegisterWindow);
explicit GeneratorObject(GlobalObject&);
GeneratorObject(GlobalObject&, Object& prototype);
virtual void initialize(GlobalObject&) override;
virtual ~GeneratorObject() override;
void visit_edges(Cell::Visitor&) override;
static GeneratorObject* typed_this(VM&, GlobalObject&);
Value next_impl(VM&, GlobalObject&, Optional<Value> value_to_throw);
void set_done() { m_done = true; }
private:
JS_DECLARE_NATIVE_FUNCTION(next);
JS_DECLARE_NATIVE_FUNCTION(return_);
JS_DECLARE_NATIVE_FUNCTION(throw_);
Value next_impl(VM&, GlobalObject&, Optional<Value> value_to_throw);
ScopeObject* m_scope { nullptr };
ScriptFunction* m_generating_function { nullptr };
Value m_previous_value;

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Runtime/GeneratorObject.h>
#include <LibJS/Runtime/GeneratorObjectPrototype.h>
namespace JS {
static GeneratorObject* typed_this(VM& vm, GlobalObject& global_object)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!is<GeneratorObject>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Generator");
return nullptr;
}
return static_cast<GeneratorObject*>(this_object);
}
GeneratorObjectPrototype::GeneratorObjectPrototype(GlobalObject& global_object)
: Object(*global_object.iterator_prototype())
{
}
void GeneratorObjectPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.next, next, 1, attr);
define_native_function(vm.names.return_, return_, 1, attr);
define_native_function(vm.names.throw_, throw_, 1, attr);
// 27.5.1.5 Generator.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-generator.prototype-@@tostringtag
define_property(vm.well_known_symbol_to_string_tag(), js_string(vm, "Generator"), Attribute::Configurable);
}
GeneratorObjectPrototype::~GeneratorObjectPrototype()
{
}
// 27.5.1.2 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.next
JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::next)
{
auto generator_object = typed_this(vm, global_object);
if (!generator_object)
return {};
return generator_object->next_impl(vm, global_object, {});
}
// 27.5.1.3 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.return
JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::return_)
{
auto generator_object = typed_this(vm, global_object);
if (!generator_object)
return {};
generator_object->set_done();
return generator_object->next_impl(vm, global_object, {});
}
// 27.5.1.4 Generator.prototype.next ( value ), https://tc39.es/ecma262/#sec-generator.prototype.throw
JS_DEFINE_NATIVE_FUNCTION(GeneratorObjectPrototype::throw_)
{
auto generator_object = typed_this(vm, global_object);
if (!generator_object)
return {};
return generator_object->next_impl(vm, global_object, vm.argument(0));
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/GlobalObject.h>
namespace JS {
// 27.5.1 %GeneratorFunction.prototype.prototype%, https://tc39.es/ecma262/#sec-properties-of-generator-prototype
class GeneratorObjectPrototype final : public Object {
JS_OBJECT(GeneratorObjectPrototype, Object);
public:
explicit GeneratorObjectPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~GeneratorObjectPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(next);
JS_DECLARE_NATIVE_FUNCTION(return_);
JS_DECLARE_NATIVE_FUNCTION(throw_);
};
}

View file

@ -37,6 +37,9 @@
#include <LibJS/Runtime/FinalizationRegistryPrototype.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/FunctionPrototype.h>
#include <LibJS/Runtime/GeneratorFunctionConstructor.h>
#include <LibJS/Runtime/GeneratorFunctionPrototype.h>
#include <LibJS/Runtime/GeneratorObjectPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorPrototype.h>
#include <LibJS/Runtime/JSONObject.h>
@ -110,18 +113,23 @@ void GlobalObject::initialize_global_object()
// This must be initialized before allocating AggregateErrorPrototype, which uses ErrorPrototype as its prototype.
m_error_prototype = heap().allocate<ErrorPrototype>(*this, *this);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (!m_##snake_name##_prototype) \
m_##snake_name##_prototype = heap().allocate<PrototypeName>(*this, *this);
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
#define __JS_ENUMERATE(ClassName, snake_name) \
if (!m_##snake_name##_prototype) \
m_##snake_name##_prototype = heap().allocate<ClassName##Prototype>(*this, *this);
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
// %GeneratorFunction.prototype.prototype% must be initialized separately as it has no
// companion constructor
m_generator_object_prototype = heap().allocate<GeneratorObjectPrototype>(*this, *this);
m_generator_object_prototype->define_property(vm.names.constructor, m_generator_function_constructor, Attribute::Configurable);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (!m_##snake_name##_prototype) \
m_##snake_name##_prototype = heap().allocate<PrototypeName>(*this, *this);
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.gc, gc, 0, attr);
define_native_function(vm.names.isNaN, is_nan, 1, attr);
@ -179,6 +187,11 @@ void GlobalObject::initialize_global_object()
JS_ENUMERATE_NATIVE_ERRORS
JS_ENUMERATE_TYPED_ARRAYS
#undef __JS_ENUMERATE
// The generator constructor cannot be initialized with add_constructor as it has no global binding
m_generator_function_constructor = heap().allocate<GeneratorFunctionConstructor>(*this, *this);
// 27.3.3.1 GeneratorFunction.prototype.constructor, https://tc39.es/ecma262/#sec-generatorfunction.prototype.constructor
m_generator_function_prototype->define_property(vm.names.constructor, m_generator_function_constructor, Attribute::Configurable);
}
GlobalObject::~GlobalObject()
@ -193,6 +206,7 @@ void GlobalObject::visit_edges(Visitor& visitor)
visitor.visit(m_new_object_shape);
visitor.visit(m_new_script_function_prototype_object_shape);
visitor.visit(m_proxy_constructor);
visitor.visit(m_generator_object_prototype);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
visitor.visit(m_##snake_name##_constructor); \

View file

@ -37,6 +37,9 @@ public:
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
ProxyConstructor* proxy_constructor() { return m_proxy_constructor; }
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
GeneratorObjectPrototype* generator_object_prototype() { return m_generator_object_prototype; }
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName* snake_name##_constructor() { return m_##snake_name##_constructor; } \
Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
@ -81,6 +84,9 @@ private:
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
ProxyConstructor* m_proxy_constructor { nullptr };
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
GeneratorObjectPrototype* m_generator_object_prototype { nullptr };
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName* m_##snake_name##_constructor { nullptr }; \
Object* m_##snake_name##_prototype { nullptr };

View file

@ -15,6 +15,7 @@
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GeneratorObject.h>
#include <LibJS/Runtime/GeneratorObjectPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/ScriptFunction.h>
@ -36,7 +37,16 @@ static ScriptFunction* typed_this(VM& vm, GlobalObject& global_object)
ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, FunctionKind kind, bool is_strict, bool is_arrow_function)
{
return global_object.heap().allocate<ScriptFunction>(global_object, global_object, name, body, move(parameters), m_function_length, parent_scope, *global_object.function_prototype(), kind, is_strict, is_arrow_function);
Object* prototype = nullptr;
switch (kind) {
case FunctionKind::Regular:
prototype = global_object.function_prototype();
break;
case FunctionKind::Generator:
prototype = global_object.generator_function_prototype();
break;
}
return global_object.heap().allocate<ScriptFunction>(global_object, global_object, name, body, move(parameters), m_function_length, parent_scope, *prototype, kind, is_strict, is_arrow_function);
}
ScriptFunction::ScriptFunction(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, Object& prototype, FunctionKind kind, bool is_strict, bool is_arrow_function)
@ -57,8 +67,16 @@ void ScriptFunction::initialize(GlobalObject& global_object)
auto& vm = this->vm();
Function::initialize(global_object);
if (!m_is_arrow_function) {
Object* prototype = vm.heap().allocate<Object>(global_object, *global_object.new_script_function_prototype_object_shape());
prototype->define_property(vm.names.constructor, this, Attribute::Writable | Attribute::Configurable);
auto* prototype = vm.heap().allocate<Object>(global_object, *global_object.new_script_function_prototype_object_shape());
switch (m_kind) {
case FunctionKind::Regular:
prototype->define_property(vm.names.constructor, this, Attribute::Writable | Attribute::Configurable);
break;
case FunctionKind::Generator:
// prototype is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
prototype->set_prototype(global_object.generator_object_prototype());
break;
}
define_property(vm.names.prototype, prototype, Attribute::Writable);
}
define_native_property(vm.names.length, length_getter, {}, Attribute::Configurable);
@ -205,7 +223,7 @@ Value ScriptFunction::call()
Value ScriptFunction::construct(Function&)
{
if (m_is_arrow_function) {
if (m_is_arrow_function || m_kind == FunctionKind::Generator) {
vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, m_name);
return {};
}