Browse Source

LibJS: Add interpreter exception checks

Matthew Olsson 5 years ago
parent
commit
4e33fbdb67

+ 20 - 2
Libraries/LibJS/AST.cpp

@@ -173,8 +173,13 @@ Value CallExpression::execute(Interpreter& interpreter) const
     if (is_new_expression()) {
         new_object = Object::create_empty(interpreter, interpreter.global_object());
         auto prototype = function.get("prototype");
-        if (prototype.is_object())
+        if (interpreter.exception())
+            return {};
+        if (prototype.is_object()) {
             new_object->set_prototype(&prototype.as_object());
+            if (interpreter.exception())
+                return {};
+        }
         call_frame.this_value = new_object;
         result = function.construct(interpreter);
     } else {
@@ -382,6 +387,8 @@ Value ForInStatement::execute(Interpreter& interpreter) const
             }
         }
         object = object->prototype();
+        if (interpreter.exception())
+            return {};
     }
     return last_value;
 }
@@ -1320,14 +1327,19 @@ Value ObjectExpression::execute(Interpreter& interpreter) const
                 auto& obj_to_spread = key_result.as_object();
 
                 for (auto& it : obj_to_spread.shape().property_table_ordered()) {
-                    if (it.value.attributes.is_enumerable())
+                    if (it.value.attributes.is_enumerable()) {
                         object->define_property(it.key, obj_to_spread.get(it.key));
+                        if (interpreter.exception())
+                            return {};
+                    }
                 }
             } else if (key_result.is_string()) {
                 auto& str_to_spread = key_result.as_string().string();
 
                 for (size_t i = 0; i < str_to_spread.length(); i++) {
                     object->define_property(i, js_string(interpreter, str_to_spread.substring(i, 1)));
+                    if (interpreter.exception())
+                        return {};
                 }
             }
 
@@ -1362,6 +1374,8 @@ Value ObjectExpression::execute(Interpreter& interpreter) const
             if (!accessor) {
                 accessor = Accessor::create(interpreter, nullptr, nullptr);
                 object->define_property(key, accessor, Attribute::Configurable | Attribute::Enumerable);
+                if (interpreter.exception())
+                    return {};
             }
             if (property.type() == ObjectProperty::Type::Getter)
                 accessor->set_getter(&value.as_function());
@@ -1369,6 +1383,8 @@ Value ObjectExpression::execute(Interpreter& interpreter) const
                 accessor->set_setter(&value.as_function());
         } else {
             object->define_property(key, value);
+            if (interpreter.exception())
+                return {};
         }
     }
     return object;
@@ -1586,6 +1602,8 @@ Value TaggedTemplateLiteral::execute(Interpreter& interpreter) const
         raw_strings->indexed_properties().append(value);
     }
     strings->define_property("raw", raw_strings, 0);
+    if (interpreter.exception())
+        return {};
 
     return interpreter.call(tag_function, js_undefined(), move(arguments));
 }

+ 11 - 3
Libraries/LibJS/Interpreter.cpp

@@ -107,10 +107,13 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector argume
 
     for (auto& declaration : scope_node.variables()) {
         for (auto& declarator : declaration.declarations()) {
-            if (scope_node.is_program())
+            if (scope_node.is_program()) {
                 global_object().put(declarator.id().string(), js_undefined());
-            else
+                if (exception())
+                    return;
+            } else {
                 scope_variables_with_declaration_kind.set(declarator.id().string(), { js_undefined(), declaration.declaration_kind() });
+            }
         }
     }
 
