diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index dce5c0092c..2f51372831 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -27,10 +27,9 @@ #include #include #include -#include #include -#include #include +#include #include #include @@ -43,7 +42,7 @@ Value ScopeNode::execute(Interpreter& interpreter) const Value FunctionDeclaration::execute(Interpreter& interpreter) const { - auto* function = interpreter.heap().allocate(name(), body(), parameters()); + auto* function = interpreter.heap().allocate(body(), parameters()); interpreter.set_variable(m_name, function); return function; } @@ -57,26 +56,14 @@ Value CallExpression::execute(Interpreter& interpreter) const { auto callee = interpreter.get_variable(name()); ASSERT(callee.is_object()); - auto* callee_object = callee.as_object(); + ASSERT(callee.as_object()->is_function()); + auto* function = static_cast(callee.as_object()); - Vector passed_arguments; - for (size_t i = 0; i < m_arguments.size(); ++i) { - String name; - if (callee_object->is_function()) - name = static_cast(*callee_object).parameters()[i]; - auto value = m_arguments[i].execute(interpreter); - dbg() << name << ": " << value; - passed_arguments.append({ move(name), move(value) }); - } + Vector argument_values; + for (size_t i = 0; i < m_arguments.size(); ++i) + argument_values.append(m_arguments[i].execute(interpreter)); - if (callee_object->is_function()) - return interpreter.run(static_cast(*callee_object).body(), move(passed_arguments), ScopeType::Function); - - if (callee_object->is_native_function()) { - return static_cast(*callee_object).native_function()(interpreter, move(passed_arguments)); - } - - ASSERT_NOT_REACHED(); + return function->call(interpreter, move(argument_values)); } Value ReturnStatement::execute(Interpreter& interpreter) const diff --git a/Libraries/LibJS/Function.cpp b/Libraries/LibJS/Function.cpp index 665e0d4218..d10dbedb43 100644 --- a/Libraries/LibJS/Function.cpp +++ b/Libraries/LibJS/Function.cpp @@ -29,10 +29,7 @@ namespace JS { -Function::Function(String name, const ScopeNode& body, Vector parameters) - : m_name(move(name)) - , m_body(body) - , m_parameters(move(parameters)) +Function::Function() { } diff --git a/Libraries/LibJS/Function.h b/Libraries/LibJS/Function.h index 0a1cdfa55c..f7f5426bdc 100644 --- a/Libraries/LibJS/Function.h +++ b/Libraries/LibJS/Function.h @@ -33,22 +33,16 @@ namespace JS { class Function : public Object { public: - Function(String name, const ScopeNode& body, Vector parameters = {}); virtual ~Function(); - const String& name() const { return m_name; } - const ScopeNode& body() const { return m_body; } - const Vector& parameters() const { return m_parameters; }; + virtual Value call(Interpreter&, Vector) = 0; protected: + Function(); virtual const char* class_name() const override { return "Function"; } private: virtual bool is_function() const final { return true; } - - String m_name; - const ScopeNode& m_body; - const Vector m_parameters; }; } diff --git a/Libraries/LibJS/GlobalObject.cpp b/Libraries/LibJS/GlobalObject.cpp index 4f18c81190..788d6ccd71 100644 --- a/Libraries/LibJS/GlobalObject.cpp +++ b/Libraries/LibJS/GlobalObject.cpp @@ -11,12 +11,12 @@ namespace JS { GlobalObject::GlobalObject(Heap& heap) { - put("print", heap.allocate([](Interpreter&, Vector arguments) -> Value { + put("print", heap.allocate([](Interpreter&, Vector arguments) -> Value { for (auto& argument : arguments) - printf("%s ", argument.value.to_string().characters()); + printf("%s ", argument.to_string().characters()); return js_undefined(); })); - put("gc", heap.allocate([](Interpreter& interpreter, Vector) -> Value { + put("gc", heap.allocate([](Interpreter& interpreter, Vector) -> Value { dbg() << "Forced garbage collection requested!"; interpreter.heap().collect_garbage(); return js_undefined(); diff --git a/Libraries/LibJS/Makefile b/Libraries/LibJS/Makefile index b553889594..4bdd216e00 100644 --- a/Libraries/LibJS/Makefile +++ b/Libraries/LibJS/Makefile @@ -11,6 +11,7 @@ OBJS = \ Object.o \ Parser.o \ PrimitiveString.o \ + ScriptFunction.o \ StringObject.o \ Token.o \ Value.o diff --git a/Libraries/LibJS/NativeFunction.cpp b/Libraries/LibJS/NativeFunction.cpp index dd62039a0e..098b7f9b81 100644 --- a/Libraries/LibJS/NativeFunction.cpp +++ b/Libraries/LibJS/NativeFunction.cpp @@ -30,7 +30,7 @@ namespace JS { -NativeFunction::NativeFunction(AK::Function)> native_function) +NativeFunction::NativeFunction(AK::Function)> native_function) : m_native_function(move(native_function)) { } @@ -39,4 +39,9 @@ NativeFunction::~NativeFunction() { } +Value NativeFunction::call(Interpreter& interpreter, Vector arguments) +{ + return m_native_function(interpreter, move(arguments)); +} + } diff --git a/Libraries/LibJS/NativeFunction.h b/Libraries/LibJS/NativeFunction.h index 1b3b3be81e..609a9572d4 100644 --- a/Libraries/LibJS/NativeFunction.h +++ b/Libraries/LibJS/NativeFunction.h @@ -27,22 +27,22 @@ #pragma once #include -#include +#include namespace JS { -class NativeFunction final : public Object { +class NativeFunction final : public Function { public: - explicit NativeFunction(AK::Function)>); + explicit NativeFunction(AK::Function)>); virtual ~NativeFunction() override; - AK::Function)>& native_function() { return m_native_function; } + virtual Value call(Interpreter&, Vector) override; private: virtual bool is_native_function() const override { return true; } virtual const char* class_name() const override { return "NativeFunction"; } - AK::Function)> m_native_function; + AK::Function)> m_native_function; }; } diff --git a/Libraries/LibJS/ScriptFunction.cpp b/Libraries/LibJS/ScriptFunction.cpp new file mode 100644 index 0000000000..05e56f2b52 --- /dev/null +++ b/Libraries/LibJS/ScriptFunction.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020, Stephan Unverwerth + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace JS { + +ScriptFunction::ScriptFunction(const ScopeNode& body, Vector parameters) + : m_body(body) + , m_parameters(move(parameters)) +{ +} + +ScriptFunction::~ScriptFunction() +{ +} + +Value ScriptFunction::call(Interpreter& interpreter, Vector argument_values) +{ + Vector arguments; + for (size_t i = 0; i < m_parameters.size(); ++i) { + auto name = parameters()[i]; + auto value = js_undefined(); + if (i < argument_values.size()) + value = argument_values[i]; + arguments.append({ move(name), move(value) }); + } + return interpreter.run(m_body, move(arguments), ScopeType::Function); +} + +} diff --git a/Libraries/LibJS/ScriptFunction.h b/Libraries/LibJS/ScriptFunction.h new file mode 100644 index 0000000000..910ae8222f --- /dev/null +++ b/Libraries/LibJS/ScriptFunction.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace JS { + +class ScriptFunction final : public Function { +public: + ScriptFunction(const ScopeNode& body, Vector parameters = {}); + virtual ~ScriptFunction(); + + const ScopeNode& body() const { return m_body; } + const Vector& parameters() const { return m_parameters; }; + + virtual Value call(Interpreter&, Vector) override; + +private: + virtual bool is_script_function() const final { return true; } + virtual const char* class_name() const override { return "ScriptFunction"; } + + const ScopeNode& m_body; + const Vector m_parameters; +}; + +}