diff options
author | Andreas Kling <kling@serenityos.org> | 2020-03-11 18:58:19 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-03-11 19:00:26 +0100 |
commit | 542108421edd65f9fc5605d17331a7b14efab581 (patch) | |
tree | 3e64b8562c508dbb30c2cc58b2def0ad1bb0a1b0 | |
parent | 6ec6fa1a02d1eba0945b37e86fdbf850446eb97b (diff) | |
download | serenity-542108421edd65f9fc5605d17331a7b14efab581.zip |
LibJS: Support "hello friends".length
The above snippet is a MemberExpression that necessitates the implicit
construction of a StringObject wrapper around a PrimitiveString.
We then do a property lookup (a "get") on the StringObject, where we
find the "length" property. This is pretty neat! :^)
-rw-r--r-- | Libraries/LibJS/AST.cpp | 23 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 18 | ||||
-rw-r--r-- | Libraries/LibJS/Cell.cpp | 5 | ||||
-rw-r--r-- | Libraries/LibJS/Value.cpp | 19 | ||||
-rw-r--r-- | Libraries/LibJS/Value.h | 26 | ||||
-rw-r--r-- | Userland/js.cpp | 20 |
6 files changed, 100 insertions, 11 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 44325fb7cd..b60824bcd0 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -387,7 +387,6 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const return js_undefined(); } - void VariableDeclaration::dump(int indent) const { ASTNode::dump(indent); @@ -406,4 +405,26 @@ Value ObjectExpression::execute(Interpreter& interpreter) const return Value(interpreter.heap().allocate<Object>()); } +void MemberExpression::dump(int indent) const +{ + ASTNode::dump(indent); + m_object->dump(indent + 1); + m_property->dump(indent + 1); +} + +Value MemberExpression::execute(Interpreter& interpreter) const +{ + auto object_result = m_object->execute(interpreter).to_object(interpreter.heap()); + ASSERT(object_result.is_object()); + + String property_name; + if (m_property->is_identifier()) { + property_name = static_cast<const Identifier&>(*m_property).string(); + } else { + ASSERT_NOT_REACHED(); + } + + return object_result.as_object()->get(property_name); +} + } diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 77ec8288c7..30b1827254 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -363,4 +363,22 @@ private: virtual const char* class_name() const override { return "ObjectExpression"; } }; +class MemberExpression final : public Expression { +public: + MemberExpression(NonnullOwnPtr<Expression> object, NonnullOwnPtr<Expression> property) + : m_object(move(object)) + , m_property(move(property)) + { + } + + virtual Value execute(Interpreter&) const override; + virtual void dump(int indent) const override; + +private: + virtual const char* class_name() const override { return "MemberExpression"; } + + NonnullOwnPtr<Expression> m_object; + NonnullOwnPtr<Expression> m_property; +}; + } diff --git a/Libraries/LibJS/Cell.cpp b/Libraries/LibJS/Cell.cpp index 8e0744ea0b..d07f877e16 100644 --- a/Libraries/LibJS/Cell.cpp +++ b/Libraries/LibJS/Cell.cpp @@ -27,14 +27,15 @@ #include <AK/LogStream.h> #include <LibJS/Cell.h> #include <LibJS/Object.h> +#include <LibJS/PrimitiveString.h> #include <LibJS/Value.h> namespace JS { void Cell::Visitor::visit(Value value) { - if (value.is_object()) - visit(value.as_object()); + if (value.is_cell()) + visit(value.as_cell()); } const LogStream& operator<<(const LogStream& stream, const Cell* cell) diff --git a/Libraries/LibJS/Value.cpp b/Libraries/LibJS/Value.cpp index 530fe31d54..d2e0bfa091 100644 --- a/Libraries/LibJS/Value.cpp +++ b/Libraries/LibJS/Value.cpp @@ -25,7 +25,10 @@ */ #include <AK/String.h> +#include <LibJS/Heap.h> #include <LibJS/Object.h> +#include <LibJS/PrimitiveString.h> +#include <LibJS/StringObject.h> #include <LibJS/Value.h> namespace JS { @@ -50,6 +53,9 @@ String Value::to_string() const return String::format("{%s}", as_object()->class_name()); } + if (is_string()) + return m_value.as_string->string(); + ASSERT_NOT_REACHED(); } @@ -64,7 +70,7 @@ bool Value::to_boolean() const case Type::Undefined: return false; case Type::String: - return String(as_string()).is_empty(); + return !as_string()->string().is_empty(); case Type::Object: return true; default: @@ -72,6 +78,17 @@ bool Value::to_boolean() const } } +Value Value::to_object(Heap& heap) const +{ + if (is_object()) + return Value(as_object()); + + if (is_string()) + return Value(heap.allocate<StringObject>(m_value.as_string)); + + ASSERT_NOT_REACHED(); +} + Value greater_than(Value lhs, Value rhs) { ASSERT(lhs.is_number()); diff --git a/Libraries/LibJS/Value.h b/Libraries/LibJS/Value.h index 5de0bf8af3..7b232c2a93 100644 --- a/Libraries/LibJS/Value.h +++ b/Libraries/LibJS/Value.h @@ -50,6 +50,7 @@ public: bool is_string() const { return m_type == Type::String; } bool is_object() const { return m_type == Type::Object; } bool is_boolean() const { return m_type == Type::Boolean; } + bool is_cell() const { return is_string() || is_object(); } explicit Value(bool value) : m_type(Type::Boolean) @@ -75,6 +76,12 @@ public: m_value.as_object = object; } + explicit Value(PrimitiveString* string) + : m_type(Type::String) + { + m_value.as_string = string; + } + explicit Value(Type type) : m_type(type) { @@ -106,23 +113,38 @@ public: return m_value.as_object; } - const StringImpl* as_string() const + PrimitiveString* as_string() + { + ASSERT(is_string()); + return m_value.as_string; + } + + const PrimitiveString* as_string() const { ASSERT(is_string()); return m_value.as_string; } + Cell* as_cell() + { + ASSERT(is_cell()); + return m_value.as_cell; + } + String to_string() const; bool to_boolean() const; + Value to_object(Heap&) const; + private: Type m_type { Type::Undefined }; union { bool as_bool; double as_double; - StringImpl* as_string; + PrimitiveString* as_string; Object* as_object; + Cell* as_cell; } m_value; }; diff --git a/Userland/js.cpp b/Userland/js.cpp index 3017ae30b2..50648637d1 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -28,21 +28,22 @@ #include <LibJS/AST.h> #include <LibJS/Interpreter.h> #include <LibJS/Object.h> +#include <LibJS/PrimitiveString.h> #include <LibJS/Value.h> #include <stdio.h> -#define PROGRAM 2 +#define PROGRAM 4 -static void build_program(JS::Program&); +static void build_program(JS::Program&, JS::Heap&); int main() { - auto program = make<JS::Program>(); - build_program(*program); + JS::Interpreter interpreter; + auto program = make<JS::Program>(); + build_program(*program, interpreter.heap()); program->dump(0); - JS::Interpreter interpreter; auto result = interpreter.run(*program); dbg() << "Interpreter returned " << result; @@ -125,4 +126,13 @@ void build_program(JS::Program& program) program.append<JS::FunctionDeclaration>("foo", move(block)); program.append<JS::CallExpression>("foo"); } +#elif PROGRAM == 4 +void build_program(JS::Program& program, JS::Heap& heap) +{ + // "hello friends".length + + program.append<JS::MemberExpression>( + make<JS::Literal>(JS::Value(js_string(heap, "hello friends"))), + make<JS::Identifier>("length")); +} #endif |