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()); } +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(*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 object, NonnullOwnPtr 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 m_object; + NonnullOwnPtr 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 #include #include +#include #include 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 +#include #include +#include +#include #include 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(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 #include #include +#include #include #include -#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(); - build_program(*program); + JS::Interpreter interpreter; + auto program = make(); + 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("foo", move(block)); program.append("foo"); } +#elif PROGRAM == 4 +void build_program(JS::Program& program, JS::Heap& heap) +{ + // "hello friends".length + + program.append( + make(JS::Value(js_string(heap, "hello friends"))), + make("length")); +} #endif