diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 873b64b0c6..8c27bf549c 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -136,8 +136,8 @@ 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.has_value() && prototype.value().is_object()) - new_object->set_prototype(&prototype.value().as_object()); + if (prototype.is_object()) + new_object->set_prototype(&prototype.as_object()); call_frame.this_value = new_object; result = function.construct(interpreter); } else { @@ -704,10 +704,10 @@ void ForStatement::dump(int indent) const Value Identifier::execute(Interpreter& interpreter) const { - auto variable = interpreter.get_variable(string()); - if (!variable.has_value()) + auto value = interpreter.get_variable(string()); + if (value.is_empty()) return interpreter.throw_exception(String::format("'%s' not known", string().characters())); - return variable.value(); + return value; } void Identifier::dump(int indent) const @@ -1022,11 +1022,7 @@ Value MemberExpression::execute(Interpreter& interpreter) const auto* object_result = object_value.to_object(interpreter.heap()); if (interpreter.exception()) return {}; - auto result = object_result->get(computed_property_name(interpreter)); - if (result.has_value()) { - ASSERT(!result.value().is_empty()); - } - return result.value_or(js_undefined()); + return object_result->get(computed_property_name(interpreter)).value_or(js_undefined()); } Value StringLiteral::execute(Interpreter& interpreter) const diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 577f52df17..409f01809d 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -151,7 +151,7 @@ void Interpreter::set_variable(const FlyString& name, Value value, bool first_as global_object().put(move(name), move(value)); } -Optional Interpreter::get_variable(const FlyString& name) +Value Interpreter::get_variable(const FlyString& name) { if (m_call_stack.size()) { for (auto* environment = current_environment(); environment; environment = environment->parent()) { diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 9b32334df8..ebc4cb8bfb 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -93,7 +93,7 @@ public: bool should_unwind_until(ScopeType type) const { return m_unwind_until == type; } bool should_unwind() const { return m_unwind_until != ScopeType::None; } - Optional get_variable(const FlyString& name); + Value get_variable(const FlyString& name); void set_variable(const FlyString& name, Value, bool first_assignment = false); void gather_roots(Badge, HashTable&); diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Libraries/LibJS/Runtime/ErrorPrototype.cpp index d2602e021c..22ea084ab6 100644 --- a/Libraries/LibJS/Runtime/ErrorPrototype.cpp +++ b/Libraries/LibJS/Runtime/ErrorPrototype.cpp @@ -88,13 +88,13 @@ Value ErrorPrototype::to_string(Interpreter& interpreter) String name = "Error"; auto object_name_property = this_object.get("name"); - if (object_name_property.has_value() && !object_name_property.value().is_undefined()) - name = object_name_property.value().to_string(); + if (!object_name_property.is_empty() && !object_name_property.is_undefined()) + name = object_name_property.to_string(); String message = ""; auto object_message_property = this_object.get("message"); - if (object_message_property.has_value() && !object_message_property.value().is_undefined()) - message = object_message_property.value().to_string(); + if (!object_message_property.is_empty() && !object_message_property.is_undefined()) + message = object_message_property.to_string(); if (name.length() == 0) return js_string(interpreter, message); diff --git a/Libraries/LibJS/Runtime/Function.cpp b/Libraries/LibJS/Runtime/Function.cpp index 249a2b8d6f..d5b05f7251 100644 --- a/Libraries/LibJS/Runtime/Function.cpp +++ b/Libraries/LibJS/Runtime/Function.cpp @@ -68,8 +68,8 @@ BoundFunction* Function::bind(Value bound_this_value, Vector arguments) if (interpreter().exception()) { return nullptr; } - if (length_property.has_value() && length_property.value().is_number()) { - computed_length = max(0, length_property.value().to_i32() - static_cast(arguments.size())); + if (length_property.is_number()) { + computed_length = max(0, length_property.to_i32() - static_cast(arguments.size())); } Object* constructor_prototype = nullptr; @@ -77,8 +77,8 @@ BoundFunction* Function::bind(Value bound_this_value, Vector arguments) if (interpreter().exception()) { return nullptr; } - if (prototype_property.has_value() && prototype_property.value().is_object()) { - constructor_prototype = &prototype_property.value().as_object(); + if (prototype_property.is_object()) { + constructor_prototype = &prototype_property.as_object(); } auto all_bound_arguments = bound_arguments(); diff --git a/Libraries/LibJS/Runtime/FunctionPrototype.cpp b/Libraries/LibJS/Runtime/FunctionPrototype.cpp index 3e9f0e7ad5..644bffa481 100644 --- a/Libraries/LibJS/Runtime/FunctionPrototype.cpp +++ b/Libraries/LibJS/Runtime/FunctionPrototype.cpp @@ -72,11 +72,15 @@ Value FunctionPrototype::apply(Interpreter& interpreter) return interpreter.throw_exception("argument array must be an object"); size_t length = 0; auto length_property = arg_array.as_object().get("length"); - if (length_property.has_value()) - length = length_property.value().to_number().to_i32(); + if (!length_property.is_empty()) + length = length_property.to_number().to_i32(); MarkedValueList arguments(interpreter.heap()); - for (size_t i = 0; i < length; ++i) - arguments.append(arg_array.as_object().get(String::number(i)).value_or(js_undefined())); + for (size_t i = 0; i < length; ++i) { + auto element = arg_array.as_object().get(String::number(i)); + if (interpreter.exception()) + return {}; + arguments.append(element.value_or(js_undefined())); + } return interpreter.call(function, this_arg, move(arguments)); } diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index bc9affce08..0950660999 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -83,7 +83,7 @@ bool Object::has_prototype(const Object* prototype) const return false; } -Optional Object::get_own_property(const Object& this_object, const FlyString& property_name) const +Value Object::get_own_property(const Object& this_object, const FlyString& property_name) const { auto metadata = shape().lookup(property_name); if (!metadata.has_value()) @@ -154,7 +154,7 @@ void Object::put_own_property(Object& this_object, const FlyString& property_nam } } -Optional Object::get_by_index(i32 property_index) const +Value Object::get_by_index(i32 property_index) const { if (property_index < 0) return get(String::number(property_index)); @@ -172,7 +172,7 @@ Optional Object::get_by_index(i32 property_index) const return {}; } -Optional Object::get(const FlyString& property_name) const +Value Object::get(const FlyString& property_name) const { bool ok; i32 property_index = property_name.to_int(ok); @@ -182,14 +182,14 @@ Optional Object::get(const FlyString& property_name) const const Object* object = this; while (object) { auto value = object->get_own_property(*this, property_name); - if (value.has_value()) - return value.value(); + if (!value.is_empty()) + return value; object = object->prototype(); } return {}; } -Optional Object::get(PropertyName property_name) const +Value Object::get(PropertyName property_name) const { if (property_name.is_number()) return get_by_index(property_name.as_number()); @@ -308,10 +308,10 @@ Value Object::to_primitive(PreferredType preferred_type) const Value Object::to_string() const { auto to_string_property = get("toString"); - if (to_string_property.has_value() - && to_string_property.value().is_object() - && to_string_property.value().as_object().is_function()) { - auto& to_string_function = static_cast(to_string_property.value().as_object()); + if (!to_string_property.is_empty() + && to_string_property.is_object() + && to_string_property.as_object().is_function()) { + auto& to_string_function = static_cast(to_string_property.as_object()); return const_cast(this)->interpreter().call(&to_string_function, const_cast(this)); } return js_string(heap(), String::format("[object %s]", class_name())); diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index c1f59e19b5..0b0ccc4fcc 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -46,15 +46,15 @@ public: Shape& shape() { return *m_shape; } const Shape& shape() const { return *m_shape; } - virtual Optional get_by_index(i32 property_index) const; - Optional get(const FlyString& property_name) const; - Optional get(PropertyName) const; + virtual Value get_by_index(i32 property_index) const; + Value get(const FlyString& property_name) const; + Value get(PropertyName) const; virtual void put_by_index(i32 property_index, Value); void put(const FlyString& property_name, Value); void put(PropertyName, Value); - Optional get_own_property(const Object& this_object, const FlyString& property_name) const; + Value get_own_property(const Object& this_object, const FlyString& property_name) const; enum class PutOwnPropertyMode { Put, diff --git a/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Libraries/LibJS/Runtime/ObjectConstructor.cpp index d12dfbc355..95237401af 100644 --- a/Libraries/LibJS/Runtime/ObjectConstructor.cpp +++ b/Libraries/LibJS/Runtime/ObjectConstructor.cpp @@ -110,11 +110,16 @@ Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter) auto metadata = object.shape().lookup(interpreter.argument(1).to_string()); if (!metadata.has_value()) return js_undefined(); + + auto value = object.get(interpreter.argument(1).to_string()).value_or(js_undefined()); + if (interpreter.exception()) + return {}; + auto* descriptor = Object::create_empty(interpreter, interpreter.global_object()); descriptor->put("configurable", Value(!!(metadata.value().attributes & Attribute::Configurable))); descriptor->put("enumerable", Value(!!(metadata.value().attributes & Attribute::Enumerable))); descriptor->put("writable", Value(!!(metadata.value().attributes & Attribute::Writable))); - descriptor->put("value", object.get(interpreter.argument(1).to_string()).value_or(js_undefined())); + descriptor->put("value", value); return descriptor; } @@ -129,7 +134,7 @@ Value ObjectConstructor::define_property(Interpreter& interpreter) auto& object = interpreter.argument(0).as_object(); auto& descriptor = interpreter.argument(2).as_object(); - Value value = descriptor.get("value").value_or(Value()); + auto value = descriptor.get("value"); u8 configurable = descriptor.get("configurable").value_or(Value(false)).to_boolean() * Attribute::Configurable; u8 enumerable = descriptor.get("enumerable").value_or(Value(false)).to_boolean() * Attribute::Enumerable; u8 writable = descriptor.get("writable").value_or(Value(false)).to_boolean() * Attribute::Writable; diff --git a/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp b/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp index 5ae13688c3..b04b236be2 100644 --- a/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp +++ b/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp @@ -72,7 +72,7 @@ void Uint8ClampedArray::put_by_index(i32 property_index, Value value) m_data[property_index] = clamp(value.to_i32(), 0, 255); } -Optional Uint8ClampedArray::get_by_index(i32 property_index) const +Value Uint8ClampedArray::get_by_index(i32 property_index) const { ASSERT(property_index >= 0); ASSERT(property_index < m_length); diff --git a/Libraries/LibJS/Runtime/Uint8ClampedArray.h b/Libraries/LibJS/Runtime/Uint8ClampedArray.h index 393fa4d8ce..411d0018ba 100644 --- a/Libraries/LibJS/Runtime/Uint8ClampedArray.h +++ b/Libraries/LibJS/Runtime/Uint8ClampedArray.h @@ -40,7 +40,7 @@ public: i32 length() const { return m_length; } virtual void put_by_index(i32 property_index, Value value) override; - virtual Optional get_by_index(i32 property_index) const override; + virtual Value get_by_index(i32 property_index) const override; u8* data() { return m_data; } const u8* data() const { return m_data; } diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index dd92a21565..e2457d5ce4 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -387,7 +387,7 @@ Value in(Interpreter& interpreter, Value lhs, Value rhs) if (!rhs.is_object()) return interpreter.throw_exception("'in' operator must be used on object"); - return Value(rhs.as_object().get(lhs.to_string()).has_value()); + return Value(!rhs.as_object().get(lhs.to_string()).is_empty()); } Value instance_of(Interpreter&, Value lhs, Value rhs) @@ -396,10 +396,10 @@ Value instance_of(Interpreter&, Value lhs, Value rhs) return Value(false); auto constructor_prototype_property = rhs.as_object().get("prototype"); - if (!constructor_prototype_property.has_value() || !constructor_prototype_property.value().is_object()) + if (!constructor_prototype_property.is_object()) return Value(false); - return Value(lhs.as_object().has_prototype(&constructor_prototype_property.value().as_object())); + return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object())); } const LogStream& operator<<(const LogStream& stream, const Value& value) diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index df0bd23efb..3bed8c2db7 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -164,6 +164,13 @@ public: Object* to_object(Heap&) const; + Value value_or(Value fallback) const + { + if (is_empty()) + return fallback; + return *this; + } + private: Type m_type { Type::Empty }; diff --git a/Userland/js.cpp b/Userland/js.cpp index e3135d2c44..602728c3d1 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -607,13 +607,13 @@ int main(int argc, char** argv) auto property_pattern = parts[1]; auto maybe_variable = interpreter->get_variable(name); - if (!maybe_variable.has_value()) { + if (maybe_variable.is_empty()) { maybe_variable = interpreter->global_object().get(name); - if (!maybe_variable.has_value()) + if (maybe_variable.is_empty()) return {}; } - const auto& variable = maybe_variable.value(); + auto variable = maybe_variable; if (!variable.is_object()) return {};