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 #include #include +#include #include #include @@ -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, HashTable& 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, HashTable&); 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); +}