Przeglądaj źródła

LibJS: Split Function into subclasses NativeFunction and ScriptFunction

Both types of functions are now Function and implement calling via:

    virtual Value call(Interpreter&, Vector<Value> arguments);

This removes the need for CallExpression::execute() to care about which
kind of function it's calling. :^)
Andreas Kling 5 lat temu
rodzic
commit
d9c7009604

+ 8 - 21
Libraries/LibJS/AST.cpp

@@ -27,10 +27,9 @@
 #include <AK/HashMap.h>
 #include <AK/StringBuilder.h>
 #include <LibJS/AST.h>
-#include <LibJS/Function.h>
 #include <LibJS/Interpreter.h>
-#include <LibJS/NativeFunction.h>
 #include <LibJS/PrimitiveString.h>
+#include <LibJS/ScriptFunction.h>
 #include <LibJS/Value.h>
 #include <stdio.h>
 
@@ -43,7 +42,7 @@ Value ScopeNode::execute(Interpreter& interpreter) const
 
 Value FunctionDeclaration::execute(Interpreter& interpreter) const
 {
-    auto* function = interpreter.heap().allocate<Function>(name(), body(), parameters());
+    auto* function = interpreter.heap().allocate<ScriptFunction>(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();
-
-    Vector<Argument> passed_arguments;
-    for (size_t i = 0; i < m_arguments.size(); ++i) {
-        String name;
-        if (callee_object->is_function())
-            name = static_cast<Function&>(*callee_object).parameters()[i];
-        auto value = m_arguments[i].execute(interpreter);
-        dbg() << name << ": " << value;
-        passed_arguments.append({ move(name), move(value) });
-    }
-
-    if (callee_object->is_function())
-        return interpreter.run(static_cast<Function&>(*callee_object).body(), move(passed_arguments), ScopeType::Function);
+    ASSERT(callee.as_object()->is_function());
+    auto* function = static_cast<Function*>(callee.as_object());
 
-    if (callee_object->is_native_function()) {
-        return static_cast<NativeFunction&>(*callee_object).native_function()(interpreter, move(passed_arguments));
-    }
+    Vector<Value> argument_values;
+    for (size_t i = 0; i < m_arguments.size(); ++i)
+        argument_values.append(m_arguments[i].execute(interpreter));
 
-    ASSERT_NOT_REACHED();
+    return function->call(interpreter, move(argument_values));
 }
 
 Value ReturnStatement::execute(Interpreter& interpreter) const

+ 1 - 4
Libraries/LibJS/Function.cpp

@@ -29,10 +29,7 @@
 
 namespace JS {
 
-Function::Function(String name, const ScopeNode& body, Vector<String> parameters)
-    : m_name(move(name))
-    , m_body(body)
-    , m_parameters(move(parameters))
+Function::Function()
 {
 }
 

+ 2 - 8
Libraries/LibJS/Function.h

@@ -33,22 +33,16 @@ namespace JS {
 
 class Function : public Object {
 public:
-    Function(String name, const ScopeNode& body, Vector<String> parameters = {});
     virtual ~Function();
 
-    const String& name() const { return m_name; }
-    const ScopeNode& body() const { return m_body; }
-    const Vector<String>& parameters() const { return m_parameters; };
+    virtual Value call(Interpreter&, Vector<Value>) = 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<String> m_parameters;
 };
 
 }

+ 3 - 3
Libraries/LibJS/GlobalObject.cpp

@@ -11,12 +11,12 @@ namespace JS {
 
 GlobalObject::GlobalObject(Heap& heap)
 {
-    put("print", heap.allocate<NativeFunction>([](Interpreter&, Vector<Argument> arguments) -> Value {
+    put("print", heap.allocate<NativeFunction>([](Interpreter&, Vector<Value> 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<NativeFunction>([](Interpreter& interpreter, Vector<Argument>) -> Value {
+    put("gc", heap.allocate<NativeFunction>([](Interpreter& interpreter, Vector<Value>) -> Value {
         dbg() << "Forced garbage collection requested!";
         interpreter.heap().collect_garbage();
         return js_undefined();

+ 1 - 0
Libraries/LibJS/Makefile

@@ -11,6 +11,7 @@ OBJS = \
     Object.o \
     Parser.o \
     PrimitiveString.o \
+    ScriptFunction.o \
     StringObject.o \
     Token.o \
     Value.o

+ 6 - 1
Libraries/LibJS/NativeFunction.cpp

@@ -30,7 +30,7 @@
 
 namespace JS {
 
-NativeFunction::NativeFunction(AK::Function<Value(Interpreter&, Vector<Argument>)> native_function)
+NativeFunction::NativeFunction(AK::Function<Value(Interpreter&, Vector<Value>)> native_function)
     : m_native_function(move(native_function))
 {
 }
@@ -39,4 +39,9 @@ NativeFunction::~NativeFunction()
 {
 }
 
+Value NativeFunction::call(Interpreter& interpreter, Vector<Value> arguments)
+{
+    return m_native_function(interpreter, move(arguments));
+}
+
 }

+ 5 - 5
Libraries/LibJS/NativeFunction.h

@@ -27,22 +27,22 @@
 #pragma once
 
 #include <AK/Function.h>
-#include <LibJS/Object.h>
+#include <LibJS/Function.h>
 
 namespace JS {
 
-class NativeFunction final : public Object {
+class NativeFunction final : public Function {
 public:
-    explicit NativeFunction(AK::Function<Value(Interpreter&, Vector<Argument>)>);
+    explicit NativeFunction(AK::Function<Value(Interpreter&, Vector<Value>)>);
     virtual ~NativeFunction() override;
 
-    AK::Function<Value(Interpreter&, Vector<Argument>)>& native_function() { return m_native_function; }
+    virtual Value call(Interpreter&, Vector<Value>) override;
 
 private:
     virtual bool is_native_function() const override { return true; }
     virtual const char* class_name() const override { return "NativeFunction"; }
 
-    AK::Function<Value(Interpreter&, Vector<Argument>)> m_native_function;
+    AK::Function<Value(Interpreter&, Vector<Value>)> m_native_function;
 };
 
 }

+ 56 - 0
Libraries/LibJS/ScriptFunction.cpp

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
+ * 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 <LibJS/Interpreter.h>
+#include <LibJS/ScriptFunction.h>
+#include <LibJS/Value.h>
+
+namespace JS {
+
+ScriptFunction::ScriptFunction(const ScopeNode& body, Vector<String> parameters)
+    : m_body(body)
+    , m_parameters(move(parameters))
+{
+}
+
+ScriptFunction::~ScriptFunction()
+{
+}
+
+Value ScriptFunction::call(Interpreter& interpreter, Vector<Value> argument_values)
+{
+    Vector<Argument> 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);
+}
+
+}

+ 51 - 0
Libraries/LibJS/ScriptFunction.h

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <LibJS/Function.h>
+
+namespace JS {
+
+class ScriptFunction final : public Function {
+public:
+    ScriptFunction(const ScopeNode& body, Vector<String> parameters = {});
+    virtual ~ScriptFunction();
+
+    const ScopeNode& body() const { return m_body; }
+    const Vector<String>& parameters() const { return m_parameters; };
+
+    virtual Value call(Interpreter&, Vector<Value>) 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<String> m_parameters;
+};
+
+}