diff options
author | Andreas Kling <kling@serenityos.org> | 2020-03-12 19:53:31 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-03-12 19:54:47 +0100 |
commit | 7912f33ea0c223b88ff79ad4c072ec87dd0c29c4 (patch) | |
tree | c5c1b67c6dcf6605b02d2eb4a5f52be4fc956499 /Libraries | |
parent | cc8e3048bc7d807154765297d4bf381f368bf3ed (diff) | |
download | serenity-7912f33ea0c223b88ff79ad4c072ec87dd0c29c4.zip |
LibJS: Add NativeFunction, a callable wrapper around a C++ lambda
This can be used to implement arbitrary functionality, callable from
JavaScript.
To make this work, I had to change the way CallExpression passes
arguments to the callee. Instead of a HashMap<String, Value>, we now
pass an ordered list of Argument { String name; Value value; }.
This patch includes a native "print(argument)" function. :^)
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibJS/AST.cpp | 26 | ||||
-rw-r--r-- | Libraries/LibJS/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.cpp | 18 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 10 | ||||
-rw-r--r-- | Libraries/LibJS/Makefile | 1 | ||||
-rw-r--r-- | Libraries/LibJS/NativeFunction.cpp | 42 | ||||
-rw-r--r-- | Libraries/LibJS/NativeFunction.h | 48 | ||||
-rw-r--r-- | Libraries/LibJS/Object.h | 1 |
8 files changed, 129 insertions, 18 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 7539dcf0f3..bc65f30c11 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -29,6 +29,7 @@ #include <LibJS/AST.h> #include <LibJS/Function.h> #include <LibJS/Interpreter.h> +#include <LibJS/NativeFunction.h> #include <LibJS/PrimitiveString.h> #include <LibJS/Value.h> #include <stdio.h> @@ -62,20 +63,25 @@ Value CallExpression::execute(Interpreter& interpreter) const auto callee = interpreter.get_variable(name()); ASSERT(callee.is_object()); auto* callee_object = callee.as_object(); - ASSERT(callee_object->is_function()); - auto& function = static_cast<Function&>(*callee_object); - - const size_t arguments_size = m_arguments.size(); - ASSERT(function.parameters().size() == arguments_size); - HashMap<String, Value> passed_parameters; - for (size_t i = 0; i < arguments_size; ++i) { - auto name = function.parameters()[i]; + + 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_parameters.set(move(name), move(value)); + passed_arguments.append({ move(name), move(value) }); } - return interpreter.run(function.body(), move(passed_parameters), ScopeType::Function); + if (callee_object->is_function()) + return interpreter.run(static_cast<Function&>(*callee_object).body(), move(passed_arguments), ScopeType::Function); + + if (callee_object->is_native_function()) { + return static_cast<NativeFunction&>(*callee_object).native_function()(move(passed_arguments)); + } + + ASSERT_NOT_REACHED(); } Value ReturnStatement::execute(Interpreter& interpreter) const diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 4f38acf973..bb4f0499ba 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -29,6 +29,7 @@ namespace JS { class ASTNode; +class Argument; class Cell; class Expression; class Heap; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 8a569e0fe3..b62c7db5ca 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -27,8 +27,10 @@ #include <AK/Badge.h> #include <LibJS/AST.h> #include <LibJS/Interpreter.h> +#include <LibJS/NativeFunction.h> #include <LibJS/Object.h> #include <LibJS/Value.h> +#include <stdio.h> namespace JS { @@ -36,15 +38,20 @@ Interpreter::Interpreter() : m_heap(*this) { m_global_object = heap().allocate<Object>(); + m_global_object->put("print", Value(heap().allocate<NativeFunction>([](Vector<Argument> arguments) -> Value { + for (auto& argument : arguments) + printf("%s ", argument.value.to_string().characters()); + return js_undefined(); + }))); } Interpreter::~Interpreter() { } -Value Interpreter::run(const ScopeNode& scope_node, HashMap<String, Value> scope_variables, ScopeType scope_type) +Value Interpreter::run(const ScopeNode& scope_node, Vector<Argument> arguments, ScopeType scope_type) { - enter_scope(scope_node, move(scope_variables), scope_type); + enter_scope(scope_node, move(arguments), scope_type); Value last_value = js_undefined(); for (auto& node : scope_node.children()) { @@ -55,13 +62,12 @@ Value Interpreter::run(const ScopeNode& scope_node, HashMap<String, Value> scope return last_value; } -void Interpreter::enter_scope(const ScopeNode& scope_node, HashMap<String, Value> scope_variables, ScopeType scope_type) +void Interpreter::enter_scope(const ScopeNode& scope_node, Vector<Argument> arguments, ScopeType scope_type) { HashMap<String, Variable> scope_variables_with_declaration_type; - for (String name : scope_variables.keys()) { - scope_variables_with_declaration_type.set(name, { scope_variables.get(name).value(), DeclarationType::Var }); + for (auto& argument : arguments) { + scope_variables_with_declaration_type.set(argument.name, { argument.value, DeclarationType::Var }); } - m_scope_stack.append({ scope_type, scope_node, move(scope_variables_with_declaration_type) }); } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 39fd4166af..9164e286b3 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -27,6 +27,7 @@ #pragma once #include <AK/HashMap.h> +#include <AK/String.h> #include <AK/Vector.h> #include <LibJS/Forward.h> #include <LibJS/Heap.h> @@ -50,12 +51,17 @@ struct ScopeFrame { HashMap<String, Variable> variables; }; +struct Argument { + String name; + Value value; +}; + class Interpreter { public: Interpreter(); ~Interpreter(); - Value run(const ScopeNode&, HashMap<String, Value> scope_variables = {}, ScopeType = ScopeType::Block); + Value run(const ScopeNode&, Vector<Argument> = {}, ScopeType = ScopeType::Block); Object& global_object() { return *m_global_object; } const Object& global_object() const { return *m_global_object; } @@ -71,7 +77,7 @@ public: void collect_roots(Badge<Heap>, HashTable<Cell*>&); private: - void enter_scope(const ScopeNode&, HashMap<String, Value> scope_variables, ScopeType); + void enter_scope(const ScopeNode&, Vector<Argument>, ScopeType); void exit_scope(const ScopeNode&); Heap m_heap; diff --git a/Libraries/LibJS/Makefile b/Libraries/LibJS/Makefile index 8e07c0c206..64d4b9bed2 100644 --- a/Libraries/LibJS/Makefile +++ b/Libraries/LibJS/Makefile @@ -6,6 +6,7 @@ OBJS = \ HeapBlock.o \ Interpreter.o \ Lexer.o \ + NativeFunction.o \ Object.o \ Parser.o \ PrimitiveString.o \ diff --git a/Libraries/LibJS/NativeFunction.cpp b/Libraries/LibJS/NativeFunction.cpp new file mode 100644 index 0000000000..92ad88b544 --- /dev/null +++ b/Libraries/LibJS/NativeFunction.cpp @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#include <LibJS/Interpreter.h> +#include <LibJS/NativeFunction.h> +#include <LibJS/Value.h> + +namespace JS { + +NativeFunction::NativeFunction(AK::Function<Value(Vector<Argument>)> native_function) + : m_native_function(move(native_function)) +{ +} + +NativeFunction::~NativeFunction() +{ +} + +} diff --git a/Libraries/LibJS/NativeFunction.h b/Libraries/LibJS/NativeFunction.h new file mode 100644 index 0000000000..6d68148461 --- /dev/null +++ b/Libraries/LibJS/NativeFunction.h @@ -0,0 +1,48 @@ +/* + * 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 <AK/Function.h> +#include <LibJS/Object.h> + +namespace JS { + +class NativeFunction final : public Object { +public: + explicit NativeFunction(AK::Function<Value(Vector<Argument>)>); + virtual ~NativeFunction() override; + + AK::Function<Value(Vector<Argument>)>& native_function() { return m_native_function; } + +private: + virtual bool is_native_function() const override { return true; } + virtual const char* class_name() const override { return "NativeFunction"; } + + AK::Function<Value(Vector<Argument>)> m_native_function; +}; + +} diff --git a/Libraries/LibJS/Object.h b/Libraries/LibJS/Object.h index cb13c4b4ba..626396ff01 100644 --- a/Libraries/LibJS/Object.h +++ b/Libraries/LibJS/Object.h @@ -42,6 +42,7 @@ public: void put(String property_name, Value); virtual bool is_function() const { return false; } + virtual bool is_native_function() const { return false; } virtual const char* class_name() const override { return "Object"; } virtual void visit_children(Cell::Visitor&) override; |