mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 19:37:35 +00:00
LibJS: Allow "delete someGlobalVariable"
This is solved by allowing Identifier nodes to produce a Reference with the global object as base.
This commit is contained in:
parent
67b8e6fc5b
commit
3c4a9e421f
6 changed files with 54 additions and 0 deletions
|
@ -382,6 +382,11 @@ Reference Expression::to_reference(Interpreter&) const
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference Identifier::to_reference(Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
return interpreter.get_reference(string());
|
||||||
|
}
|
||||||
|
|
||||||
Reference MemberExpression::to_reference(Interpreter& interpreter) const
|
Reference MemberExpression::to_reference(Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto object_value = m_object->execute(interpreter);
|
auto object_value = m_object->execute(interpreter);
|
||||||
|
@ -404,6 +409,8 @@ Value UnaryExpression::execute(Interpreter& interpreter) const
|
||||||
return {};
|
return {};
|
||||||
if (reference.is_unresolvable())
|
if (reference.is_unresolvable())
|
||||||
return Value(true);
|
return Value(true);
|
||||||
|
// FIXME: Support deleting locals
|
||||||
|
ASSERT(!reference.is_local_variable());
|
||||||
auto* base_object = reference.base().to_object(interpreter.heap());
|
auto* base_object = reference.base().to_object(interpreter.heap());
|
||||||
if (!base_object)
|
if (!base_object)
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -516,6 +516,7 @@ public:
|
||||||
virtual Value execute(Interpreter&) const override;
|
virtual Value execute(Interpreter&) const override;
|
||||||
virtual void dump(int indent) const override;
|
virtual void dump(int indent) const override;
|
||||||
virtual bool is_identifier() const override { return true; }
|
virtual bool is_identifier() const override { return true; }
|
||||||
|
virtual Reference to_reference(Interpreter&) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual const char* class_name() const override { return "Identifier"; }
|
virtual const char* class_name() const override { return "Identifier"; }
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <LibJS/Runtime/MarkedValueList.h>
|
#include <LibJS/Runtime/MarkedValueList.h>
|
||||||
#include <LibJS/Runtime/NativeFunction.h>
|
#include <LibJS/Runtime/NativeFunction.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
#include <LibJS/Runtime/Reference.h>
|
||||||
#include <LibJS/Runtime/Shape.h>
|
#include <LibJS/Runtime/Shape.h>
|
||||||
#include <LibJS/Runtime/Value.h>
|
#include <LibJS/Runtime/Value.h>
|
||||||
|
|
||||||
|
@ -163,6 +164,18 @@ Value Interpreter::get_variable(const FlyString& name)
|
||||||
return global_object().get(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)
|
void Interpreter::gather_roots(Badge<Heap>, HashTable<Cell*>& roots)
|
||||||
{
|
{
|
||||||
roots.set(m_global_object);
|
roots.set(m_global_object);
|
||||||
|
|
|
@ -96,6 +96,8 @@ public:
|
||||||
Value get_variable(const FlyString& name);
|
Value get_variable(const FlyString& name);
|
||||||
void set_variable(const FlyString& name, Value, bool first_assignment = false);
|
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 gather_roots(Badge<Heap>, HashTable<Cell*>&);
|
||||||
|
|
||||||
void enter_scope(const ScopeNode&, ArgumentVector, ScopeType);
|
void enter_scope(const ScopeNode&, ArgumentVector, ScopeType);
|
||||||
|
|
|
@ -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; }
|
Value base() const { return m_base; }
|
||||||
const PropertyName& name() const { return m_name; }
|
const PropertyName& name() const { return m_name; }
|
||||||
bool is_strict() const { return m_strict; }
|
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();
|
return m_base.is_boolean() || m_base.is_string() || m_base.is_number();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_local_variable() const
|
||||||
|
{
|
||||||
|
return m_local_variable;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Value m_base { js_undefined() };
|
Value m_base { js_undefined() };
|
||||||
PropertyName m_name;
|
PropertyName m_name;
|
||||||
bool m_strict { false };
|
bool m_strict { false };
|
||||||
|
bool m_local_variable { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
const LogStream& operator<<(const LogStream&, const Value&);
|
const LogStream& operator<<(const LogStream&, const Value&);
|
||||||
|
|
16
Libraries/LibJS/Tests/delete-global-variable.js
Normal file
16
Libraries/LibJS/Tests/delete-global-variable.js
Normal file
|
@ -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);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue