summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-04-27 12:37:27 +0200
committerAndreas Kling <kling@serenityos.org>2020-04-28 15:07:08 +0200
commit3c4a9e421f7465d13da256211414957a9d8650ec (patch)
tree1a48800523f79f10f145cb018549adff26cecdd7 /Libraries/LibJS
parent67b8e6fc5b4619cb5efe99d4c0cd3dfbc9a8b970 (diff)
downloadserenity-3c4a9e421f7465d13da256211414957a9d8650ec.zip
LibJS: Allow "delete someGlobalVariable"
This is solved by allowing Identifier nodes to produce a Reference with the global object as base.
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r--Libraries/LibJS/AST.cpp7
-rw-r--r--Libraries/LibJS/AST.h1
-rw-r--r--Libraries/LibJS/Interpreter.cpp13
-rw-r--r--Libraries/LibJS/Interpreter.h2
-rw-r--r--Libraries/LibJS/Runtime/Reference.h15
-rw-r--r--Libraries/LibJS/Tests/delete-global-variable.js16
6 files changed, 54 insertions, 0 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp
index 62de874b72..ea0741363f 100644
--- a/Libraries/LibJS/AST.cpp
+++ b/Libraries/LibJS/AST.cpp
@@ -382,6 +382,11 @@ Reference Expression::to_reference(Interpreter&) const
return {};
}
+Reference Identifier::to_reference(Interpreter& interpreter) const
+{
+ return interpreter.get_reference(string());
+}
+
Reference MemberExpression::to_reference(Interpreter& interpreter) const
{
auto object_value = m_object->execute(interpreter);
@@ -404,6 +409,8 @@ Value UnaryExpression::execute(Interpreter& interpreter) const
return {};
if (reference.is_unresolvable())
return Value(true);
+ // FIXME: Support deleting locals
+ ASSERT(!reference.is_local_variable());
auto* base_object = reference.base().to_object(interpreter.heap());
if (!base_object)
return {};
diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h
index 27891d2c21..e0143f3015 100644
--- a/Libraries/LibJS/AST.h
+++ b/Libraries/LibJS/AST.h
@@ -516,6 +516,7 @@ public:
virtual Value execute(Interpreter&) const override;
virtual void dump(int indent) const override;
virtual bool is_identifier() const override { return true; }
+ virtual Reference to_reference(Interpreter&) const override;
private:
virtual const char* class_name() const override { return "Identifier"; }
diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp
index 409f01809d..57e3b78176 100644
--- a/Libraries/LibJS/Interpreter.cpp
+++ b/Libraries/LibJS/Interpreter.cpp
@@ -33,6 +33,7 @@
#include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/Value.h>
@@ -163,6 +164,18 @@ Value Interpreter::get_variable(const FlyString& name)
return global_object().get(name);
}
+Reference Interpreter::get_reference(const FlyString& name)
+{
+ if (m_call_stack.size()) {
+ for (auto* environment = current_environment(); environment; environment = environment->parent()) {
+ auto possible_match = environment->get(name);
+ if (possible_match.has_value())
+ return { Reference::LocalVariable, name };
+ }
+ }
+ return { &global_object(), PropertyName(name) };
+}
+
void Interpreter::gather_roots(Badge<Heap>, HashTable<Cell*>& roots)
{
roots.set(m_global_object);
diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h
index ebc4cb8bfb..0640b5a919 100644
--- a/Libraries/LibJS/Interpreter.h
+++ b/Libraries/LibJS/Interpreter.h
@@ -96,6 +96,8 @@ public:
Value get_variable(const FlyString& name);
void set_variable(const FlyString& name, Value, bool first_assignment = false);
+ Reference get_reference(const FlyString& name);
+
void gather_roots(Badge<Heap>, HashTable<Cell*>&);
void enter_scope(const ScopeNode&, ArgumentVector, ScopeType);
diff --git a/Libraries/LibJS/Runtime/Reference.h b/Libraries/LibJS/Runtime/Reference.h
index a054e3c578..2cad110497 100644
--- a/Libraries/LibJS/Runtime/Reference.h
+++ b/Libraries/LibJS/Runtime/Reference.h
@@ -42,6 +42,15 @@ public:
{
}
+ enum LocalVariableTag { LocalVariable };
+ Reference(LocalVariableTag, const String& name, bool strict = false)
+ : m_base(js_null())
+ , m_name(name)
+ , m_strict(strict)
+ , m_local_variable(true)
+ {
+ }
+
Value base() const { return m_base; }
const PropertyName& name() const { return m_name; }
bool is_strict() const { return m_strict; }
@@ -57,10 +66,16 @@ public:
return m_base.is_boolean() || m_base.is_string() || m_base.is_number();
}
+ bool is_local_variable() const
+ {
+ return m_local_variable;
+ }
+
private:
Value m_base { js_undefined() };
PropertyName m_name;
bool m_strict { false };
+ bool m_local_variable { false };
};
const LogStream& operator<<(const LogStream&, const Value&);
diff --git a/Libraries/LibJS/Tests/delete-global-variable.js b/Libraries/LibJS/Tests/delete-global-variable.js
new file mode 100644
index 0000000000..99143772fb
--- /dev/null
+++ b/Libraries/LibJS/Tests/delete-global-variable.js
@@ -0,0 +1,16 @@
+load("test-common.js");
+
+try {
+ a = 1;
+ assert(delete a === true);
+
+ assertThrowsError(() => {
+ a;
+ }, {
+ error: ReferenceError
+ });
+
+ console.log("PASS");
+} catch (e) {
+ console.log("FAIL: " + e);
+}