@@ -233,8 +236,13 @@ Value Interpreter::construct(Function& function, Function& new_target, Optional<
 
     auto* new_object = Object::create_empty(*this, global_object());
     auto prototype = new_target.get("prototype");
-    if (prototype.is_object())
+    if (exception())
+        return {};
+    if (prototype.is_object()) {
         new_object->set_prototype(&prototype.as_object());
+        if (exception())
+            return {};
+    }
     call_frame.this_value = new_object;
     auto result = function.construct(*this);
 

+ 16 - 6
Libraries/LibJS/Runtime/ArrayPrototype.cpp

@@ -170,6 +170,8 @@ Value ArrayPrototype::map(Interpreter& interpreter)
     new_array->indexed_properties().set_array_like_size(initial_length);
     for_each_item(interpreter, "map", [&](auto index, auto, auto callback_result) {
         new_array->put(index, callback_result);
+        if (interpreter.exception())
+            return IterationDecision::Break;
         return IterationDecision::Continue;
     });
     return Value(new_array);
@@ -193,8 +195,11 @@ Value ArrayPrototype::push(Interpreter& interpreter)
     auto new_length = length + argument_count;
     if (new_length > MAX_ARRAY_LIKE_INDEX)
         return interpreter.throw_exception<TypeError>("Maximum array size exceeded");
-    for (size_t i = 0; i < argument_count; ++i)
+    for (size_t i = 0; i < argument_count; ++i) {
         this_object->put(length + i, interpreter.argument(i));
+        if (interpreter.exception())
+            return {};
+    }
     auto new_length_value = Value((i32)new_length);
     this_object->put("length", new_length_value);
     if (interpreter.exception())
@@ -233,6 +238,8 @@ Value ArrayPrototype::pop(Interpreter& interpreter)
     if (interpreter.exception())
         return {};
     this_object->delete_property(index);
+    if (interpreter.exception())
+        return {};
     this_object->put("length", Value((i32)index));
     if (interpreter.exception())
         return {};
@@ -749,15 +756,18 @@ Value ArrayPrototype::splice(Interpreter& interpreter)
 
             if (!from.is_empty()) {
                 this_object->put(to, from);
-                if (interpreter.exception())
-                    return {};
             } else {
                 this_object->delete_property(to);
             }
+            if (interpreter.exception())
+                return {};
         }
 
-        for (size_t i = initial_length; i > new_length; --i)
+        for (size_t i = initial_length; i > new_length; --i) {
             this_object->delete_property(i - 1);
+            if (interpreter.exception())
+                return {};
+        }
     } else if (insert_count > actual_delete_count) {
         for (size_t i = initial_length - actual_delete_count; i > actual_start; --i) {
             auto from = this_object->get(i + actual_delete_count - 1);
@@ -768,11 +778,11 @@ Value ArrayPrototype::splice(Interpreter& interpreter)
 
             if (!from.is_empty()) {
                 this_object->put(to, from);
-                if (interpreter.exception())
-                    return {};
             } else {
                 this_object->delete_property(to);
             }
+            if (interpreter.exception())
+                return {};
         }
     }
 

+ 2 - 0
Libraries/LibJS/Runtime/BoundFunction.cpp

@@ -52,6 +52,8 @@ Value BoundFunction::construct(Interpreter& interpreter)
 {
     if (auto this_value = interpreter.this_value(); m_constructor_prototype && this_value.is_object()) {
         this_value.as_object().set_prototype(m_constructor_prototype);
+        if (interpreter.exception())
+            return {};
     }
     return m_target_function->construct(interpreter);
 }

+ 4 - 0
Libraries/LibJS/Runtime/ErrorPrototype.cpp

@@ -91,6 +91,8 @@ Value ErrorPrototype::to_string(Interpreter& interpreter)
 
     String name = "Error";
     auto name_property = this_object.get("name");
