diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 549a577e39..cda698bf70 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,14 @@ Value CallExpression::execute(Interpreter& interpreter) const if (interpreter.exception()) return {}; + if (is_new_expression()) { + if (!callee.is_object() + || !callee.as_object()->is_function() + || (callee.as_object()->is_native_function() + && !static_cast(callee.as_object())->has_constructor())) + return interpreter.throw_exception("TypeError", String::format("%s is not a constructor", callee.to_string().characters())); + } + if (!callee.is_object() || !callee.as_object()->is_function()) return interpreter.throw_exception("TypeError", String::format("%s is not a function", callee.to_string().characters())); @@ -103,17 +112,19 @@ Value CallExpression::execute(Interpreter& interpreter) const } Object* new_object = nullptr; + Value result; if (is_new_expression()) { new_object = interpreter.heap().allocate(); auto prototype = function->get("prototype"); if (prototype.has_value() && prototype.value().is_object()) new_object->set_prototype(prototype.value().as_object()); call_frame.this_value = new_object; + result = function->construct(interpreter); } else { call_frame.this_value = this_value; + result = function->call(interpreter); } - auto result = function->call(interpreter); interpreter.pop_call_frame(); if (is_new_expression()) { @@ -955,4 +966,5 @@ void SwitchCase::dump(int indent) const statement.dump(indent + 1); } } + } diff --git a/Libraries/LibJS/Runtime/DateConstructor.cpp b/Libraries/LibJS/Runtime/DateConstructor.cpp index 7e4f9ed41c..de21c22db5 100644 --- a/Libraries/LibJS/Runtime/DateConstructor.cpp +++ b/Libraries/LibJS/Runtime/DateConstructor.cpp @@ -46,6 +46,14 @@ DateConstructor::~DateConstructor() } Value DateConstructor::call(Interpreter& interpreter) +{ + auto* date = static_cast(construct(interpreter).as_object()); + if (!date) + return {}; + return js_string(interpreter.heap(), date->string()); +} + +Value DateConstructor::construct(Interpreter& interpreter) { // TODO: Support args struct timeval tv; diff --git a/Libraries/LibJS/Runtime/DateConstructor.h b/Libraries/LibJS/Runtime/DateConstructor.h index 82a3f2eb0b..707d9fe6fd 100644 --- a/Libraries/LibJS/Runtime/DateConstructor.h +++ b/Libraries/LibJS/Runtime/DateConstructor.h @@ -36,8 +36,10 @@ public: virtual ~DateConstructor() override; virtual Value call(Interpreter&) override; + virtual Value construct(Interpreter&) override; private: + virtual bool has_constructor() const override { return true; } virtual const char* class_name() const override { return "DateConstructor"; } static Value now(Interpreter&); diff --git a/Libraries/LibJS/Runtime/Function.h b/Libraries/LibJS/Runtime/Function.h index 942069a0cd..4cdcaf2467 100644 --- a/Libraries/LibJS/Runtime/Function.h +++ b/Libraries/LibJS/Runtime/Function.h @@ -36,6 +36,7 @@ public: virtual ~Function(); virtual Value call(Interpreter&) = 0; + virtual Value construct(Interpreter&) = 0; protected: Function(); diff --git a/Libraries/LibJS/Runtime/NativeFunction.cpp b/Libraries/LibJS/Runtime/NativeFunction.cpp index 9383409bda..f1363ca867 100644 --- a/Libraries/LibJS/Runtime/NativeFunction.cpp +++ b/Libraries/LibJS/Runtime/NativeFunction.cpp @@ -44,4 +44,9 @@ Value NativeFunction::call(Interpreter& interpreter) return m_native_function(interpreter); } +Value NativeFunction::construct(Interpreter&) +{ + return {}; +} + } diff --git a/Libraries/LibJS/Runtime/NativeFunction.h b/Libraries/LibJS/Runtime/NativeFunction.h index 85ecd63294..4b661e634d 100644 --- a/Libraries/LibJS/Runtime/NativeFunction.h +++ b/Libraries/LibJS/Runtime/NativeFunction.h @@ -37,6 +37,9 @@ public: virtual ~NativeFunction() override; virtual Value call(Interpreter&) override; + virtual Value construct(Interpreter&) override; + + virtual bool has_constructor() const { return false; } protected: NativeFunction() {} diff --git a/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Libraries/LibJS/Runtime/ObjectConstructor.cpp index 36296ea6e5..da6505a749 100644 --- a/Libraries/LibJS/Runtime/ObjectConstructor.cpp +++ b/Libraries/LibJS/Runtime/ObjectConstructor.cpp @@ -50,6 +50,11 @@ Value ObjectConstructor::call(Interpreter& interpreter) return interpreter.heap().allocate(); } +Value ObjectConstructor::construct(Interpreter& interpreter) +{ + return call(interpreter); +} + Value ObjectConstructor::get_own_property_names(Interpreter& interpreter) { if (interpreter.call_frame().arguments.size() < 1) diff --git a/Libraries/LibJS/Runtime/ObjectConstructor.h b/Libraries/LibJS/Runtime/ObjectConstructor.h index 21430772ab..06fd81ba01 100644 --- a/Libraries/LibJS/Runtime/ObjectConstructor.h +++ b/Libraries/LibJS/Runtime/ObjectConstructor.h @@ -36,8 +36,10 @@ public: virtual ~ObjectConstructor() override; virtual Value call(Interpreter&) override; + virtual Value construct(Interpreter&) override; private: + virtual bool has_constructor() const override { return true; } virtual const char* class_name() const override { return "ObjectConstructor"; } static Value get_own_property_names(Interpreter&); diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp index 07728ed7e0..83b8b542b9 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -55,4 +55,9 @@ Value ScriptFunction::call(Interpreter& interpreter) return interpreter.run(m_body, arguments, ScopeType::Function); } +Value ScriptFunction::construct(Interpreter& interpreter) +{ + return call(interpreter); +} + } diff --git a/Libraries/LibJS/Runtime/ScriptFunction.h b/Libraries/LibJS/Runtime/ScriptFunction.h index 76609e4e57..7691eedc42 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.h +++ b/Libraries/LibJS/Runtime/ScriptFunction.h @@ -39,6 +39,7 @@ public: const Vector& parameters() const { return m_parameters; }; virtual Value call(Interpreter&) override; + virtual Value construct(Interpreter&) override; private: virtual bool is_script_function() const final { return true; } diff --git a/Libraries/LibJS/Tests/function-TypeError.js b/Libraries/LibJS/Tests/function-TypeError.js index 90cae589a4..204fc7e495 100644 --- a/Libraries/LibJS/Tests/function-TypeError.js +++ b/Libraries/LibJS/Tests/function-TypeError.js @@ -25,6 +25,27 @@ try { assert(e.message === "undefined is not a function"); } + try { + Math(); + } catch(e) { + assert(e.name === "TypeError"); + assert(e.message === "[object MathObject] is not a function"); + } + + try { + new Math(); + } catch(e) { + assert(e.name === "TypeError"); + assert(e.message === "[object MathObject] is not a constructor"); + } + + try { + new isNaN(); + } catch(e) { + assert(e.name === "TypeError"); + assert(e.message === "[object NativeFunction] is not a constructor"); + } + console.log("PASS"); } catch(e) { console.log("FAIL: " + e);