diff options
author | Andreas Kling <kling@serenityos.org> | 2020-04-27 12:37:27 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-04-28 15:07:08 +0200 |
commit | 3c4a9e421f7465d13da256211414957a9d8650ec (patch) | |
tree | 1a48800523f79f10f145cb018549adff26cecdd7 /Libraries/LibJS | |
parent | 67b8e6fc5b4619cb5efe99d4c0cd3dfbc9a8b970 (diff) | |
download | serenity-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.cpp | 7 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.cpp | 13 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Reference.h | 15 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/delete-global-variable.js | 16 |
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); +} |