+    if (interpreter.exception())
+        return {};
     if (!name_property.is_empty() && !name_property.is_undefined()) {
         name = name_property.to_string(interpreter);
         if (interpreter.exception())
@@ -99,6 +101,8 @@ Value ErrorPrototype::to_string(Interpreter& interpreter)
 
     String message = "";
     auto message_property = this_object.get("message");
+    if (interpreter.exception())
+        return {};
     if (!message_property.is_empty() && !message_property.is_undefined()) {
         message = message_property.to_string(interpreter);
         if (interpreter.exception())

+ 4 - 0
Libraries/LibJS/Runtime/GlobalObject.h

@@ -74,7 +74,11 @@ inline void GlobalObject::add_constructor(const FlyString& property_name, Constr
 {
     constructor = heap().allocate<ConstructorType>();
     constructor->define_property("name", js_string(heap(), property_name), Attribute::Configurable);
+    if (interpreter().exception())
+        return;
     prototype.define_property("constructor", constructor, Attribute::Writable | Attribute::Configurable);
+    if (interpreter().exception())
+        return;
     define_property(property_name, constructor, Attribute::Writable | Attribute::Configurable);
 }
 

+ 57 - 17
Libraries/LibJS/Runtime/Object.cpp

@@ -44,8 +44,6 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(Interpreter& interpreter,
 {
     PropertyAttributes attributes;
     if (object.has_property("configurable")) {
-        if (interpreter.exception())
-            return {};
         attributes.set_has_configurable();
         if (object.get("configurable").value_or(Value(false)).to_boolean())
             attributes.set_configurable();
@@ -53,8 +51,6 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(Interpreter& interpreter,
             return {};
     }
     if (object.has_property("enumerable")) {
-        if (interpreter.exception())
-            return {};
         attributes.set_has_enumerable();
         if (object.get("enumerable").value_or(Value(false)).to_boolean())
             attributes.set_enumerable();
@@ -62,8 +58,6 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(Interpreter& interpreter,
             return {};
     }
     if (object.has_property("writable")) {
-        if (interpreter.exception())
-            return {};
         attributes.set_has_writable();
         if (object.get("writable").value_or(Value(false)).to_boolean())
             attributes.set_writable();
@@ -71,10 +65,16 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(Interpreter& interpreter,
             return {};
     }
     PropertyDescriptor descriptor { attributes, object.get("value"), nullptr, nullptr };
+    if (interpreter.exception())
+        return {};
     auto getter = object.get("get");
+    if (interpreter.exception())
+        return {};
     if (getter.is_function())
         descriptor.getter = &getter.as_function();
     auto setter = object.get("set");
+    if (interpreter.exception())
+        return {};
     if (setter.is_function())
         descriptor.setter = &setter.as_function();
     return descriptor;
@@ -126,6 +126,8 @@ bool Object::set_prototype(Object* new_prototype)
 bool Object::has_prototype(const Object* prototype) const
 {
     for (auto* object = this->prototype(); object; object = object->prototype()) {
+        if (interpreter().exception())
+            return false;
         if (object == prototype)
             return true;
     }
@@ -179,9 +181,15 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
             } else {
                 auto* entry_array = Array::create(interpreter().global_object());
                 entry_array->define_property(0, js_string(interpreter(), String::number(i)));
+                if (interpreter().exception())
+                    return {};
                 entry_array->define_property(1, js_string(interpreter(), String::format("%c", str[i])));
+                if (interpreter().exception())
+                    return {};
                 properties_array->define_property(i, entry_array);
             }
+            if (interpreter().exception())
+                return {};
         }
 
         return properties_array;
@@ -197,16 +205,18 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
             properties_array->define_property(property_index, js_string(interpreter(), String::number(entry.index())));
         } else if (kind == GetOwnPropertyMode::Value) {
             properties_array->define_property(property_index, value_and_attributes.value);
-            if (interpreter().exception())
-                return {};
         } else {
             auto* entry_array = Array::create(interpreter().global_object());
             entry_array->define_property(0, js_string(interpreter(), String::number(entry.index())));
+            if (interpreter().exception())
+                return {};
             entry_array->define_property(1, value_and_attributes.value);
             if (interpreter().exception())
                 return {};
             properties_array->define_property(property_index, entry_array);
         }
+        if (interpreter().exception())
+            return {};
 
         ++property_index;
     }
@@ -221,16 +231,18 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
             properties_array->define_property(offset, js_string(interpreter(), it.key));
         } else if (kind == GetOwnPropertyMode::Value) {
             properties_array->define_property(offset, this_object.get(it.key));
-            if (interpreter().exception())
-                return {};
         } else {
             auto* entry_array = Array::create(interpreter().global_object());
             entry_array->define_property(0, js_string(interpreter(), it.key));
+            if (interpreter().exception())
+                return {};
             entry_array->define_property(1, this_object.get(it.key));
             if (interpreter().exception())
                 return {};
             properties_array->define_property(offset, entry_array);
         }
+        if (interpreter().exception())
+            return {};
     }
 
     return properties_array;
@@ -284,16 +296,29 @@ Value Object::get_own_property_descriptor_object(PropertyName property_name) con
 
     auto* descriptor_object = Object::create_empty(interpreter(), interpreter().global_object());
     descriptor_object->define_property("enumerable", Value(descriptor.attributes.is_enumerable()));
+    if (interpreter().exception())
+        return {};
     descriptor_object->define_property("configurable", Value(descriptor.attributes.is_configurable()));
+    if (interpreter().exception())
+        return {};
     if (descriptor.is_data_descriptor()) {
         descriptor_object->define_property("value", descriptor.value.value_or(js_undefined()));
+        if (interpreter().exception())
+            return {};
         descriptor_object->define_property("writable", Value(descriptor.attributes.is_writable()));
+        if (interpreter().exception())
+            return {};
     } else if (descriptor.is_accessor_descriptor()) {
         if (descriptor.getter) {
             descriptor_object->define_property("get", Value(descriptor.getter));
+            if (interpreter().exception())
+                return {};
         }
-        if (descriptor.setter)
+        if (descriptor.setter) {
             descriptor_object->define_property("set", Value(descriptor.setter));
+            if (interpreter().exception())
+                return {};
+        }
     }
     return descriptor_object;
 }
@@ -309,8 +334,6 @@ bool Object::define_property(const FlyString& property_name, const Object& descr
     bool is_accessor_property = descriptor.has_property("get") || descriptor.has_property("set");
     PropertyAttributes attributes;
     if (descriptor.has_property("configurable")) {
-        if (interpreter().exception())
-            return false;
         attributes.set_has_configurable();
         if (descriptor.get("configurable").value_or(Value(false)).to_boolean())
             attributes.set_configurable();
@@ -318,8 +341,6 @@ bool Object::define_property(const FlyString& property_name, const Object& descr
             return false;
     }
     if (descriptor.has_property("enumerable")) {
-        if (interpreter().exception())
-            return false;
         attributes.set_has_enumerable();
         if (descriptor.get("enumerable").value_or(Value(false)).to_boolean())
             attributes.set_enumerable();
@@ -369,8 +390,6 @@ bool Object::define_property(const FlyString& property_name, const Object& descr
     if (interpreter().exception())
         return {};
     if (descriptor.has_property("writable")) {
-        if (interpreter().exception())
-            return false;
         attributes.set_has_writable();
         if (descriptor.get("writable").value_or(Value(false)).to_boolean())
             attributes.set_writable();
@@ -523,6 +542,11 @@ Value Object::delete_property(PropertyName property_name)
     ASSERT(property_name.is_valid());
     if (property_name.is_number())
         return Value(m_indexed_properties.remove(property_name.as_number()));
+    bool ok;
+    int property_index = property_name.as_string().to_int(ok);
+    if (ok && property_index >= 0)
+        return Value(m_indexed_properties.remove(property_name.as_number()));
+
     auto metadata = shape().lookup(property_name.as_string());
     if (!metadata.has_value())
         return Value(true);
@@ -565,6 +589,8 @@ Value Object::get_by_index(u32 property_index) const
             return {};
         }
         object = object->prototype();
+        if (interpreter().exception())
+            return {};
     }
     return {};
 }
@@ -583,9 +609,13 @@ Value Object::get(PropertyName property_name) const
     const Object* object = this;
     while (object) {
         auto value = object->get_own_property(*this, property_name);
+        if (interpreter().exception())
+            return {};
         if (!value.is_empty())
             return value;
         object = object->prototype();
+        if (interpreter().exception())
+            return {};
     }
     return {};
 }
@@ -611,6 +641,8 @@ bool Object::put_by_index(u32 property_index, Value value)
             }
         }
         object = object->prototype();
+        if (interpreter().exception())
+            return {};
     }
     return put_own_property_by_index(*this, property_index, value, default_attributes, PutOwnPropertyMode::Put);
 }
