From f07f8d5a44fe98671361a3842a41467c8ccba502 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 8 Apr 2020 11:05:38 +0200 Subject: [PATCH] LibJS: Add "constructor" property to constructor prototypes --- Libraries/LibJS/AST.cpp | 1 + Libraries/LibJS/Forward.h | 7 ++++ Libraries/LibJS/Interpreter.cpp | 10 ++++++ Libraries/LibJS/Interpreter.h | 4 +-- Libraries/LibJS/Runtime/GlobalObject.cpp | 36 +++++++++++++++---- Libraries/LibJS/Runtime/GlobalObject.h | 21 ++++++++++- Libraries/LibJS/Runtime/Object.cpp | 1 + .../Tests/Object.prototype.constructor.js | 28 +++++++++++++++ 8 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 Libraries/LibJS/Tests/Object.prototype.constructor.js diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index d11d6a6295b..8ff7de15129 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index accc71d813d..eafd2f09d22 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -29,17 +29,24 @@ namespace JS { class ASTNode; +class ArrayConstructor; +class BooleanConstructor; class Cell; +class DateConstructor; class Error; +class ErrorConstructor; class Exception; class Expression; class Function; +class FunctionConstructor; class GlobalObject; class HandleImpl; class Heap; class HeapBlock; class Interpreter; +class NumberConstructor; class Object; +class ObjectConstructor; class PrimitiveString; class ScopeNode; class Shape; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 910751cf7e5..dc50045f719 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -227,4 +227,14 @@ Value Interpreter::throw_exception(Exception* exception) return {}; } +GlobalObject& Interpreter::global_object() +{ + return static_cast(*m_global_object); +} + +const GlobalObject& Interpreter::global_object() const +{ + return static_cast(*m_global_object); +} + } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 4d799482793..3e541321842 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -83,8 +83,8 @@ public: Value run(const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block); - Object& global_object() { return *m_global_object; } - const Object& global_object() const { return *m_global_object; } + GlobalObject& global_object(); + const GlobalObject& global_object() const; Heap& heap() { return m_heap; } diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index d5dc5b37f74..1ca293e6ab9 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -43,6 +43,14 @@ namespace JS { +template +void GlobalObject::add_constructor(const FlyString& property_name, ConstructorType*& constructor, Object& prototype) +{ + constructor = heap().allocate(); + prototype.put("constructor", constructor); + put(property_name, constructor); +} + GlobalObject::GlobalObject() { put_native_function("gc", gc); @@ -54,20 +62,34 @@ GlobalObject::GlobalObject() put("undefined", js_undefined()); put("console", heap().allocate()); - put("Date", heap().allocate()); - put("Error", heap().allocate()); - put("Function", heap().allocate()); put("Math", heap().allocate()); - put("Object", heap().allocate()); - put("Array", heap().allocate()); - put("Boolean", heap().allocate()); - put("Number", heap().allocate()); + + add_constructor("Array", m_array_constructor, *interpreter().array_prototype()); + add_constructor("Boolean", m_boolean_constructor, *interpreter().boolean_prototype()); + add_constructor("Date", m_date_constructor, *interpreter().date_prototype()); + add_constructor("Error", m_error_constructor, *interpreter().error_prototype()); + add_constructor("Function", m_function_constructor, *interpreter().function_prototype()); + add_constructor("Number", m_number_constructor, *interpreter().number_prototype()); + add_constructor("Object", m_object_constructor, *interpreter().object_prototype()); } GlobalObject::~GlobalObject() { } +void GlobalObject::visit_children(Visitor& visitor) +{ + Object::visit_children(visitor); + + visitor.visit(m_array_constructor); + visitor.visit(m_boolean_constructor); + visitor.visit(m_date_constructor); + visitor.visit(m_error_constructor); + visitor.visit(m_function_constructor); + visitor.visit(m_number_constructor); + visitor.visit(m_object_constructor); +} + Value GlobalObject::gc(Interpreter& interpreter) { dbg() << "Forced garbage collection requested!"; diff --git a/Libraries/LibJS/Runtime/GlobalObject.h b/Libraries/LibJS/Runtime/GlobalObject.h index eb33d8baa81..38e283361a8 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.h +++ b/Libraries/LibJS/Runtime/GlobalObject.h @@ -24,7 +24,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #pragma once #include @@ -36,11 +35,31 @@ public: explicit GlobalObject(); virtual ~GlobalObject() override; + ArrayConstructor* array_constructor() { return m_array_constructor; } + BooleanConstructor* boolean_constructor() { return m_boolean_constructor; } + DateConstructor* date_constructor() { return m_date_constructor; } + ErrorConstructor* error_constructor() { return m_error_constructor; } + FunctionConstructor* function_constructor() { return m_function_constructor; } + NumberConstructor* number_constructor() { return m_number_constructor; }; + ObjectConstructor* object_constructor() { return m_object_constructor; } + private: virtual const char* class_name() const override { return "GlobalObject"; } + virtual void visit_children(Visitor&) override; static Value gc(Interpreter&); static Value is_nan(Interpreter&); + + template + void add_constructor(const FlyString& property_name, ConstructorType*&, Object& prototype); + + ArrayConstructor* m_array_constructor { nullptr }; + BooleanConstructor* m_boolean_constructor { nullptr }; + DateConstructor* m_date_constructor { nullptr }; + ErrorConstructor* m_error_constructor { nullptr }; + FunctionConstructor* m_function_constructor { nullptr }; + NumberConstructor* m_number_constructor { nullptr }; + ObjectConstructor* m_object_constructor { nullptr }; }; } diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index da570ee7858..774ba51af48 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/Libraries/LibJS/Tests/Object.prototype.constructor.js b/Libraries/LibJS/Tests/Object.prototype.constructor.js new file mode 100644 index 00000000000..9f9218fd6db --- /dev/null +++ b/Libraries/LibJS/Tests/Object.prototype.constructor.js @@ -0,0 +1,28 @@ +try { + assert(Array.prototype.constructor === Array) + assert(Boolean.prototype.constructor === Boolean) + assert(Date.prototype.constructor === Date) + assert(Error.prototype.constructor === Error) + assert(Function.prototype.constructor === Function) + assert(Number.prototype.constructor === Number) + assert(Object.prototype.constructor === Object) + + o = {} + assert(o.constructor === Object) + + o = new Object + assert(o.constructor === Object) + + a = [] + assert(a.constructor === Array) + + a = new Array + assert(a.constructor === Array) + + n = new Number(3) + assert(n.constructor === Number) + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}