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:
Andreas Kling 2020-04-27 12:37:27 +02:00
parent 67b8e6fc5b
commit 3c4a9e421f
6 changed files with 54 additions and 0 deletions

View file

@ -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 {};

View file

@ -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"; }

View file

@ -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);

View file

@ -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);

View file

@ -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&);

View 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);
}