summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-04-27 12:10:16 +0200
committerAndreas Kling <kling@serenityos.org>2020-04-28 15:07:08 +0200
commit67b8e6fc5b4619cb5efe99d4c0cd3dfbc9a8b970 (patch)
tree1912aadbddcfd45df56452a50a934332a52e2d34
parent3a12a8a3484415369342e4d19a7346f95ece04ee (diff)
downloadserenity-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.cpp35
-rw-r--r--Libraries/LibJS/AST.h3
-rw-r--r--Libraries/LibJS/Forward.h1
-rw-r--r--Libraries/LibJS/Runtime/PropertyName.h7
-rw-r--r--Libraries/LibJS/Runtime/Reference.h68
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&);
+
+}