diff options
author | Andreas Kling <kling@serenityos.org> | 2020-04-27 12:10:16 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-04-28 15:07:08 +0200 |
commit | 67b8e6fc5b4619cb5efe99d4c0cd3dfbc9a8b970 (patch) | |
tree | 1912aadbddcfd45df56452a50a934332a52e2d34 | |
parent | 3a12a8a3484415369342e4d19a7346f95ece04ee (diff) | |
download | serenity-67b8e6fc5b4619cb5efe99d4c0cd3dfbc9a8b970.zip |
LibJS: Add Reference class to represent a base.property reference
Expression nodes can now be asked to produce a Reference. We then use
this to implement the "delete" operator without downcasting the child
node to a MemberExpression manually.
-rw-r--r-- | Libraries/LibJS/AST.cpp | 35 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 3 | ||||
-rw-r--r-- | Libraries/LibJS/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/PropertyName.h | 7 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Reference.h | 68 |
5 files changed, 105 insertions, 9 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 7b1b25e4a2..62de874b72 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -37,6 +37,7 @@ #include <LibJS/Runtime/MarkedValueList.h> #include <LibJS/Runtime/NativeFunction.h> #include <LibJS/Runtime/PrimitiveString.h> +#include <LibJS/Runtime/Reference.h> #include <LibJS/Runtime/ScriptFunction.h> #include <LibJS/Runtime/Value.h> #include <stdio.h> @@ -376,21 +377,37 @@ Value LogicalExpression::execute(Interpreter& interpreter) const ASSERT_NOT_REACHED(); } +Reference Expression::to_reference(Interpreter&) const +{ + return {}; +} + +Reference MemberExpression::to_reference(Interpreter& interpreter) const +{ + auto object_value = m_object->execute(interpreter); + if (object_value.is_empty()) + return {}; + auto* object = object_value.to_object(interpreter.heap()); + if (!object) + return {}; + auto property_name = computed_property_name(interpreter); + if (!property_name.is_valid()) + return {}; + return { object, property_name }; +} + Value UnaryExpression::execute(Interpreter& interpreter) const { if (m_op == UnaryOp::Delete) { - if (!m_lhs->is_member_expression()) - return Value(true); - auto object_value = static_cast<const MemberExpression&>(*m_lhs).object().execute(interpreter); + auto reference = m_lhs->to_reference(interpreter); if (interpreter.exception()) return {}; - auto* object = object_value.to_object(interpreter.heap()); - if (!object) - return {}; - auto property_name = static_cast<const MemberExpression&>(*m_lhs).computed_property_name(interpreter); - if (!property_name.is_valid()) + if (reference.is_unresolvable()) + return Value(true); + auto* base_object = reference.base().to_object(interpreter.heap()); + if (!base_object) return {}; - return object->delete_property(property_name); + return base_object->delete_property(reference.name()); } auto lhs_result = m_lhs->execute(interpreter); diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 54d6152a5d..27891d2c21 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -140,6 +140,8 @@ private: }; class Expression : public ASTNode { +public: + virtual Reference to_reference(Interpreter&) const; }; class Declaration : public Statement { @@ -755,6 +757,7 @@ public: virtual Value execute(Interpreter&) const override; virtual void dump(int indent) const override; + virtual Reference to_reference(Interpreter&) const override; bool is_computed() const { return m_computed; } const Expression& object() const { return *m_object; } diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 53edcc48e4..7c75f169d7 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -66,6 +66,7 @@ class Interpreter; class LexicalEnvironment; class MarkedValueList; class PrimitiveString; +class Reference; class ScopeNode; class Shape; class Statement; diff --git a/Libraries/LibJS/Runtime/PropertyName.h b/Libraries/LibJS/Runtime/PropertyName.h index 49542c81c5..4ab31e7284 100644 --- a/Libraries/LibJS/Runtime/PropertyName.h +++ b/Libraries/LibJS/Runtime/PropertyName.h @@ -60,6 +60,13 @@ public: i32 as_number() const { return m_number; } const FlyString& as_string() const { return m_string; } + String to_string() const + { + if (is_string()) + return as_string(); + return String::number(as_number()); + } + private: Type m_type { Type::Invalid }; FlyString m_string; diff --git a/Libraries/LibJS/Runtime/Reference.h b/Libraries/LibJS/Runtime/Reference.h new file mode 100644 index 0000000000..a054e3c578 --- /dev/null +++ b/Libraries/LibJS/Runtime/Reference.h @@ -0,0 +1,68 @@ +/* + * 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/String.h> +#include <LibJS/Runtime/PropertyName.h> +#include <LibJS/Runtime/Value.h> + +namespace JS { + +class Reference { +public: + Reference() {} + Reference(Value base, const PropertyName& name, bool strict = false) + : m_base(base) + , m_name(name) + , m_strict(strict) + { + } + + Value base() const { return m_base; } + const PropertyName& name() const { return m_name; } + bool is_strict() const { return m_strict; } + + bool is_unresolvable() const { return m_base.is_undefined(); } + bool is_property() const + { + return m_base.is_object() || has_primitive_base(); + } + + bool has_primitive_base() const + { + return m_base.is_boolean() || m_base.is_string() || m_base.is_number(); + } + +private: + Value m_base { js_undefined() }; + PropertyName m_name; + bool m_strict { false }; +}; + +const LogStream& operator<<(const LogStream&, const Value&); + +} |