mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
LibJS: Make Value() default-construct the undefined value
The special empty value (that we use for array holes, Optional<Value> when empty and a few other other placeholder/sentinel tasks) still exists, but you now create one via JS::js_special_empty_value() and check for it with Value::is_special_empty_value(). The main idea here is to make it very unlikely to accidentally create an unexpected special empty value.
This commit is contained in:
parent
0d91363742
commit
3cf50539ec
Notes:
github-actions[bot]
2025-04-05 09:21:31 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/3cf50539ec0 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4232
43 changed files with 165 additions and 122 deletions
34
AK/Vector.h
34
AK/Vector.h
|
@ -686,6 +686,22 @@ public:
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> 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<void> 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())
|
||||
|
|
|
@ -137,7 +137,7 @@ static ThrowCompletionOr<ClassElementName> 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));
|
||||
|
|
|
@ -1201,7 +1201,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> 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<Value> 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<Optional<ScopedOperand>> 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<Optional<ScopedOperand>> ArrayExpression::genera
|
|||
if (first_spread != m_elements.end()) {
|
||||
for (auto it = first_spread; it != m_elements.end(); ++it) {
|
||||
if (!*it) {
|
||||
generator.emit<Bytecode::Op::ArrayAppend>(dst, generator.add_constant(Value()), false);
|
||||
generator.emit<Bytecode::Op::ArrayAppend>(dst, generator.add_constant(js_special_empty_value()), false);
|
||||
} else {
|
||||
auto value = TRY((*it)->generate_bytecode(generator)).value();
|
||||
generator.emit<Bytecode::Op::ArrayAppend>(dst, value, *it && is<SpreadExpression>(**it));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -298,7 +298,7 @@ public:
|
|||
emit<Bytecode::Op::PrepareYield>(Operand(Register::saved_return_value()), value);
|
||||
else
|
||||
emit<Bytecode::Op::Mov>(Operand(Register::saved_return_value()), value);
|
||||
emit<Bytecode::Op::Mov>(Operand(Register::exception()), add_constant(Value {}));
|
||||
emit<Bytecode::Op::Mov>(Operand(Register::exception()), add_constant(js_special_empty_value()));
|
||||
emit<Bytecode::Op::Jump>(Label { *m_current_basic_block->finalizer() });
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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("<Empty>"sv);
|
||||
else if (value.is_boolean())
|
||||
builder.appendff("Bool({})", value.as_bool() ? "true"sv : "false"sv);
|
||||
|
@ -268,12 +268,13 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ
|
|||
auto result_or_error = run_executable(*executable, {}, {});
|
||||
if (result_or_error.value.is_error())
|
||||
result = result_or_error.value.release_error();
|
||||
else
|
||||
result = result_or_error.return_register_value.value_or(js_undefined());
|
||||
else {
|
||||
result = result_or_error.return_register_value.is_special_empty_value() ? normal_completion(js_undefined()) : result_or_error.return_register_value;
|
||||
}
|
||||
}
|
||||
|
||||
// b. If result is a normal completion 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()) {
|
||||
// i. Set result to NormalCompletion(undefined).
|
||||
result = normal_completion(js_undefined());
|
||||
}
|
||||
|
@ -300,9 +301,7 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ
|
|||
// 17. Return ? result.
|
||||
if (result.is_abrupt()) {
|
||||
VERIFY(result.type() == Completion::Type::Throw);
|
||||
auto error = result.release_error();
|
||||
VERIFY(!error.value().is_empty());
|
||||
return error;
|
||||
return result.release_error();
|
||||
}
|
||||
|
||||
return result.value();
|
||||
|
@ -515,12 +514,12 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
|
||||
handle_ContinuePendingUnwind: {
|
||||
auto& instruction = *reinterpret_cast<Op::ContinuePendingUnwind const*>(&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<Value> 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<void> DeleteByIdWithThis::execute_impl(Bytecode::Interpreter&
|
|||
ThrowCompletionOr<void> 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<ECMAScriptFunctionObject>(*function) && !static_cast<ECMAScriptFunctionObject&>(*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<void> 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<ReferenceError>(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<CompletionCell const&>(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())));
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
ThrowCompletionOr<Value> run(Script&, GC::Ptr<Environment> lexical_environment_override = nullptr);
|
||||
ThrowCompletionOr<Value> run(SourceTextModule&);
|
||||
|
||||
ThrowCompletionOr<Value> run(Bytecode::Executable& executable, Optional<size_t> entry_point = {}, Value initial_accumulator_value = {})
|
||||
ThrowCompletionOr<Value> run(Bytecode::Executable& executable, Optional<size_t> 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;
|
||||
Value return_register_value;
|
||||
};
|
||||
ResultAndReturnRegister run_executable(Bytecode::Executable&, Optional<size_t> entry_point, Value initial_accumulator_value = {});
|
||||
ResultAndReturnRegister run_executable(Bytecode::Executable&, Optional<size_t> 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();
|
||||
|
|
|
@ -46,7 +46,7 @@ ErrorOr<String> MarkupGenerator::html_from_error(Error const& object, bool in_pr
|
|||
|
||||
ErrorOr<void> MarkupGenerator::value_to_html(Value value, StringBuilder& output_html, HashTable<Object*>& 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<void> MarkupGenerator::trace_to_html(TracebackFrame const& traceback_fra
|
|||
ErrorOr<void> 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));
|
||||
|
|
|
@ -272,8 +272,8 @@ ErrorOr<void> print_date(JS::PrintContext& print_context, JS::Date const& date,
|
|||
|
||||
ErrorOr<void> print_error(JS::PrintContext& print_context, JS::Object const& object, HashTable<JS::Object*>& 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<void> print_string_object(JS::PrintContext& print_context, JS::StringObj
|
|||
|
||||
ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
if (value.is_empty()) {
|
||||
if (value.is_special_empty_value()) {
|
||||
TRY(js_out(print_context, "\033[34;1m<empty>\033[0m"));
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -699,9 +699,7 @@ ThrowCompletionOr<Value> 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).
|
||||
|
|
|
@ -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<size_t> 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));
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
|
|||
auto generated_value = [](Value value) -> Value {
|
||||
if (value.is_cell())
|
||||
return static_cast<GeneratorResult const&>(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<size_t> {
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
, m_value(value)
|
||||
{
|
||||
VERIFY(type != Type::Empty);
|
||||
VERIFY(!value.is_empty());
|
||||
VERIFY(!value.is_special_empty_value());
|
||||
}
|
||||
|
||||
Completion(ThrowCompletionOr<Value> const&);
|
||||
|
@ -245,7 +245,7 @@ public:
|
|||
: m_value_or_error(move(value))
|
||||
{
|
||||
if constexpr (IsSame<ValueType, Value>)
|
||||
VERIFY(!m_value_or_error.template get<ValueType>().is_empty());
|
||||
VERIFY(!m_value_or_error.template get<ValueType>().is_special_empty_value());
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThrowCompletionOr(ThrowCompletionOr const&) = default;
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
mutable RefPtr<CachedSourceRange> cached_source_range;
|
||||
|
||||
GC::Ptr<PrimitiveString> function_name;
|
||||
Value this_value;
|
||||
Optional<Value> this_value;
|
||||
|
||||
GC::Ptr<Bytecode::Executable> executable;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ FinalizationRegistry::FinalizationRegistry(Realm& realm, GC::Ref<JobCallback> 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 });
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ ThrowCompletionOr<Value> FunctionEnvironment::get_this_binding(VM& vm) const
|
|||
// 9.1.1.3.1 BindThisValue ( V ), https://tc39.es/ecma262/#sec-bindthisvalue
|
||||
ThrowCompletionOr<Value> 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
|
|||
auto generated_value = [](Value value) -> Value {
|
||||
if (value.is_cell())
|
||||
return static_cast<GeneratorResult const&>(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<size_t> {
|
||||
|
|
|
@ -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<SimpleIndexedPropertyStorage const&>(*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<u32> IndexedProperties::indices() const
|
|||
Vector<u32> 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;
|
||||
|
|
|
@ -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<ValueAndAttributes> inline_get(u32 index) const
|
||||
|
|
|
@ -128,7 +128,7 @@ ThrowCompletionOr<void> 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<bool> 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<bool> 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<bool> 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<Optional<PropertyDescriptor>> 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<bool> Object::internal_has_property(PropertyKey const& propert
|
|||
// 10.1.8.1 OrdinaryGet ( O, P, Receiver ), https://tc39.es/ecma262/#sec-ordinaryget
|
||||
ThrowCompletionOr<Value> 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<Value> Object::internal_get(PropertyKey const& property_key, V
|
|||
// 10.1.9.1 OrdinarySet ( O, P, V, Receiver ), https://tc39.es/ecma262/#sec-ordinaryset
|
||||
ThrowCompletionOr<bool> 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<bool> 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<bool> Object::ordinary_set_with_own_descriptor(PropertyKey const& property_key, Value value, Value receiver, Optional<PropertyDescriptor> 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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
|
||||
static ThrowCompletionOr<PropertyKey> 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<u32>::max())
|
||||
|
|
|
@ -486,12 +486,10 @@ ThrowCompletionOr<Value> 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<bool> 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]].
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
Reference(Value base, PropertyKey name, Value this_value, bool strict = false)
|
||||
Reference(Value base, PropertyKey name, Optional<Value> 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<PropertyKey, PrivateName> m_name;
|
||||
Value m_this_value;
|
||||
Optional<Value> m_this_value;
|
||||
bool m_strict { false };
|
||||
|
||||
Optional<EnvironmentCoordinate> m_environment_coordinate;
|
||||
|
|
|
@ -158,13 +158,13 @@ ThrowCompletionOr<Value> 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());
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ inline Value typed_array_get_element(TypedArrayBase const& typed_array, Canonica
|
|||
template<typename T>
|
||||
inline ThrowCompletionOr<void> 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<Value> 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<bool> 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
|
||||
|
|
|
@ -175,7 +175,7 @@ public:
|
|||
|
||||
Value this_value() const
|
||||
{
|
||||
return running_execution_context().this_value;
|
||||
return running_execution_context().this_value.value();
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> resolve_this_binding();
|
||||
|
|
|
@ -559,7 +559,7 @@ ThrowCompletionOr<Value> Value::to_primitive_slow_case(VM& vm, PreferredType pre
|
|||
ThrowCompletionOr<GC::Ref<Object>> 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> 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())
|
||||
|
|
|
@ -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<PrimitiveString> typeof_(VM&) const;
|
||||
|
||||
|
@ -424,6 +426,13 @@ private:
|
|||
ThrowCompletionOr<Value> to_numeric_slow_case(VM&) const;
|
||||
ThrowCompletionOr<Value> 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<Value> greater_than(VM&, Value lhs, Value rhs);
|
||||
friend ThrowCompletionOr<Value> greater_than_equals(VM&, Value lhs, Value rhs);
|
||||
friend ThrowCompletionOr<Value> 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<JS::Value> : Formatter<StringView> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, JS::Value value)
|
||||
{
|
||||
if (value.is_empty())
|
||||
if (value.is_special_empty_value())
|
||||
return Formatter<StringView>::format(builder, "<empty>"sv);
|
||||
return Formatter<StringView>::format(builder, value.to_string_without_side_effects());
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace JS {
|
|||
struct ValueTraits : public Traits<Value> {
|
||||
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();
|
||||
|
||||
|
|
|
@ -727,7 +727,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<Promise
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -436,8 +436,8 @@ inline JSFileResult TestRunner::run_file_test(ByteString const& test_path)
|
|||
StringBuilder detail_builder;
|
||||
|
||||
auto& error_object = error.as_object();
|
||||
auto name = error_object.get_without_side_effects(g_vm->names.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());
|
||||
|
|
|
@ -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<WebIDL::DOMException>(object)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<WebView::StackFrame> trace;
|
||||
trace.ensure_capacity(exception.traceback().size());
|
||||
|
|
|
@ -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()); \
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ static ErrorOr<void, TestError> 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<void, TestError> 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())
|
||||
|
|
|
@ -778,7 +778,7 @@ ErrorOr<int> 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;
|
||||
|
|
Loading…
Add table
Reference in a new issue