summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibJS/AST.cpp11
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.cpp5
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp5
-rw-r--r--Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Reference.cpp28
-rw-r--r--Userland/Libraries/LibJS/Runtime/Reference.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScopeObject.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/VM.cpp23
-rw-r--r--Userland/Libraries/LibJS/Runtime/VM.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/WithScope.cpp5
-rw-r--r--Userland/Libraries/LibJS/Runtime/WithScope.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/operators/delete-local-variable.js8
14 files changed, 82 insertions, 10 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp
index 3a036cf6a7..ad1762cad8 100644
--- a/Userland/Libraries/LibJS/AST.cpp
+++ b/Userland/Libraries/LibJS/AST.cpp
@@ -685,16 +685,7 @@ Value UnaryExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
auto reference = m_lhs->to_reference(interpreter, global_object);
if (interpreter.exception())
return {};
- if (reference.is_unresolvable())
- return Value(true);
- // FIXME: Support deleting locals
- VERIFY(!reference.is_local_variable());
- if (reference.is_global_variable())
- return Value(global_object.delete_property(reference.name()));
- auto* base_object = reference.base().to_object(global_object);
- if (!base_object)
- return {};
- return Value(base_object->delete_property(reference.name()));
+ return Value(reference.delete_(global_object));
}
Value lhs_result;
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
index d35e150261..e353ae36ef 100644
--- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
+++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
@@ -130,6 +130,7 @@
"target is non-extensible") \
M(ProxyTwoArguments, "Proxy constructor requires at least two arguments") \
M(ReduceNoInitial, "Reduce of empty array with no initial value") \
+ M(ReferenceNullishDeleteProperty, "Cannot delete property '{}' of {}") \
M(ReferenceNullishGetProperty, "Cannot get property '{}' of {}") \
M(ReferenceNullishSetProperty, "Cannot set property '{}' of {}") \
M(ReferencePrimitiveSetProperty, "Cannot set property '{}' of {} '{}'") \
diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
index bbef7109d9..d0af3f8a8d 100644
--- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
+++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -290,6 +290,11 @@ void GlobalObject::put_to_scope(const FlyString& name, Variable variable)
put(name, variable.value);
}
+bool GlobalObject::delete_from_scope(FlyString const& name)
+{
+ return delete_property(name);
+}
+
bool GlobalObject::has_this_binding() const
{
return true;
diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.h b/Userland/Libraries/LibJS/Runtime/GlobalObject.h
index ddcb0dbc13..96bd795c08 100644
--- a/Userland/Libraries/LibJS/Runtime/GlobalObject.h
+++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.h
@@ -23,6 +23,7 @@ public:
virtual Optional<Variable> get_from_scope(const FlyString&) const override;
virtual void put_to_scope(const FlyString&, Variable) override;
+ virtual bool delete_from_scope(FlyString const&) override;
virtual bool has_this_binding() const override;
virtual Value get_this_binding(GlobalObject&) const override;
diff --git a/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp
index 07647f3348..df6ebed89a 100644
--- a/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp
+++ b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp
@@ -62,6 +62,11 @@ void LexicalEnvironment::put_to_scope(const FlyString& name, Variable variable)
m_variables.set(name, variable);
}
+bool LexicalEnvironment::delete_from_scope(FlyString const& name)
+{
+ return m_variables.remove(name);
+}
+
bool LexicalEnvironment::has_super_binding() const
{
return m_environment_record_type == EnvironmentRecordType::Function && this_binding_status() != ThisBindingStatus::Lexical && m_home_object.is_object();
diff --git a/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h
index a2e41a7752..73f8170e86 100644
--- a/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h
+++ b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h
@@ -39,6 +39,7 @@ public:
// ^ScopeObject
virtual Optional<Variable> get_from_scope(const FlyString&) const override;
virtual void put_to_scope(const FlyString&, Variable) override;
+ virtual bool delete_from_scope(FlyString const&) override;
virtual bool has_this_binding() const override;
virtual Value get_this_binding(GlobalObject&) const override;
diff --git a/Userland/Libraries/LibJS/Runtime/Reference.cpp b/Userland/Libraries/LibJS/Runtime/Reference.cpp
index 7598abf3de..2bc6b466f8 100644
--- a/Userland/Libraries/LibJS/Runtime/Reference.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Reference.cpp
@@ -98,4 +98,32 @@ Value Reference::get(GlobalObject& global_object)
return object->get(m_name).value_or(js_undefined());
}
+bool Reference::delete_(GlobalObject& global_object)
+{
+ if (is_unresolvable())
+ return true;
+
+ auto& vm = global_object.vm();
+
+ if (is_local_variable() || is_global_variable()) {
+ if (is_local_variable())
+ return vm.delete_variable(m_name.to_string());
+ else
+ return global_object.delete_property(m_name);
+ }
+
+ auto base = this->base();
+
+ if (base.is_nullish()) {
+ // This will always fail the to_object() call below, let's throw the TypeError ourselves with a nice message instead.
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishDeleteProperty, m_name.to_value(vm).to_string_without_side_effects(), base.to_string_without_side_effects());
+ return false;
+ }
+
+ auto* object = base.to_object(global_object);
+ VERIFY(object);
+
+ return object->delete_property(m_name);
+}
+
}
diff --git a/Userland/Libraries/LibJS/Runtime/Reference.h b/Userland/Libraries/LibJS/Runtime/Reference.h
index 0a9303e2ae..864d7855c4 100644
--- a/Userland/Libraries/LibJS/Runtime/Reference.h
+++ b/Userland/Libraries/LibJS/Runtime/Reference.h
@@ -67,6 +67,7 @@ public:
void put(GlobalObject&, Value);
Value get(GlobalObject&);
+ bool delete_(GlobalObject&);
private:
void throw_reference_error(GlobalObject&);
diff --git a/Userland/Libraries/LibJS/Runtime/ScopeObject.h b/Userland/Libraries/LibJS/Runtime/ScopeObject.h
index 4abde3a250..d64826f95b 100644
--- a/Userland/Libraries/LibJS/Runtime/ScopeObject.h
+++ b/Userland/Libraries/LibJS/Runtime/ScopeObject.h
@@ -21,6 +21,7 @@ class ScopeObject : public Object {
public:
virtual Optional<Variable> get_from_scope(const FlyString&) const = 0;
virtual void put_to_scope(const FlyString&, Variable) = 0;
+ virtual bool delete_from_scope(FlyString const&) = 0;
virtual bool has_this_binding() const = 0;
virtual Value get_this_binding(GlobalObject&) const = 0;
diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp
index 4069263041..21765fa6c3 100644
--- a/Userland/Libraries/LibJS/Runtime/VM.cpp
+++ b/Userland/Libraries/LibJS/Runtime/VM.cpp
@@ -161,6 +161,29 @@ void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_o
global_object.put(name, value);
}
+bool VM::delete_variable(FlyString const& name)
+{
+ ScopeObject* specific_scope = nullptr;
+ Optional<Variable> possible_match;
+ if (!m_call_stack.is_empty()) {
+ for (auto* scope = current_scope(); scope; scope = scope->parent()) {
+ possible_match = scope->get_from_scope(name);
+ if (possible_match.has_value()) {
+ specific_scope = scope;
+ break;
+ }
+ }
+ }
+
+ if (!possible_match.has_value())
+ return false;
+ if (possible_match.value().declaration_kind == DeclarationKind::Const)
+ return false;
+
+ VERIFY(specific_scope);
+ return specific_scope->delete_from_scope(name);
+}
+
void VM::assign(const FlyString& target, Value value, GlobalObject& global_object, bool first_assignment, ScopeObject* specific_scope)
{
set_variable(target, move(value), global_object, first_assignment, specific_scope);
diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h
index 90a2d701ce..b74126bf3d 100644
--- a/Userland/Libraries/LibJS/Runtime/VM.h
+++ b/Userland/Libraries/LibJS/Runtime/VM.h
@@ -187,6 +187,7 @@ public:
Value get_variable(const FlyString& name, GlobalObject&);
void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false, ScopeObject* specific_scope = nullptr);
+ bool delete_variable(FlyString const& name);
void assign(const Variant<NonnullRefPtr<Identifier>, NonnullRefPtr<BindingPattern>>& target, Value, GlobalObject&, bool first_assignment = false, ScopeObject* specific_scope = nullptr);
void assign(const FlyString& target, Value, GlobalObject&, bool first_assignment = false, ScopeObject* specific_scope = nullptr);
void assign(const NonnullRefPtr<BindingPattern>& target, Value, GlobalObject&, bool first_assignment = false, ScopeObject* specific_scope = nullptr);
diff --git a/Userland/Libraries/LibJS/Runtime/WithScope.cpp b/Userland/Libraries/LibJS/Runtime/WithScope.cpp
index 21c7cab49e..bacda90a96 100644
--- a/Userland/Libraries/LibJS/Runtime/WithScope.cpp
+++ b/Userland/Libraries/LibJS/Runtime/WithScope.cpp
@@ -34,6 +34,11 @@ void WithScope::put_to_scope(const FlyString& name, Variable variable)
m_object.put(name, variable.value);
}
+bool WithScope::delete_from_scope(FlyString const& name)
+{
+ return m_object.delete_property(name);
+}
+
bool WithScope::has_this_binding() const
{
return parent()->has_this_binding();
diff --git a/Userland/Libraries/LibJS/Runtime/WithScope.h b/Userland/Libraries/LibJS/Runtime/WithScope.h
index 69f2d7415f..829725b7d2 100644
--- a/Userland/Libraries/LibJS/Runtime/WithScope.h
+++ b/Userland/Libraries/LibJS/Runtime/WithScope.h
@@ -18,6 +18,7 @@ public:
virtual Optional<Variable> get_from_scope(const FlyString&) const override;
virtual void put_to_scope(const FlyString&, Variable) override;
+ virtual bool delete_from_scope(FlyString const&) override;
virtual bool has_this_binding() const override;
virtual Value get_this_binding(GlobalObject&) const override;
diff --git a/Userland/Libraries/LibJS/Tests/operators/delete-local-variable.js b/Userland/Libraries/LibJS/Tests/operators/delete-local-variable.js
new file mode 100644
index 0000000000..c1585aa5c8
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/operators/delete-local-variable.js
@@ -0,0 +1,8 @@
+test("basic functionality", () => {
+ let a = 5;
+ expect(delete a).toBeTrue();
+
+ expect(() => {
+ a;
+ }).toThrowWithMessage(ReferenceError, "'a' is not defined");
+});