diff --git a/AK/Vector.h b/AK/Vector.h index 16d83d6c4fa..28b2d0e05c1 100644 --- a/AK/Vector.h +++ b/AK/Vector.h @@ -686,6 +686,22 @@ public: return {}; } + ErrorOr try_resize_with_default_value(size_t new_size, T const& default_value, bool keep_capacity) + requires(!contains_reference) + { + if (new_size <= size()) { + shrink(new_size, keep_capacity); + return {}; + } + + TRY(try_ensure_capacity(new_size)); + + for (size_t i = size(); i < new_size; ++i) + new (slot(i)) StorageType { default_value }; + m_size = new_size; + return {}; + } + ErrorOr try_resize_and_keep_capacity(size_t new_size) requires(!contains_reference) { @@ -733,6 +749,24 @@ public: MUST(try_resize_and_keep_capacity(new_size)); } + void resize_with_default_value_and_keep_capacity(size_t new_size, T const& default_value) + requires(!contains_reference) + { + MUST(try_resize_with_default_value(new_size, default_value, true)); + } + + void resize_with_default_value(size_t new_size, T const& default_value, bool keep_capacity = false) + requires(!contains_reference) + { + MUST(try_resize_with_default_value(new_size, default_value, keep_capacity)); + } + + void fill(T const& value) + { + for (size_t i = 0; i < size(); ++i) + at(i) = value; + } + void shrink_to_fit() { if (size() == capacity()) diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 83acef7b1f2..2072a5166ea 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -137,7 +137,7 @@ static ThrowCompletionOr class_key_to_property_name(VM& vm, Ex return ClassElementName { private_environment->resolve_private_identifier(private_identifier.string()) }; } - VERIFY(!prop_key.is_empty()); + VERIFY(!prop_key.is_special_empty_value()); if (prop_key.is_object()) prop_key = TRY(prop_key.to_primitive(vm, Value::PreferredType::String)); diff --git a/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 550d78fbbbb..85d9f61f2e2 100644 --- a/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1201,7 +1201,7 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::genera // If all elements are constant primitives, we can just emit a single instruction to initialize the array, // instead of emitting instructions to manually evaluate them one-by-one Vector values; - values.resize(m_elements.size()); + values.resize_with_default_value(m_elements.size(), js_special_empty_value()); for (auto i = 0u; i < m_elements.size(); ++i) { if (!m_elements[i]) continue; @@ -1221,7 +1221,7 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::genera auto value = TRY((*it)->generate_bytecode(generator)).value(); args.append(generator.copy_if_needed_to_preserve_evaluation_order(value)); } else { - args.append(generator.add_constant(Value())); + args.append(generator.add_constant(js_special_empty_value())); } } @@ -1235,7 +1235,7 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::genera if (first_spread != m_elements.end()) { for (auto it = first_spread; it != m_elements.end(); ++it) { if (!*it) { - generator.emit(dst, generator.add_constant(Value()), false); + generator.emit(dst, generator.add_constant(js_special_empty_value()), false); } else { auto value = TRY((*it)->generate_bytecode(generator)).value(); generator.emit(dst, value, *it && is(**it)); diff --git a/Libraries/LibJS/Bytecode/Generator.cpp b/Libraries/LibJS/Bytecode/Generator.cpp index 4ce726fe7c4..c757f1ed07a 100644 --- a/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Libraries/LibJS/Bytecode/Generator.cpp @@ -1304,7 +1304,7 @@ ScopedOperand Generator::add_constant(Value value) m_null_constant = append_new_constant(); return m_null_constant.value(); } - if (value.is_empty()) { + if (value.is_special_empty_value()) { if (!m_empty_constant.has_value()) m_empty_constant = append_new_constant(); return m_empty_constant.value(); diff --git a/Libraries/LibJS/Bytecode/Generator.h b/Libraries/LibJS/Bytecode/Generator.h index 51b17d602c9..f85279ad9c5 100644 --- a/Libraries/LibJS/Bytecode/Generator.h +++ b/Libraries/LibJS/Bytecode/Generator.h @@ -298,7 +298,7 @@ public: emit(Operand(Register::saved_return_value()), value); else emit(Operand(Register::saved_return_value()), value); - emit(Operand(Register::exception()), add_constant(Value {})); + emit(Operand(Register::exception()), add_constant(js_special_empty_value())); emit(Label { *m_current_basic_block->finalizer() }); return; } diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 2f40c702270..0bec0d2f05c 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -86,7 +86,7 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe case Operand::Type::Constant: { builder.append("\033[36m"sv); auto value = executable.constants[operand.index() - executable.number_of_registers]; - if (value.is_empty()) + if (value.is_special_empty_value()) builder.append(""sv); else if (value.is_boolean()) builder.appendff("Bool({})", value.as_bool() ? "true"sv : "false"sv); @@ -268,12 +268,13 @@ ThrowCompletionOr Interpreter::run(Script& script_record, GC::Ptr Interpreter::run(Script& script_record, GC::Ptr(&bytecode[program_counter]); - if (auto exception = reg(Register::exception()); !exception.is_empty()) { + if (auto exception = reg(Register::exception()); !exception.is_special_empty_value()) { if (handle_exception(program_counter, exception) == HandleExceptionResponse::ExitFromExecutable) return; goto start; } - if (!saved_return_value().is_empty()) { + if (!saved_return_value().is_special_empty_value()) { do_return(saved_return_value()); if (auto handlers = executable.exception_handlers_for_offset(program_counter); handlers.has_value()) { if (auto finalizer = handlers.value().finalizer_offset; finalizer.has_value()) { @@ -528,7 +527,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) auto& unwind_context = running_execution_context.unwind_contexts.last(); VERIFY(unwind_context.executable == m_current_executable); reg(Register::saved_return_value()) = reg(Register::return_value()); - reg(Register::return_value()) = {}; + reg(Register::return_value()) = js_special_empty_value(); program_counter = finalizer.value(); // the unwind_context will be pop'ed when entering the finally block goto start; @@ -734,21 +733,21 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe auto& running_execution_context = vm().running_execution_context(); u32 registers_and_constants_and_locals_count = executable.number_of_registers + executable.constants.size() + executable.local_variable_names.size(); if (running_execution_context.registers_and_constants_and_locals.size() < registers_and_constants_and_locals_count) - running_execution_context.registers_and_constants_and_locals.resize(registers_and_constants_and_locals_count); + running_execution_context.registers_and_constants_and_locals.resize_with_default_value(registers_and_constants_and_locals_count, js_special_empty_value()); TemporaryChange restore_running_execution_context { m_running_execution_context, &running_execution_context }; TemporaryChange restore_arguments { m_arguments, running_execution_context.arguments.span() }; TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals, running_execution_context.registers_and_constants_and_locals.span() }; reg(Register::accumulator()) = initial_accumulator_value; - reg(Register::return_value()) = {}; + reg(Register::return_value()) = js_special_empty_value(); // NOTE: We only copy the `this` value from ExecutionContext if it's not already set. // If we are re-entering an async/generator context, the `this` value // may have already been cached by a ResolveThisBinding instruction, // and subsequent instructions expect this value to be set. - if (reg(Register::this_value()).is_empty()) - reg(Register::this_value()) = running_execution_context.this_value; + if (reg(Register::this_value()).is_special_empty_value()) + reg(Register::this_value()) = running_execution_context.this_value.value_or(js_special_empty_value()); running_execution_context.executable = &executable; @@ -764,7 +763,7 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe auto const& registers_and_constants_and_locals = running_execution_context.registers_and_constants_and_locals; for (size_t i = 0; i < executable.number_of_registers; ++i) { String value_string; - if (registers_and_constants_and_locals[i].is_empty()) + if (registers_and_constants_and_locals[i].is_special_empty_value()) value_string = "(empty)"_string; else value_string = registers_and_constants_and_locals[i].to_string_without_side_effects(); @@ -773,14 +772,14 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe } auto return_value = js_undefined(); - if (!reg(Register::return_value()).is_empty()) + if (!reg(Register::return_value()).is_special_empty_value()) return_value = reg(Register::return_value()); auto exception = reg(Register::exception()); vm().run_queued_promise_jobs(); vm().finish_execution_generation(); - if (!exception.is_empty()) + if (!exception.is_special_empty_value()) return { throw_completion(exception), running_execution_context.registers_and_constants_and_locals[0] }; return { return_value, running_execution_context.registers_and_constants_and_locals[0] }; } @@ -802,7 +801,7 @@ void Interpreter::leave_unwind_context() void Interpreter::catch_exception(Operand dst) { set(dst, reg(Register::exception())); - reg(Register::exception()) = {}; + reg(Register::exception()) = js_special_empty_value(); auto& context = running_execution_context().unwind_contexts.last(); VERIFY(!context.handler_called); VERIFY(context.executable == ¤t_executable()); @@ -817,7 +816,7 @@ void Interpreter::restore_scheduled_jump() void Interpreter::leave_finally() { - reg(Register::exception()) = {}; + reg(Register::exception()) = js_special_empty_value(); m_scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last(); } @@ -1265,7 +1264,7 @@ inline ThrowCompletionOr perform_call(Interpreter& interpreter, Value thi Value return_value; if (call_type == Op::CallType::DirectEval) { if (callee == interpreter.realm().intrinsics().eval_function()) - return_value = TRY(perform_eval(vm, !argument_values.is_empty() ? argument_values[0].value_or(JS::js_undefined()) : js_undefined(), vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct)); + return_value = TRY(perform_eval(vm, !argument_values.is_empty() ? argument_values[0] : js_undefined(), vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct)); else return_value = TRY(JS::call(vm, function, this_value, argument_values)); } else if (call_type == Op::CallType::Call) @@ -2539,13 +2538,13 @@ ThrowCompletionOr DeleteByIdWithThis::execute_impl(Bytecode::Interpreter& ThrowCompletionOr ResolveThisBinding::execute_impl(Bytecode::Interpreter& interpreter) const { auto& cached_this_value = interpreter.reg(Register::this_value()); - if (!cached_this_value.is_empty()) + if (!cached_this_value.is_special_empty_value()) return {}; // OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's // resolved once and then saved for subsequent use. auto& running_execution_context = interpreter.running_execution_context(); if (auto function = running_execution_context.function; function && is(*function) && !static_cast(*function).allocates_function_environment()) { - cached_this_value = running_execution_context.this_value; + cached_this_value = running_execution_context.this_value.value(); } else { auto& vm = interpreter.vm(); cached_this_value = TRY(vm.resolve_this_binding()); @@ -2801,7 +2800,7 @@ ThrowCompletionOr ThrowIfTDZ::execute_impl(Bytecode::Interpreter& interpre { auto& vm = interpreter.vm(); auto value = interpreter.get(m_src); - if (value.is_empty()) + if (value.is_special_empty_value()) return vm.throw_completion(ErrorType::BindingNotInitialized, value.to_string_without_side_effects()); return {}; } @@ -2825,20 +2824,20 @@ void LeaveUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const void Yield::execute_impl(Bytecode::Interpreter& interpreter) const { - auto yielded_value = interpreter.get(m_value).value_or(js_undefined()); + auto yielded_value = interpreter.get(m_value).is_special_empty_value() ? js_undefined() : interpreter.get(m_value); interpreter.do_return( interpreter.do_yield(yielded_value, m_continuation_label)); } void PrepareYield::execute_impl(Bytecode::Interpreter& interpreter) const { - auto value = interpreter.get(m_value).value_or(js_undefined()); + auto value = interpreter.get(m_value).is_special_empty_value() ? js_undefined() : interpreter.get(m_value); interpreter.set(m_dest, interpreter.do_yield(value, {})); } void Await::execute_impl(Bytecode::Interpreter& interpreter) const { - auto yielded_value = interpreter.get(m_argument).value_or(js_undefined()); + auto yielded_value = interpreter.get(m_argument).is_special_empty_value() ? js_undefined() : interpreter.get(m_argument); // FIXME: If we get a pointer, which is not accurately representable as a double // will cause this to explode auto continuation_value = Value(m_continuation_label.address()); @@ -3832,7 +3831,7 @@ ByteString Dump::to_byte_string_impl(Bytecode::Executable const& executable) con void GetCompletionFields::execute_impl(Bytecode::Interpreter& interpreter) const { auto const& completion_cell = static_cast(interpreter.get(m_completion).as_cell()); - interpreter.set(m_value_dst, completion_cell.completion().value().value_or(js_undefined())); + interpreter.set(m_value_dst, completion_cell.completion().value()); interpreter.set(m_type_dst, Value(to_underlying(completion_cell.completion().type()))); } diff --git a/Libraries/LibJS/Bytecode/Interpreter.h b/Libraries/LibJS/Bytecode/Interpreter.h index 62042710c9d..b5b25ab98c7 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Libraries/LibJS/Bytecode/Interpreter.h @@ -33,7 +33,7 @@ public: ThrowCompletionOr run(Script&, GC::Ptr lexical_environment_override = nullptr); ThrowCompletionOr run(SourceTextModule&); - ThrowCompletionOr run(Bytecode::Executable& executable, Optional entry_point = {}, Value initial_accumulator_value = {}) + ThrowCompletionOr run(Bytecode::Executable& executable, Optional entry_point = {}, Value initial_accumulator_value = js_special_empty_value()) { auto result_and_return_register = run_executable(executable, entry_point, initial_accumulator_value); return move(result_and_return_register.value); @@ -43,7 +43,7 @@ public: ThrowCompletionOr value; Value return_register_value; }; - ResultAndReturnRegister run_executable(Bytecode::Executable&, Optional entry_point, Value initial_accumulator_value = {}); + ResultAndReturnRegister run_executable(Bytecode::Executable&, Optional entry_point, Value initial_accumulator_value = js_special_empty_value()); ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); } ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); } @@ -63,7 +63,7 @@ public: void do_return(Value value) { reg(Register::return_value()) = value; - reg(Register::exception()) = {}; + reg(Register::exception()) = js_special_empty_value(); } void enter_unwind_context(); diff --git a/Libraries/LibJS/MarkupGenerator.cpp b/Libraries/LibJS/MarkupGenerator.cpp index e018d7dd532..e18a694b7d2 100644 --- a/Libraries/LibJS/MarkupGenerator.cpp +++ b/Libraries/LibJS/MarkupGenerator.cpp @@ -46,7 +46,7 @@ ErrorOr MarkupGenerator::html_from_error(Error const& object, bool in_pr ErrorOr MarkupGenerator::value_to_html(Value value, StringBuilder& output_html, HashTable& seen_objects) { - if (value.is_empty()) { + if (value.is_special_empty_value()) { TRY(output_html.try_append("<empty>"sv)); return {}; } @@ -166,8 +166,8 @@ ErrorOr MarkupGenerator::trace_to_html(TracebackFrame const& traceback_fra ErrorOr MarkupGenerator::error_to_html(Error const& error, StringBuilder& html_output, bool in_promise) { auto& vm = error.vm(); - auto name = error.get_without_side_effects(vm.names.name).value_or(js_undefined()); - auto message = error.get_without_side_effects(vm.names.message).value_or(js_undefined()); + auto name = error.get_without_side_effects(vm.names.name); + auto message = error.get_without_side_effects(vm.names.message); auto name_string = name.to_string_without_side_effects(); auto message_string = message.to_string_without_side_effects(); auto uncaught_message = TRY(String::formatted("Uncaught {}[{}]: ", in_promise ? "(in promise) " : "", name_string)); diff --git a/Libraries/LibJS/Print.cpp b/Libraries/LibJS/Print.cpp index 42feeb9cc05..3c5e52fb454 100644 --- a/Libraries/LibJS/Print.cpp +++ b/Libraries/LibJS/Print.cpp @@ -272,8 +272,8 @@ ErrorOr print_date(JS::PrintContext& print_context, JS::Date const& date, ErrorOr print_error(JS::PrintContext& print_context, JS::Object const& object, HashTable& seen_objects) { - auto name = object.get_without_side_effects(print_context.vm.names.name).value_or(JS::js_undefined()); - auto message = object.get_without_side_effects(print_context.vm.names.message).value_or(JS::js_undefined()); + auto name = object.get_without_side_effects(print_context.vm.names.name); + auto message = object.get_without_side_effects(print_context.vm.names.message); if (name.is_accessor() || message.is_accessor()) { TRY(print_value(print_context, &object, seen_objects)); } else { @@ -917,7 +917,7 @@ ErrorOr print_string_object(JS::PrintContext& print_context, JS::StringObj ErrorOr print_value(JS::PrintContext& print_context, JS::Value value, HashTable& seen_objects) { - if (value.is_empty()) { + if (value.is_special_empty_value()) { TRY(js_out(print_context, "\033[34;1m\033[0m")); return {}; } diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index e74d3460806..dd69f3b4dc7 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -699,9 +699,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, if (result_or_error.value.is_error()) return result_or_error.value.release_error(); - auto& result = result_or_error.return_register_value; - if (!result.is_empty()) - eval_result = result; + eval_result = result_or_error.return_register_value; // 30. If result.[[Type]] is normal and result.[[Value]] is empty, then // a. Set result to NormalCompletion(undefined). diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp index 8af530c507d..defc5df7f41 100644 --- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -602,7 +602,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find_last_index) // 23.1.3.13.1 FlattenIntoArray ( target, source, sourceLen, start, depth [ , mapperFunction [ , thisArg ] ] ), https://tc39.es/ecma262/#sec-flattenintoarray static ThrowCompletionOr flatten_into_array(VM& vm, Object& new_array, Object& array, size_t array_length, size_t target_index, double depth, FunctionObject* mapper_func = {}, Value this_arg = {}) { - VERIFY(!mapper_func || (!this_arg.is_empty() && depth == 1)); + VERIFY(!mapper_func || (!this_arg.is_special_empty_value() && depth == 1)); for (size_t j = 0; j < array_length; ++j) { auto value_exists = TRY(array.has_property(j)); @@ -1258,7 +1258,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::slice) double relative_end; - if (vm.argument(1).is_undefined() || vm.argument(1).is_empty()) + if (vm.argument(1).is_undefined()) relative_end = (double)initial_length; else relative_end = TRY(vm.argument(1).to_integer_or_infinity(vm)); diff --git a/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp b/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp index 755a80b8438..6087c5b99e2 100644 --- a/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp +++ b/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp @@ -166,7 +166,7 @@ void AsyncFunctionDriverWrapper::continue_async_execution(VM& vm, Value value, b }(); if (result.is_throw_completion()) { - m_top_level_promise->reject(result.throw_completion().value().value_or(js_undefined())); + m_top_level_promise->reject(result.throw_completion().value()); } } diff --git a/Libraries/LibJS/Runtime/AsyncGenerator.cpp b/Libraries/LibJS/Runtime/AsyncGenerator.cpp index f2b52248e22..658cf7c9aba 100644 --- a/Libraries/LibJS/Runtime/AsyncGenerator.cpp +++ b/Libraries/LibJS/Runtime/AsyncGenerator.cpp @@ -157,7 +157,7 @@ void AsyncGenerator::execute(VM& vm, Completion completion) auto generated_value = [](Value value) -> Value { if (value.is_cell()) return static_cast(value.as_cell()).result(); - return value.is_empty() ? js_undefined() : value; + return value.is_special_empty_value() ? js_undefined() : value; }; auto generated_continuation = [&](Value value) -> Optional { diff --git a/Libraries/LibJS/Runtime/Completion.h b/Libraries/LibJS/Runtime/Completion.h index ac5d993991c..01f7b2126d2 100644 --- a/Libraries/LibJS/Runtime/Completion.h +++ b/Libraries/LibJS/Runtime/Completion.h @@ -65,7 +65,7 @@ public: , m_value(value) { VERIFY(type != Type::Empty); - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); } Completion(ThrowCompletionOr const&); @@ -245,7 +245,7 @@ public: : m_value_or_error(move(value)) { if constexpr (IsSame) - VERIFY(!m_value_or_error.template get().is_empty()); + VERIFY(!m_value_or_error.template get().is_special_empty_value()); } ALWAYS_INLINE ThrowCompletionOr(ThrowCompletionOr const&) = default; diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index d5dbe9515e2..013b81a2610 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -793,7 +793,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro return; // 5. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. - auto result = call(vm, *closure, async_context.this_value.is_empty() ? js_undefined() : async_context.this_value); + auto result = call(vm, *closure, *async_context.this_value); // 6. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. VERIFY(&vm.running_execution_context() == &running_context); @@ -828,7 +828,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() m_bytecode_executable = m_ecmascript_code->bytecode_executable(); } - vm.running_execution_context().registers_and_constants_and_locals.resize(m_local_variables_names.size() + m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size()); + vm.running_execution_context().registers_and_constants_and_locals.resize_with_default_value(m_local_variables_names.size() + m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size(), js_special_empty_value()); auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, {}); @@ -840,7 +840,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() // NOTE: Running the bytecode should eventually return a completion. // Until it does, we assume "return" and include the undefined fallback from the call site. if (m_kind == FunctionKind::Normal) - return { Completion::Type::Return, result.value_or(js_undefined()) }; + return { Completion::Type::Return, result }; if (m_kind == FunctionKind::AsyncGenerator) { auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy())); diff --git a/Libraries/LibJS/Runtime/ExecutionContext.cpp b/Libraries/LibJS/Runtime/ExecutionContext.cpp index 3baddb9fbe5..57af2c2c5e3 100644 --- a/Libraries/LibJS/Runtime/ExecutionContext.cpp +++ b/Libraries/LibJS/Runtime/ExecutionContext.cpp @@ -82,7 +82,8 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor) visitor.visit(lexical_environment); visitor.visit(private_environment); visitor.visit(context_owner); - visitor.visit(this_value); + if (this_value.has_value()) + visitor.visit(*this_value); visitor.visit(executable); visitor.visit(function_name); visitor.visit(arguments); diff --git a/Libraries/LibJS/Runtime/ExecutionContext.h b/Libraries/LibJS/Runtime/ExecutionContext.h index f6c32153045..79643cdd614 100644 --- a/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Libraries/LibJS/Runtime/ExecutionContext.h @@ -62,7 +62,7 @@ public: mutable RefPtr cached_source_range; GC::Ptr function_name; - Value this_value; + Optional this_value; GC::Ptr executable; diff --git a/Libraries/LibJS/Runtime/FinalizationRegistry.cpp b/Libraries/LibJS/Runtime/FinalizationRegistry.cpp index 2f03549141f..67f591c98b2 100644 --- a/Libraries/LibJS/Runtime/FinalizationRegistry.cpp +++ b/Libraries/LibJS/Runtime/FinalizationRegistry.cpp @@ -21,7 +21,7 @@ FinalizationRegistry::FinalizationRegistry(Realm& realm, GC::Ref cl void FinalizationRegistry::add_finalization_record(Cell& target, Value held_value, Cell* unregister_token) { - VERIFY(!held_value.is_empty()); + VERIFY(!held_value.is_special_empty_value()); m_records.append({ &target, held_value, unregister_token }); } diff --git a/Libraries/LibJS/Runtime/FunctionEnvironment.cpp b/Libraries/LibJS/Runtime/FunctionEnvironment.cpp index 734d2131857..082c36467e9 100644 --- a/Libraries/LibJS/Runtime/FunctionEnvironment.cpp +++ b/Libraries/LibJS/Runtime/FunctionEnvironment.cpp @@ -79,7 +79,7 @@ ThrowCompletionOr FunctionEnvironment::get_this_binding(VM& vm) const // 9.1.1.3.1 BindThisValue ( V ), https://tc39.es/ecma262/#sec-bindthisvalue ThrowCompletionOr FunctionEnvironment::bind_this_value(VM& vm, Value this_value) { - VERIFY(!this_value.is_empty()); + VERIFY(!this_value.is_special_empty_value()); // 1. Assert: envRec.[[ThisBindingStatus]] is not lexical. VERIFY(m_this_binding_status != ThisBindingStatus::Lexical); diff --git a/Libraries/LibJS/Runtime/FunctionEnvironment.h b/Libraries/LibJS/Runtime/FunctionEnvironment.h index d6a2f7e7e5c..ff72a29575a 100644 --- a/Libraries/LibJS/Runtime/FunctionEnvironment.h +++ b/Libraries/LibJS/Runtime/FunctionEnvironment.h @@ -34,7 +34,7 @@ public: Value new_target() const { return m_new_target; } void set_new_target(Value new_target) { - VERIFY(!new_target.is_empty()); + VERIFY(!new_target.is_special_empty_value()); m_new_target = new_target; } diff --git a/Libraries/LibJS/Runtime/GeneratorObject.cpp b/Libraries/LibJS/Runtime/GeneratorObject.cpp index 63165411a27..8cf2f67958b 100644 --- a/Libraries/LibJS/Runtime/GeneratorObject.cpp +++ b/Libraries/LibJS/Runtime/GeneratorObject.cpp @@ -85,7 +85,7 @@ ThrowCompletionOr GeneratorObject::execute(VM& vm, Completion const& comp auto generated_value = [](Value value) -> Value { if (value.is_cell()) return static_cast(value.as_cell()).result(); - return value.is_empty() ? js_undefined() : value; + return value.is_special_empty_value() ? js_undefined() : value; }; auto generated_continuation = [&](Value value) -> Optional { diff --git a/Libraries/LibJS/Runtime/IndexedProperties.cpp b/Libraries/LibJS/Runtime/IndexedProperties.cpp index f2528a061ef..91f2830c398 100644 --- a/Libraries/LibJS/Runtime/IndexedProperties.cpp +++ b/Libraries/LibJS/Runtime/IndexedProperties.cpp @@ -36,10 +36,10 @@ void SimpleIndexedPropertyStorage::grow_storage_if_needed() return; if (m_array_size <= m_packed_elements.capacity()) { - m_packed_elements.resize_and_keep_capacity(m_array_size); + m_packed_elements.resize_with_default_value_and_keep_capacity(m_array_size, js_special_empty_value()); } else { // When the array is actually full grow storage by 25% at a time. - m_packed_elements.resize_and_keep_capacity(m_array_size + (m_array_size / 4)); + m_packed_elements.resize_with_default_value_and_keep_capacity(m_array_size + (m_array_size / 4), js_special_empty_value()); } } @@ -57,7 +57,7 @@ void SimpleIndexedPropertyStorage::put(u32 index, Value value, PropertyAttribute void SimpleIndexedPropertyStorage::remove(u32 index) { VERIFY(index < m_array_size); - m_packed_elements[index] = {}; + m_packed_elements[index] = js_special_empty_value(); } ValueAndAttributes SimpleIndexedPropertyStorage::take_first() @@ -70,14 +70,14 @@ ValueAndAttributes SimpleIndexedPropertyStorage::take_last() { m_array_size--; auto last_element = m_packed_elements[m_array_size]; - m_packed_elements[m_array_size] = {}; + m_packed_elements[m_array_size] = js_special_empty_value(); return { last_element, default_attributes }; } bool SimpleIndexedPropertyStorage::set_array_like_size(size_t new_size) { m_array_size = new_size; - m_packed_elements.resize_and_keep_capacity(new_size); + m_packed_elements.resize_with_default_value_and_keep_capacity(new_size, js_special_empty_value()); return true; } @@ -87,7 +87,7 @@ GenericIndexedPropertyStorage::GenericIndexedPropertyStorage(SimpleIndexedProper m_array_size = storage.array_like_size(); for (size_t i = 0; i < storage.m_packed_elements.size(); ++i) { auto value = storage.m_packed_elements[i]; - if (!value.is_empty()) + if (!value.is_special_empty_value()) m_sparse_elements.set(i, { value, default_attributes }); } } @@ -270,7 +270,7 @@ size_t IndexedProperties::real_size() const auto& packed_elements = static_cast(*m_storage).elements(); size_t size = 0; for (auto& element : packed_elements) { - if (!element.is_empty()) + if (!element.is_special_empty_value()) ++size; } return size; @@ -288,7 +288,7 @@ Vector IndexedProperties::indices() const Vector indices; indices.ensure_capacity(storage.array_like_size()); for (size_t i = 0; i < elements.size(); ++i) { - if (!elements.at(i).is_empty()) + if (!elements.at(i).is_special_empty_value()) indices.unchecked_append(i); } return indices; diff --git a/Libraries/LibJS/Runtime/IndexedProperties.h b/Libraries/LibJS/Runtime/IndexedProperties.h index 4d6b5eaf370..03e599f3d6a 100644 --- a/Libraries/LibJS/Runtime/IndexedProperties.h +++ b/Libraries/LibJS/Runtime/IndexedProperties.h @@ -80,7 +80,7 @@ public: [[nodiscard]] bool inline_has_index(u32 index) const { - return index < m_array_size && !m_packed_elements.data()[index].is_empty(); + return index < m_array_size && !m_packed_elements.data()[index].is_special_empty_value(); } [[nodiscard]] Optional inline_get(u32 index) const diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index 0a2d5d9270a..7812d3433f6 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -128,7 +128,7 @@ ThrowCompletionOr Object::set(PropertyKey const& property_key, Value value { auto& vm = this->vm(); - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); // 1. Let success be ? O.[[Set]](P, V, O). auto success = TRY(internal_set(property_key, value, this)); @@ -161,7 +161,7 @@ ThrowCompletionOr Object::create_data_property(PropertyKey const& property // 7.3.6 CreateMethodProperty ( O, P, V ), https://tc39.es/ecma262/#sec-createmethodproperty void Object::create_method_property(PropertyKey const& property_key, Value value) { - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); // 1. Assert: O is an ordinary, extensible object with no non-configurable properties. @@ -184,7 +184,7 @@ ThrowCompletionOr Object::create_data_property_or_throw(PropertyKey const& { auto& vm = this->vm(); - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); // 1. Let success be ? CreateDataProperty(O, P, V). auto success = TRY(create_data_property(property_key, value)); @@ -202,7 +202,7 @@ ThrowCompletionOr Object::create_data_property_or_throw(PropertyKey const& // 7.3.8 CreateNonEnumerableDataPropertyOrThrow ( O, P, V ), https://tc39.es/ecma262/#sec-createnonenumerabledatapropertyorthrow void Object::create_non_enumerable_data_property_or_throw(PropertyKey const& property_key, Value value) { - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); // 1. Assert: O is an ordinary, extensible object with no non-configurable properties. @@ -812,7 +812,7 @@ ThrowCompletionOr> Object::internal_get_own_propert // 4. If X is a data property, then if (!value.is_accessor()) { // a. Set D.[[Value]] to the value of X's [[Value]] attribute. - descriptor.value = value.value_or(js_undefined()); + descriptor.value = value; // b. Set D.[[Writable]] to the value of X's [[Writable]] attribute. descriptor.writable = attributes.is_writable(); @@ -883,7 +883,7 @@ ThrowCompletionOr Object::internal_has_property(PropertyKey const& propert // 10.1.8.1 OrdinaryGet ( O, P, Receiver ), https://tc39.es/ecma262/#sec-ordinaryget ThrowCompletionOr Object::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const { - VERIFY(!receiver.is_empty()); + VERIFY(!receiver.is_special_empty_value()); auto& vm = this->vm(); @@ -950,8 +950,8 @@ ThrowCompletionOr Object::internal_get(PropertyKey const& property_key, V // 10.1.9.1 OrdinarySet ( O, P, V, Receiver ), https://tc39.es/ecma262/#sec-ordinaryset ThrowCompletionOr Object::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata* cacheable_metadata) { - VERIFY(!value.is_empty()); - VERIFY(!receiver.is_empty()); + VERIFY(!value.is_special_empty_value()); + VERIFY(!receiver.is_special_empty_value()); // 2. Let ownDesc be ? O.[[GetOwnProperty]](P). auto own_descriptor = TRY(internal_get_own_property(property_key)); @@ -963,8 +963,8 @@ ThrowCompletionOr Object::internal_set(PropertyKey const& property_key, Va // 10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ), https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor ThrowCompletionOr Object::ordinary_set_with_own_descriptor(PropertyKey const& property_key, Value value, Value receiver, Optional own_descriptor, CacheablePropertyMetadata* cacheable_metadata) { - VERIFY(!value.is_empty()); - VERIFY(!receiver.is_empty()); + VERIFY(!value.is_special_empty_value()); + VERIFY(!receiver.is_special_empty_value()); auto& vm = this->vm(); diff --git a/Libraries/LibJS/Runtime/Promise.cpp b/Libraries/LibJS/Runtime/Promise.cpp index 9770df5d0a2..9ea0a3015c4 100644 --- a/Libraries/LibJS/Runtime/Promise.cpp +++ b/Libraries/LibJS/Runtime/Promise.cpp @@ -204,7 +204,6 @@ void Promise::fulfill(Value value) // 1. Assert: The value of promise.[[PromiseState]] is pending. VERIFY(m_state == State::Pending); - VERIFY(!value.is_empty()); // 2. Let reactions be promise.[[PromiseFulfillReactions]]. // NOTE: This is a noop, we do these steps in a slightly different order. @@ -235,7 +234,6 @@ void Promise::reject(Value reason) // 1. Assert: The value of promise.[[PromiseState]] is pending. VERIFY(m_state == State::Pending); - VERIFY(!reason.is_empty()); // 2. Let reactions be promise.[[PromiseRejectReactions]]. // NOTE: This is a noop, we do these steps in a slightly different order. diff --git a/Libraries/LibJS/Runtime/PropertyDescriptor.cpp b/Libraries/LibJS/Runtime/PropertyDescriptor.cpp index b8130eff2fa..90c3757373c 100644 --- a/Libraries/LibJS/Runtime/PropertyDescriptor.cpp +++ b/Libraries/LibJS/Runtime/PropertyDescriptor.cpp @@ -196,7 +196,7 @@ void PropertyDescriptor::complete() { if (is_generic_descriptor() || is_data_descriptor()) { if (!value.has_value()) - value = Value {}; + value = js_undefined(); if (!writable.has_value()) writable = false; } else { diff --git a/Libraries/LibJS/Runtime/PropertyKey.h b/Libraries/LibJS/Runtime/PropertyKey.h index 02c0ba3458d..9be5a7a6675 100644 --- a/Libraries/LibJS/Runtime/PropertyKey.h +++ b/Libraries/LibJS/Runtime/PropertyKey.h @@ -25,7 +25,7 @@ public: static ThrowCompletionOr from_value(VM& vm, Value value) { - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); if (value.is_symbol()) return PropertyKey { value.as_symbol() }; if (value.is_integral_number() && value.as_double() >= 0 && value.as_double() < NumericLimits::max()) diff --git a/Libraries/LibJS/Runtime/ProxyObject.cpp b/Libraries/LibJS/Runtime/ProxyObject.cpp index ec29e9fd84f..60762d8e254 100644 --- a/Libraries/LibJS/Runtime/ProxyObject.cpp +++ b/Libraries/LibJS/Runtime/ProxyObject.cpp @@ -486,12 +486,10 @@ ThrowCompletionOr ProxyObject::internal_get(PropertyKey const& property_k // NOTE: We don't return any cacheable metadata for proxy lookups. - VERIFY(!receiver.is_empty()); + VERIFY(!receiver.is_special_empty_value()); auto& vm = this->vm(); - VERIFY(!receiver.is_empty()); - // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. @@ -559,8 +557,8 @@ ThrowCompletionOr ProxyObject::internal_set(PropertyKey const& property_ke auto& vm = this->vm(); - VERIFY(!value.is_empty()); - VERIFY(!receiver.is_empty()); + VERIFY(!value.is_special_empty_value()); + VERIFY(!receiver.is_special_empty_value()); // 1. Let handler be O.[[ProxyHandler]]. diff --git a/Libraries/LibJS/Runtime/Reference.h b/Libraries/LibJS/Runtime/Reference.h index f9f68204175..7864d4bb67d 100644 --- a/Libraries/LibJS/Runtime/Reference.h +++ b/Libraries/LibJS/Runtime/Reference.h @@ -30,7 +30,7 @@ public: { } - Reference(Value base, PropertyKey name, Value this_value, bool strict = false) + Reference(Value base, PropertyKey name, Optional this_value, bool strict = false) : m_base_type(BaseType::Value) , m_base_value(base) , m_name(move(name)) @@ -90,14 +90,14 @@ public: { VERIFY(is_property_reference()); if (is_super_reference()) - return m_this_value; + return m_this_value.value(); return m_base_value; } // 6.2.4.3 IsSuperReference ( V ), https://tc39.es/ecma262/#sec-issuperreference bool is_super_reference() const { - return !m_this_value.is_empty(); + return m_this_value.has_value(); } // 6.2.4.4 IsPrivateReference ( V ), https://tc39.es/ecma262/#sec-isprivatereference @@ -131,7 +131,7 @@ private: mutable Environment* m_base_environment; }; Variant m_name; - Value m_this_value; + Optional m_this_value; bool m_strict { false }; Optional m_environment_coordinate; diff --git a/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Libraries/LibJS/Runtime/ShadowRealm.cpp index 37d9a8addc9..f52868a1acc 100644 --- a/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -158,13 +158,13 @@ ThrowCompletionOr perform_shadow_realm_eval(VM& vm, StringView source_tex result = result_and_return_register.value.release_error(); } else { // Resulting value is in the accumulator. - result = result_and_return_register.return_register_value.value_or(js_undefined()); + result = result_and_return_register.return_register_value.is_special_empty_value() ? js_undefined() : result_and_return_register.return_register_value; } } } // 12. If result.[[Type]] is normal and result.[[Value]] is empty, then - if (result.type() == Completion::Type::Normal && result.value().is_empty()) { + if (result.type() == Completion::Type::Normal && result.value().is_special_empty_value()) { // a. Set result to NormalCompletion(undefined). result = normal_completion(js_undefined()); } diff --git a/Libraries/LibJS/Runtime/TypedArray.h b/Libraries/LibJS/Runtime/TypedArray.h index be6ce15e035..ee5d67fb522 100644 --- a/Libraries/LibJS/Runtime/TypedArray.h +++ b/Libraries/LibJS/Runtime/TypedArray.h @@ -161,7 +161,7 @@ inline Value typed_array_get_element(TypedArrayBase const& typed_array, Canonica template inline ThrowCompletionOr typed_array_set_element(TypedArrayBase& typed_array, CanonicalIndex property_index, Value value) { - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); auto& vm = typed_array.vm(); Value num_value; @@ -329,7 +329,7 @@ public: // 10.4.5.5 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-typedarray-get virtual ThrowCompletionOr internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override { - VERIFY(!receiver.is_empty()); + VERIFY(!receiver.is_special_empty_value()); // NOTE: If the property name is a number type (An implementation-defined optimized // property key type), it can be treated as a string property that will transparently be @@ -354,8 +354,8 @@ public: // 10.4.5.6 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver virtual ThrowCompletionOr internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) override { - VERIFY(!value.is_empty()); - VERIFY(!receiver.is_empty()); + VERIFY(!value.is_special_empty_value()); + VERIFY(!receiver.is_special_empty_value()); // NOTE: If the property name is a number type (An implementation-defined optimized // property key type), it can be treated as a string property that will transparently be diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h index ac4d10b7f8a..1d5ee4580e6 100644 --- a/Libraries/LibJS/Runtime/VM.h +++ b/Libraries/LibJS/Runtime/VM.h @@ -175,7 +175,7 @@ public: Value this_value() const { - return running_execution_context().this_value; + return running_execution_context().this_value.value(); } ThrowCompletionOr resolve_this_binding(); diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index a9e6399a3a1..555dab57828 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -559,7 +559,7 @@ ThrowCompletionOr Value::to_primitive_slow_case(VM& vm, PreferredType pre ThrowCompletionOr> Value::to_object(VM& vm) const { auto& realm = *vm.current_realm(); - VERIFY(!is_empty()); + VERIFY(!is_special_empty_value()); // Number if (is_number()) { @@ -712,7 +712,7 @@ double string_to_number(StringView string) // 7.1.4 ToNumber ( argument ), https://tc39.es/ecma262/#sec-tonumber ThrowCompletionOr Value::to_number_slow_case(VM& vm) const { - VERIFY(!is_empty()); + VERIFY(!is_special_empty_value()); // 1. If argument is a Number, return argument. if (is_number()) diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index c60c88fc9ce..5bbf8e5916e 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -95,7 +95,7 @@ public: [[nodiscard]] u16 tag() const { return m_value.tag; } - bool is_empty() const { return m_value.tag == EMPTY_TAG; } + bool is_special_empty_value() const { return m_value.tag == EMPTY_TAG; } bool is_undefined() const { return m_value.tag == UNDEFINED_TAG; } bool is_null() const { return m_value.tag == NULL_TAG; } bool is_number() const { return is_double() || is_int32(); } @@ -155,7 +155,7 @@ public: } Value() - : Value(EMPTY_TAG << GC::TAG_SHIFT, (u64)0) + : Value(UNDEFINED_TAG << GC::TAG_SHIFT, (u64)0) { } @@ -379,12 +379,14 @@ public: [[nodiscard]] String to_string_without_side_effects() const; +#if 0 Value value_or(Value fallback) const { - if (is_empty()) + if (is_special_empty_value()) return fallback; return *this; } +#endif [[nodiscard]] GC::Ref typeof_(VM&) const; @@ -424,6 +426,13 @@ private: ThrowCompletionOr to_numeric_slow_case(VM&) const; ThrowCompletionOr to_primitive_slow_case(VM&, PreferredType) const; + enum class EmptyTag { Empty }; + + Value(EmptyTag) + : Value(EMPTY_TAG << GC::TAG_SHIFT, (u64)0) + { + } + Value(u64 tag, u64 val) { ASSERT(!(tag & val)); @@ -460,6 +469,7 @@ private: friend Value js_undefined(); friend Value js_null(); + friend Value js_special_empty_value(); friend ThrowCompletionOr greater_than(VM&, Value lhs, Value rhs); friend ThrowCompletionOr greater_than_equals(VM&, Value lhs, Value rhs); friend ThrowCompletionOr less_than(VM&, Value lhs, Value rhs); @@ -478,6 +488,11 @@ inline Value js_null() return Value(NULL_TAG << GC::TAG_SHIFT, (u64)0); } +inline Value js_special_empty_value() +{ + return Value(Value::EmptyTag::Empty); +} + inline Value js_nan() { return Value(NAN); @@ -600,12 +615,12 @@ public: void clear() { - m_value = {}; + m_value = JS::js_special_empty_value(); } [[nodiscard]] bool has_value() const { - return !m_value.is_empty(); + return !m_value.is_special_empty_value(); } [[nodiscard]] JS::Value& value() & @@ -634,7 +649,7 @@ public: } private: - JS::Value m_value; + JS::Value m_value { JS::js_special_empty_value() }; }; } @@ -690,7 +705,7 @@ template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, JS::Value value) { - if (value.is_empty()) + if (value.is_special_empty_value()) return Formatter::format(builder, ""sv); return Formatter::format(builder, value.to_string_without_side_effects()); } diff --git a/Libraries/LibJS/Runtime/ValueTraits.h b/Libraries/LibJS/Runtime/ValueTraits.h index 6ce51064dfd..f99653dafea 100644 --- a/Libraries/LibJS/Runtime/ValueTraits.h +++ b/Libraries/LibJS/Runtime/ValueTraits.h @@ -16,7 +16,7 @@ namespace JS { struct ValueTraits : public Traits { static unsigned hash(Value value) { - VERIFY(!value.is_empty()); + VERIFY(!value.is_special_empty_value()); if (value.is_string()) return value.as_string().utf8_string().hash(); diff --git a/Libraries/LibJS/SourceTextModule.cpp b/Libraries/LibJS/SourceTextModule.cpp index 229bdbb6b71..d15a6c970e3 100644 --- a/Libraries/LibJS/SourceTextModule.cpp +++ b/Libraries/LibJS/SourceTextModule.cpp @@ -727,7 +727,7 @@ ThrowCompletionOr SourceTextModule::execute_module(VM& vm, GC::Ptrnames.name).value_or(JS::js_undefined()); - auto message = error_object.get_without_side_effects(g_vm->names.message).value_or(JS::js_undefined()); + auto name = error_object.get_without_side_effects(g_vm->names.name); + auto message = error_object.get_without_side_effects(g_vm->names.message); if (name.is_accessor() || message.is_accessor()) { detail_builder.append(error.to_string_without_side_effects()); diff --git a/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp b/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp index f0660becf52..c1d2fa96682 100644 --- a/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp +++ b/Libraries/LibWeb/HTML/Scripting/ExceptionReporter.cpp @@ -22,8 +22,8 @@ void report_exception_to_console(JS::Value value, JS::Realm& realm, ErrorInPromi if (value.is_object()) { auto& object = value.as_object(); auto& vm = object.vm(); - auto name = object.get_without_side_effects(vm.names.name).value_or(JS::js_undefined()); - auto message = object.get_without_side_effects(vm.names.message).value_or(JS::js_undefined()); + auto name = object.get_without_side_effects(vm.names.name); + auto message = object.get_without_side_effects(vm.names.message); if (name.is_accessor() || message.is_accessor()) { // The result is not going to be useful, let's just print the value. This affects DOMExceptions, for example. if (is(object)) { diff --git a/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Libraries/LibWeb/Streams/AbstractOperations.cpp index c39e5cc810d..1960bd6a409 100644 --- a/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -51,14 +51,14 @@ static JS::Value create_close_sentinel() { // The close sentinel is a unique value enqueued into [[queue]], in lieu of a chunk, to signal that the stream is closed. It is only used internally, and is never exposed to web developers. // Note: We use the empty Value to signal this as, similarly to the note above, the empty value is not exposed to nor creatable by web developers. - return {}; + return JS::js_special_empty_value(); } // https://streams.spec.whatwg.org/#close-sentinel // Non-standard function that implements the "If value is a close sentinel" check. static bool is_close_sentinel(JS::Value value) { - return value.is_empty(); + return value.is_special_empty_value(); } // NON-STANDARD: Can be used instead of CreateReadableStream in cases where we need to set up a newly allocated diff --git a/Services/WebContent/DevToolsConsoleClient.cpp b/Services/WebContent/DevToolsConsoleClient.cpp index b57380393ab..0e390f942d4 100644 --- a/Services/WebContent/DevToolsConsoleClient.cpp +++ b/Services/WebContent/DevToolsConsoleClient.cpp @@ -103,8 +103,8 @@ void DevToolsConsoleClient::report_exception(JS::Error const& exception, bool in { auto& vm = exception.vm(); - auto name = exception.get_without_side_effects(vm.names.name).value_or(JS::js_undefined()); - auto message = exception.get_without_side_effects(vm.names.message).value_or(JS::js_undefined()); + auto name = exception.get_without_side_effects(vm.names.name); + auto message = exception.get_without_side_effects(vm.names.message); Vector trace; trace.ensure_capacity(exception.traceback().size()); diff --git a/Tests/LibJS/test-value-js.cpp b/Tests/LibJS/test-value-js.cpp index 786d1bc5d3f..f29102c2a85 100644 --- a/Tests/LibJS/test-value-js.cpp +++ b/Tests/LibJS/test-value-js.cpp @@ -86,7 +86,7 @@ TEST_CASE(non_canon_nans) EXPECT(!val.is_integral_number()); \ EXPECT(!val.is_finite_number()); \ EXPECT(!val.is_infinity()); \ - EXPECT(!val.is_empty()); \ + EXPECT(!val.is_special_empty_value()); \ EXPECT(!val.is_nullish()); \ } diff --git a/Tests/LibJS/test262-runner.cpp b/Tests/LibJS/test262-runner.cpp index b15b6ee69e9..aaf4c7b5b5e 100644 --- a/Tests/LibJS/test262-runner.cpp +++ b/Tests/LibJS/test262-runner.cpp @@ -91,7 +91,7 @@ static ErrorOr run_program(InterpreterT& interpreter, ScriptOrM auto& object = error_value.as_object(); auto name = object.get_without_side_effects("name"_fly_string); - if (!name.is_empty() && !name.is_accessor()) { + if (!name.is_undefined() && !name.is_accessor()) { error.type = name.to_string_without_side_effects(); } else { auto constructor = object.get_without_side_effects("constructor"_fly_string); @@ -103,7 +103,7 @@ static ErrorOr run_program(InterpreterT& interpreter, ScriptOrM } auto message = object.get_without_side_effects("message"_fly_string); - if (!message.is_empty() && !message.is_accessor()) + if (!message.is_undefined() && !message.is_accessor()) error.details = message.to_string_without_side_effects(); } if (error.type.is_empty()) diff --git a/Utilities/js.cpp b/Utilities/js.cpp index 9419207039c..89caf01eaba 100644 --- a/Utilities/js.cpp +++ b/Utilities/js.cpp @@ -778,7 +778,7 @@ ErrorOr serenity_main(Main::Arguments arguments) if (value_or_error.is_error()) return {}; auto variable = value_or_error.value(); - VERIFY(!variable.is_empty()); + VERIFY(!variable.is_special_empty_value()); if (!variable.is_object()) break;