@@ -645,6 +677,8 @@ bool Object::put(PropertyName property_name, Value value)
             }
         }
         object = object->prototype();
+        if (interpreter().exception())
+            return {};
     }
     return put_own_property(*this, property_string, value, default_attributes, PutOwnPropertyMode::Put);
 }
@@ -653,7 +687,11 @@ bool Object::define_native_function(const FlyString& property_name, AK::Function
 {
     auto* function = NativeFunction::create(interpreter(), interpreter().global_object(), property_name, move(native_function));
     function->define_property("length", Value(length), Attribute::Configurable);
+    if (interpreter().exception())
+        return {};
     function->define_property("name", js_string(heap(), property_name), Attribute::Configurable);
+    if (interpreter().exception())
+        return {};
     return define_property(property_name, function, attribute);
 }
 
@@ -681,6 +719,8 @@ bool Object::has_property(PropertyName property_name) const
         if (object->has_own_property(property_name))
             return true;
         object = object->prototype();
+        if (interpreter().exception())
+            return false;
     }
     return false;
 }

+ 16 - 7
Libraries/LibJS/Runtime/ProxyObject.cpp

@@ -98,6 +98,8 @@ Object* ProxyObject::prototype()
         return nullptr;
     }
     if (m_target.is_extensible()) {
+        if (interpreter().exception())
+            return nullptr;
         if (trap_result.is_null())
             return nullptr;
         return &trap_result.as_object();
@@ -175,7 +177,8 @@ bool ProxyObject::is_extensible() const
     if (interpreter().exception())
         return false;
     if (trap_result != m_target.is_extensible()) {
-        interpreter().throw_exception<TypeError>("Proxy handler's isExtensible trap violates invariant: return value must match the target's extensibility");
+        if (!interpreter().exception())
+            interpreter().throw_exception<TypeError>("Proxy handler's isExtensible trap violates invariant: return value must match the target's extensibility");
         return false;
     }
     return trap_result;
@@ -202,7 +205,8 @@ bool ProxyObject::prevent_extensions()
     if (interpreter().exception())
         return false;
     if (trap_result && m_target.is_extensible()) {
-        interpreter().throw_exception<TypeError>("Proxy handler's preventExtensions trap violates invariant: cannot return true if the target object is extensible");
+        if (!interpreter().exception())
+            interpreter().throw_exception<TypeError>("Proxy handler's preventExtensions trap violates invariant: cannot return true if the target object is extensible");
         return false;
     }
     return trap_result;
@@ -244,7 +248,8 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(PropertyNa
             return {};
         }
         if (!m_target.is_extensible()) {
-            interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report a property as being undefined if it exists as an own property of the target and the target is non-extensible");
+            if (!interpreter().exception())
+                interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report a property as being undefined if it exists as an own property of the target and the target is non-extensible");
             return {};
         }
         return {};
@@ -253,7 +258,8 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(PropertyNa
     if (interpreter().exception())
         return {};
     if (!is_compatible_property_descriptor(interpreter(), m_target.is_extensible(), result_desc, target_desc)) {
-        interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target");
+        if (!interpreter().exception())
+            interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target");
         return {};
     }
     if (!result_desc.attributes.is_configurable() && (!target_desc.has_value() || target_desc.value().attributes.is_configurable())) {
@@ -295,7 +301,8 @@ bool ProxyObject::define_property(const FlyString& property_name, const Object&
         return false;
     if (!target_desc.has_value()) {
         if (!m_target.is_extensible()) {
-            interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: a property cannot be reported as being defined if the property does not exist on the target and the target is non-extensible");
+            if (!interpreter().exception())
+                interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: a property cannot be reported as being defined if the property does not exist on the target and the target is non-extensible");
             return false;
         }
         if (setting_config_false) {
@@ -304,7 +311,8 @@ bool ProxyObject::define_property(const FlyString& property_name, const Object&
         }
     } else {
         if (!is_compatible_property_descriptor(interpreter(), m_target.is_extensible(), PropertyDescriptor::from_dictionary(interpreter(), descriptor), target_desc)) {
-            interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: the new descriptor is not compatible with the existing descriptor of the property on the target");
+            if (!interpreter().exception())
+                interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: the new descriptor is not compatible with the existing descriptor of the property on the target");
             return false;
         }
         if (setting_config_false && target_desc.value().attributes.is_configurable()) {
@@ -346,7 +354,8 @@ bool ProxyObject::has_property(PropertyName name) const
                 return false;
             }
             if (!m_target.is_extensible()) {
-                interpreter().throw_exception<TypeError>("Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exist on the target and the target is non-extensible");
+                if (!interpreter().exception())
+                    interpreter().throw_exception<TypeError>("Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exist on the target and the target is non-extensible");
                 return false;
             }
         }

+ 2 - 0
Libraries/LibJS/Runtime/ReflectObject.cpp

@@ -145,6 +145,8 @@ Value ReflectObject::define_property(Interpreter& interpreter)
         return {};
     auto& descriptor = interpreter.argument(2).as_object();
     auto success = target->define_property(property_key, descriptor, false);
+    if (interpreter.exception())
+        return {};
     return Value(success);
 }
 

+ 2 - 0
Libraries/LibJS/Runtime/StringConstructor.cpp

@@ -79,6 +79,8 @@ Value StringConstructor::raw(Interpreter& interpreter)
         return {};
 
     auto raw = template_object->get("raw");
+    if (interpreter.exception())
+        return {};
     if (raw.is_empty() || raw.is_undefined() || raw.is_null()) {
         interpreter.throw_exception<TypeError>(String::format("Cannot convert property 'raw' to object from %s", raw.is_null() ? "null" : "undefined"));
         return {};

+ 3 - 1
Libraries/LibJS/Runtime/Value.cpp

@@ -675,11 +675,13 @@ Value in(Interpreter& interpreter, Value lhs, Value rhs)
     return Value(rhs.as_object().has_property(lhs_string));
 }
 
-Value instance_of(Interpreter&, Value lhs, Value rhs)
+Value instance_of(Interpreter& interpreter, Value lhs, Value rhs)
 {
     if (!lhs.is_object() || !rhs.is_object())
         return Value(false);
     auto constructor_prototype_property = rhs.as_object().get("prototype");
+    if (interpreter.exception())
+        return {};
     if (!constructor_prototype_property.is_object())
         return Value(false);
     return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object()));