mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-18 18:00:31 +00:00
LibJS: Make more use of Value::is and Value::as_if in JS runtime
This commit is contained in:
parent
e450f2ea16
commit
4287da7d3a
31 changed files with 109 additions and 145 deletions
|
|
@ -620,17 +620,15 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
|
|||
auto this_environment_record = get_this_environment(vm);
|
||||
|
||||
// b. If thisEnvRec is a function Environment Record, then
|
||||
if (is<FunctionEnvironment>(*this_environment_record)) {
|
||||
auto& this_function_environment_record = static_cast<FunctionEnvironment&>(*this_environment_record);
|
||||
|
||||
if (auto* this_function_environment_record = as_if<FunctionEnvironment>(*this_environment_record)) {
|
||||
// i. Let F be thisEnvRec.[[FunctionObject]].
|
||||
auto& function = as<ECMAScriptFunctionObject>(this_function_environment_record.function_object());
|
||||
auto& function = as<ECMAScriptFunctionObject>(this_function_environment_record->function_object());
|
||||
|
||||
// ii. Set inFunction to true.
|
||||
in_function = true;
|
||||
|
||||
// iii. Set inMethod to thisEnvRec.HasSuperBinding().
|
||||
in_method = this_function_environment_record.has_super_binding();
|
||||
in_method = this_function_environment_record->has_super_binding();
|
||||
|
||||
// iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true.
|
||||
if (function.constructor_kind() == ConstructorKind::Derived)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ ThrowCompletionOr<GC::Ref<Object>> ArrayBufferConstructor::construct(FunctionObj
|
|||
|
||||
if (byte_length_or_error.is_error()) {
|
||||
auto error = byte_length_or_error.release_error();
|
||||
if (error.value().is_object() && is<RangeError>(error.value().as_object())) {
|
||||
if (error.value().is<RangeError>()) {
|
||||
// Re-throw more specific RangeError
|
||||
return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "array buffer");
|
||||
}
|
||||
|
|
@ -81,16 +81,17 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferConstructor::is_view)
|
|||
|
||||
// 1. If arg is not an Object, return false.
|
||||
if (!arg.is_object())
|
||||
return Value(false);
|
||||
return false;
|
||||
auto const& object = arg.as_object();
|
||||
|
||||
// 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
|
||||
if (arg.as_object().is_typed_array())
|
||||
return Value(true);
|
||||
if (is<DataView>(arg.as_object()))
|
||||
return Value(true);
|
||||
if (object.is_typed_array())
|
||||
return true;
|
||||
if (is<DataView>(object))
|
||||
return true;
|
||||
|
||||
// 3. Return false.
|
||||
return Value(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 25.1.5.3 get ArrayBuffer [ @@species ], https://tc39.es/ecma262/#sec-get-arraybuffer-@@species
|
||||
|
|
|
|||
|
|
@ -237,9 +237,9 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
|
|||
auto new_array_buffer = TRY(construct(vm, *constructor, Value(new_length)));
|
||||
|
||||
// 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
|
||||
if (!is<ArrayBuffer>(new_array_buffer.ptr()))
|
||||
auto* new_array_buffer_object = as_if<ArrayBuffer>(*new_array_buffer);
|
||||
if (!new_array_buffer_object)
|
||||
return vm.throw_completion<TypeError>(ErrorType::SpeciesConstructorDidNotCreate, "an ArrayBuffer");
|
||||
auto* new_array_buffer_object = static_cast<ArrayBuffer*>(new_array_buffer.ptr());
|
||||
|
||||
// 18. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
|
||||
if (new_array_buffer_object->is_shared_array_buffer())
|
||||
|
|
|
|||
|
|
@ -107,13 +107,13 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncDisposableStackPrototype::dispose_async)
|
|||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let asyncDisposableStack be the this value.
|
||||
auto async_disposable_stack_value = vm.this_value();
|
||||
auto async_disposable_stack = vm.this_value().as_if<AsyncDisposableStack>();
|
||||
|
||||
// 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
|
||||
|
||||
// 3. If asyncDisposableStack does not have an [[AsyncDisposableState]] internal slot, then
|
||||
if (!async_disposable_stack_value.is_object() || !is<AsyncDisposableStack>(async_disposable_stack_value.as_object())) {
|
||||
if (!async_disposable_stack) {
|
||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||
auto error = vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, display_name());
|
||||
MUST(call(vm, *promise_capability->reject(), js_undefined(), error.value()));
|
||||
|
|
@ -122,10 +122,8 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncDisposableStackPrototype::dispose_async)
|
|||
return promise_capability->promise();
|
||||
}
|
||||
|
||||
auto& async_disposable_stack = static_cast<AsyncDisposableStack&>(async_disposable_stack_value.as_object());
|
||||
|
||||
// 4. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, then
|
||||
if (async_disposable_stack.async_disposable_state() == AsyncDisposableStack::AsyncDisposableState::Disposed) {
|
||||
if (async_disposable_stack->async_disposable_state() == AsyncDisposableStack::AsyncDisposableState::Disposed) {
|
||||
// a. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »).
|
||||
MUST(call(vm, *promise_capability->resolve(), js_undefined(), js_undefined()));
|
||||
|
||||
|
|
@ -134,11 +132,11 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncDisposableStackPrototype::dispose_async)
|
|||
}
|
||||
|
||||
// 5. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed.
|
||||
async_disposable_stack.set_disposed();
|
||||
async_disposable_stack->set_disposed();
|
||||
|
||||
// 6. Let result be DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)).
|
||||
// 7. IfAbruptRejectPromise(result, promiseCapability).
|
||||
auto result = TRY_OR_REJECT(vm, promise_capability, dispose_resources(vm, async_disposable_stack.dispose_capability(), normal_completion(js_undefined())));
|
||||
auto result = TRY_OR_REJECT(vm, promise_capability, dispose_resources(vm, async_disposable_stack->dispose_capability(), normal_completion(js_undefined())));
|
||||
|
||||
// 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result »).
|
||||
MUST(call(vm, *promise_capability->resolve(), js_undefined(), result));
|
||||
|
|
|
|||
|
|
@ -136,14 +136,13 @@ void AsyncFunctionDriverWrapper::continue_async_execution(VM& vm, Value value, b
|
|||
auto promise_value = result.value;
|
||||
if (result.done) {
|
||||
// When returning a promise, we need to unwrap it.
|
||||
if (promise_value.is_object() && is<Promise>(promise_value.as_object())) {
|
||||
auto& returned_promise = static_cast<Promise&>(promise_value.as_object());
|
||||
if (returned_promise.state() == Promise::State::Fulfilled) {
|
||||
m_top_level_promise->fulfill(returned_promise.result());
|
||||
if (auto returned_promise = promise_value.as_if<Promise>()) {
|
||||
if (returned_promise->state() == Promise::State::Fulfilled) {
|
||||
m_top_level_promise->fulfill(returned_promise->result());
|
||||
return {};
|
||||
}
|
||||
if (returned_promise.state() == Promise::State::Rejected)
|
||||
return throw_completion(returned_promise.result());
|
||||
if (returned_promise->state() == Promise::State::Rejected)
|
||||
return throw_completion(returned_promise->result());
|
||||
|
||||
// The promise is still pending but there's nothing more to do here.
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -39,17 +39,16 @@ static ThrowCompletionOr<GC::Ref<AsyncGenerator>> async_generator_validate(VM& v
|
|||
// 1. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]).
|
||||
// 2. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorState]]).
|
||||
// 3. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorQueue]]).
|
||||
if (!generator.is_object() || !is<AsyncGenerator>(generator.as_object()))
|
||||
auto async_generator = generator.as_if<AsyncGenerator>();
|
||||
if (!async_generator)
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "AsyncGenerator");
|
||||
|
||||
auto& async_generator = static_cast<AsyncGenerator&>(generator.as_object());
|
||||
|
||||
// 4. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception.
|
||||
if (async_generator.generator_brand() != generator_brand)
|
||||
return vm.throw_completion<TypeError>(ErrorType::GeneratorBrandMismatch, async_generator.generator_brand().value_or("emp"_string), generator_brand.value_or("emp"_string));
|
||||
if (async_generator->generator_brand() != generator_brand)
|
||||
return vm.throw_completion<TypeError>(ErrorType::GeneratorBrandMismatch, async_generator->generator_brand().value_or("emp"_string), generator_brand.value_or("emp"_string));
|
||||
|
||||
// 5. Return unused.
|
||||
return async_generator;
|
||||
return *async_generator;
|
||||
}
|
||||
|
||||
// 27.6.1.2 AsyncGenerator.prototype.next ( value ), https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ static ThrowCompletionOr<GC::Ref<BigInt>> this_bigint_value(VM& vm, Value value)
|
|||
return value.as_bigint();
|
||||
|
||||
// 2. If value is an Object and value has a [[BigIntData]] internal slot, then
|
||||
if (value.is_object() && is<BigIntObject>(value.as_object())) {
|
||||
if (auto bigint = value.as_if<BigIntObject>()) {
|
||||
// a. Assert: value.[[BigIntData]] is a BigInt.
|
||||
// b. Return value.[[BigIntData]].
|
||||
return static_cast<BigIntObject&>(value.as_object()).bigint();
|
||||
return bigint->bigint();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ static ThrowCompletionOr<bool> this_boolean_value(VM& vm, Value value)
|
|||
return value.as_bool();
|
||||
|
||||
// 2. If value is an Object and value has a [[BooleanData]] internal slot, then
|
||||
if (value.is_object() && is<BooleanObject>(value.as_object())) {
|
||||
if (auto boolean = value.as_if<BooleanObject>()) {
|
||||
// a. Let b be value.[[BooleanData]].
|
||||
// b. Assert: b is a Boolean.
|
||||
// c. Return b.
|
||||
return static_cast<BooleanObject&>(value.as_object()).boolean();
|
||||
return boolean->boolean();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
|
|
|
|||
|
|
@ -21,11 +21,8 @@ namespace JS {
|
|||
|
||||
static void update_function_name(Value value, Utf16FlyString const& name)
|
||||
{
|
||||
if (!value.is_function())
|
||||
return;
|
||||
auto& function = value.as_function();
|
||||
if (is<ECMAScriptFunctionObject>(function) && static_cast<ECMAScriptFunctionObject const&>(function).name().is_empty())
|
||||
static_cast<ECMAScriptFunctionObject&>(function).set_name(name);
|
||||
if (auto function = value.as_if<ECMAScriptFunctionObject>(); function && function->name().is_empty())
|
||||
function->set_name(name);
|
||||
}
|
||||
|
||||
static ThrowCompletionOr<ClassElementName> resolve_element_key(VM& vm, Bytecode::ClassElementDescriptor const& descriptor, Value property_key)
|
||||
|
|
|
|||
|
|
@ -38,10 +38,7 @@ namespace JS {
|
|||
auto&& _temporary_result = (__VA_ARGS__)); \
|
||||
if (_temporary_result.is_error()) { \
|
||||
auto _completion = _temporary_result.release_error(); \
|
||||
\
|
||||
VERIFY(_completion.value().is_object()); \
|
||||
VERIFY(::AK::is<JS::InternalError>(_completion.value().as_object())); \
|
||||
\
|
||||
VERIFY(_completion.value().is<JS::InternalError>()); \
|
||||
return _completion; \
|
||||
} \
|
||||
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
|
||||
|
|
|
|||
|
|
@ -46,32 +46,30 @@ ThrowCompletionOr<GC::Ref<Object>> DataViewConstructor::construct(FunctionObject
|
|||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
auto buffer = vm.argument(0);
|
||||
auto buffer = vm.argument(0).as_if<ArrayBuffer>();
|
||||
auto byte_offset = vm.argument(1);
|
||||
auto byte_length = vm.argument(2);
|
||||
|
||||
// 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).
|
||||
if (!buffer.is_object() || !is<ArrayBuffer>(buffer.as_object()))
|
||||
if (!buffer)
|
||||
return vm.throw_completion<TypeError>(ErrorType::IsNotAn, buffer, vm.names.ArrayBuffer);
|
||||
|
||||
auto& array_buffer = static_cast<ArrayBuffer&>(buffer.as_object());
|
||||
|
||||
// 3. Let offset be ? ToIndex(byteOffset).
|
||||
auto offset = TRY(byte_offset.to_index(vm));
|
||||
|
||||
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (array_buffer.is_detached())
|
||||
if (buffer->is_detached())
|
||||
return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
|
||||
|
||||
// 5. Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).
|
||||
auto buffer_byte_length = array_buffer_byte_length(array_buffer, ArrayBuffer::Order::SeqCst);
|
||||
auto buffer_byte_length = array_buffer_byte_length(*buffer, ArrayBuffer::Order::SeqCst);
|
||||
|
||||
// 6. If offset > bufferByteLength, throw a RangeError exception.
|
||||
if (offset > buffer_byte_length)
|
||||
return vm.throw_completion<RangeError>(ErrorType::DataViewOutOfRangeByteOffset, offset, buffer_byte_length);
|
||||
|
||||
// 7. Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).
|
||||
auto buffer_is_fixed_length = array_buffer.is_fixed_length();
|
||||
auto buffer_is_fixed_length = buffer->is_fixed_length();
|
||||
|
||||
ByteLength view_byte_length { 0 };
|
||||
|
||||
|
|
@ -101,14 +99,14 @@ ThrowCompletionOr<GC::Ref<Object>> DataViewConstructor::construct(FunctionObject
|
|||
}
|
||||
|
||||
// 10. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).
|
||||
auto data_view = TRY(ordinary_create_from_constructor<DataView>(vm, new_target, &Intrinsics::data_view_prototype, &array_buffer, move(view_byte_length), offset));
|
||||
auto data_view = TRY(ordinary_create_from_constructor<DataView>(vm, new_target, &Intrinsics::data_view_prototype, buffer, move(view_byte_length), offset));
|
||||
|
||||
// 11. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (array_buffer.is_detached())
|
||||
if (buffer->is_detached())
|
||||
return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
|
||||
|
||||
// 12. Set bufferByteLength to ArrayBufferByteLength(buffer, seq-cst).
|
||||
buffer_byte_length = array_buffer_byte_length(array_buffer, ArrayBuffer::Order::SeqCst);
|
||||
buffer_byte_length = array_buffer_byte_length(*buffer, ArrayBuffer::Order::SeqCst);
|
||||
|
||||
// 13. If offset > bufferByteLength, throw a RangeError exception.
|
||||
if (offset > buffer_byte_length)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ ThrowCompletionOr<GC::Ref<Object>> DateConstructor::construct(FunctionObject& ne
|
|||
double time_value;
|
||||
|
||||
// b. If Type(value) is Object and value has a [[DateValue]] internal slot, then
|
||||
if (value.is_object() && is<Date>(value.as_object())) {
|
||||
if (value.is<Date>()) {
|
||||
// i. Let tv be ! thisTimeValue(value).
|
||||
time_value = MUST(this_time_value(vm, value));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,9 +106,9 @@ void DatePrototype::initialize(Realm& realm)
|
|||
ThrowCompletionOr<double> this_time_value(VM& vm, Value value)
|
||||
{
|
||||
// 1. If Type(value) is Object and value has a [[DateValue]] internal slot, then
|
||||
if (value.is_object() && is<Date>(value.as_object())) {
|
||||
if (auto date = value.as_if<Date>()) {
|
||||
// a. Return value.[[DateValue]].
|
||||
return static_cast<Date&>(value.as_object()).date_value();
|
||||
return date->date_value();
|
||||
}
|
||||
|
||||
// 2. Throw a TypeError exception.
|
||||
|
|
|
|||
|
|
@ -126,12 +126,10 @@ JS_ENUMERATE_NATIVE_ERRORS
|
|||
// 20.5.2.1 Error.isError ( arg ), https://tc39.es/ecma262/#sec-error.iserror
|
||||
JS_DEFINE_NATIVE_FUNCTION(ErrorConstructor::is_error)
|
||||
{
|
||||
auto arg = vm.argument(0);
|
||||
|
||||
// 1. If arg is not an Object, return false.
|
||||
// 2. If arg does not have an [[ErrorData]] internal slot, return false.
|
||||
// 3. Return true.
|
||||
return arg.is_object() && is<Error>(arg.as_object());
|
||||
return vm.argument(0).is<Error>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,36 +78,35 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::stack_getter)
|
|||
auto this_object = TRY(PrototypeObject::this_object(vm));
|
||||
|
||||
// 3. If E does not have an [[ErrorData]] internal slot, return undefined.
|
||||
if (!is<Error>(*this_object))
|
||||
auto* error = as_if<Error>(*this_object);
|
||||
if (!error)
|
||||
return js_undefined();
|
||||
|
||||
auto& error = static_cast<Error&>(*this_object);
|
||||
|
||||
// OPTIMIZATION: Avoid recomputing the stack string if we already have it cached.
|
||||
// At least one major engine does this as well, so it's not expected that changing
|
||||
// the name or message properties updates the stack string.
|
||||
if (error.cached_string())
|
||||
return error.cached_string();
|
||||
if (error->cached_string())
|
||||
return error->cached_string();
|
||||
|
||||
// 4. Return ? GetStackString(error).
|
||||
// NOTE: These steps are not implemented based on the proposal, but to roughly follow behavior of other browsers.
|
||||
|
||||
String name {};
|
||||
if (auto name_property = TRY(error.get(vm.names.name)); !name_property.is_undefined())
|
||||
if (auto name_property = TRY(error->get(vm.names.name)); !name_property.is_undefined())
|
||||
name = TRY(name_property.to_string(vm));
|
||||
else
|
||||
name = "Error"_string;
|
||||
|
||||
Utf16String message {};
|
||||
if (auto message_property = TRY(error.get(vm.names.message)); !message_property.is_undefined())
|
||||
if (auto message_property = TRY(error->get(vm.names.message)); !message_property.is_undefined())
|
||||
message = TRY(message_property.to_utf16_string(vm));
|
||||
|
||||
auto header = message.is_empty()
|
||||
? move(name)
|
||||
: MUST(String::formatted("{}: {}", name, message));
|
||||
|
||||
auto string = PrimitiveString::create(vm, Utf16String::formatted("{}\n{}", header, error.stack_string()));
|
||||
error.set_cached_string(string);
|
||||
auto string = PrimitiveString::create(vm, Utf16String::formatted("{}\n{}", header, error->stack_string()));
|
||||
error->set_cached_string(string);
|
||||
return string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,9 +53,11 @@ GC::Ref<PrimitiveString> FunctionObject::make_function_name(Variant<PropertyKey,
|
|||
}
|
||||
|
||||
// 4. If F has an [[InitialName]] internal slot, then
|
||||
if (is<NativeFunction>(this)) {
|
||||
auto* native_function = as_if<NativeFunction>(this);
|
||||
|
||||
if (native_function) {
|
||||
// a. Set F.[[InitialName]] to name.
|
||||
static_cast<NativeFunction&>(*this).set_initial_name({}, name);
|
||||
native_function->set_initial_name({}, name);
|
||||
}
|
||||
|
||||
// 5. If prefix is present, then
|
||||
|
|
@ -64,9 +66,9 @@ GC::Ref<PrimitiveString> FunctionObject::make_function_name(Variant<PropertyKey,
|
|||
name = Utf16String::formatted("{} {}", *prefix, name);
|
||||
|
||||
// b. If F has an [[InitialName]] internal slot, then
|
||||
if (is<NativeFunction>(this)) {
|
||||
if (native_function) {
|
||||
// i. Optionally, set F.[[InitialName]] to name.
|
||||
static_cast<NativeFunction&>(*this).set_initial_name({}, name);
|
||||
native_function->set_initial_name({}, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,16 +166,15 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string)
|
|||
auto& function = function_value.as_function();
|
||||
|
||||
// 2. If Type(func) is Object and func has a [[SourceText]] internal slot and func.[[SourceText]] is a sequence of Unicode code points and HostHasSourceTextAvailable(func) is true, then
|
||||
if (is<ECMAScriptFunctionObject>(function)) {
|
||||
if (auto const* ecma_script_function_object = as_if<ECMAScriptFunctionObject>(function)) {
|
||||
// a. Return CodePointsToString(func.[[SourceText]]).
|
||||
return PrimitiveString::create(vm, static_cast<ECMAScriptFunctionObject&>(function).source_text());
|
||||
return PrimitiveString::create(vm, ecma_script_function_object->source_text());
|
||||
}
|
||||
|
||||
// 3. If func is a built-in function object, return an implementation-defined String source code representation of func. The representation must have the syntax of a NativeFunction. Additionally, if func has an [[InitialName]] internal slot and func.[[InitialName]] is a String, the portion of the returned String that would be matched by NativeFunctionAccessor[opt] PropertyName must be the value of func.[[InitialName]].
|
||||
if (is<NativeFunction>(function)) {
|
||||
if (auto const* native_function = as_if<NativeFunction>(function)) {
|
||||
// NOTE: once we remove name(), the fallback here can simply be an empty string.
|
||||
auto const& native_function = static_cast<NativeFunction&>(function);
|
||||
auto const name = native_function.initial_name().value_or(native_function.name());
|
||||
auto const name = native_function->initial_name().value_or(native_function->name());
|
||||
return PrimitiveString::create(vm, ByteString::formatted("function {}() {{ [native code] }}", name));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,14 +185,14 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
|
|||
value = TRY(value.to_primitive_string(vm));
|
||||
}
|
||||
// d. Else if value has a [[BooleanData]] internal slot, then
|
||||
else if (is<BooleanObject>(value_object)) {
|
||||
else if (auto const* boolean = as_if<BooleanObject>(value_object)) {
|
||||
// i. Set value to value.[[BooleanData]].
|
||||
value = Value(static_cast<BooleanObject&>(value_object).boolean());
|
||||
value = Value { boolean->boolean() };
|
||||
}
|
||||
// e. Else if value has a [[BigIntData]] internal slot, then
|
||||
else if (is<BigIntObject>(value_object)) {
|
||||
else if (auto const* bigint = as_if<BigIntObject>(value_object)) {
|
||||
// i. Set value to value.[[BigIntData]].
|
||||
value = Value(&static_cast<BigIntObject&>(value_object).bigint());
|
||||
value = Value { &bigint->bigint() };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -912,11 +912,8 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::raw_json)
|
|||
JS_DEFINE_NATIVE_FUNCTION(JSONObject::is_raw_json)
|
||||
{
|
||||
// 1. If Type(O) is Object and O has an [[IsRawJSON]] internal slot, return true.
|
||||
if (vm.argument(0).is_object() && is<RawJSONObject>(vm.argument(0).as_object()))
|
||||
return Value(true);
|
||||
|
||||
// 2. Return false.
|
||||
return Value(false);
|
||||
return vm.argument(0).is<RawJSONObject>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,11 +79,11 @@ static ThrowCompletionOr<Value> this_number_value(VM& vm, Value value)
|
|||
return value;
|
||||
|
||||
// 2. If Type(value) is Object and value has a [[NumberData]] internal slot, then
|
||||
if (value.is_object() && is<NumberObject>(value.as_object())) {
|
||||
if (auto number = value.as_if<NumberObject>()) {
|
||||
// a. Let n be value.[[NumberData]].
|
||||
// b. Assert: Type(n) is Number.
|
||||
// c. Return n.
|
||||
return Value(static_cast<NumberObject&>(value.as_object()).number());
|
||||
return number->number();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ GC_DEFINE_ALLOCATOR(Promise);
|
|||
ThrowCompletionOr<Object*> promise_resolve(VM& vm, Object& constructor, Value value)
|
||||
{
|
||||
// 1. If IsPromise(x) is true, then
|
||||
if (value.is_object() && is<Promise>(value.as_object())) {
|
||||
if (auto promise = value.as_if<Promise>()) {
|
||||
// a. Let xConstructor be ? Get(x, "constructor").
|
||||
auto value_constructor = TRY(value.as_object().get(vm.names.constructor));
|
||||
|
||||
// b. If SameValue(xConstructor, C) is true, return x.
|
||||
if (same_value(value_constructor, &constructor))
|
||||
return &static_cast<Promise&>(value.as_object());
|
||||
return promise.ptr();
|
||||
}
|
||||
|
||||
// 2. Let promiseCapability be ? NewPromiseCapability(C).
|
||||
|
|
|
|||
|
|
@ -42,18 +42,18 @@ public:
|
|||
static ThrowCompletionOr<GC::Ref<ObjectType>> typed_this_object(VM& vm)
|
||||
{
|
||||
auto this_object = TRY(vm.this_value().to_object(vm));
|
||||
if (!is<ObjectType>(*this_object))
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, PrototypeType::display_name());
|
||||
return static_cast<ObjectType&>(*this_object);
|
||||
if (auto* typed_object = as_if<ObjectType>(*this_object))
|
||||
return *typed_object;
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, PrototypeType::display_name());
|
||||
}
|
||||
|
||||
// Use typed_this_value() when the spec does not coerce |this| value to an object.
|
||||
static ThrowCompletionOr<GC::Ref<ObjectType>> typed_this_value(VM& vm)
|
||||
{
|
||||
auto this_value = vm.this_value();
|
||||
if (!this_value.is_object() || !is<ObjectType>(this_value.as_object()))
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, PrototypeType::display_name());
|
||||
return static_cast<ObjectType&>(this_value.as_object());
|
||||
if (auto typed_object = this_value.as_if<ObjectType>())
|
||||
return *typed_object;
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, PrototypeType::display_name());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -104,14 +104,13 @@ ThrowCompletionOr<GC::Ref<Object>> RegExpConstructor::construct(FunctionObject&
|
|||
Value flags_value;
|
||||
|
||||
// 4. If pattern is an Object and pattern has a [[RegExpMatcher]] internal slot, then
|
||||
if (pattern.is_object() && is<RegExpObject>(pattern.as_object())) {
|
||||
if (auto regexp_pattern = pattern.as_if<RegExpObject>()) {
|
||||
// a. Let P be pattern.[[OriginalSource]].
|
||||
auto& regexp_pattern = static_cast<RegExpObject&>(pattern.as_object());
|
||||
pattern_value = PrimitiveString::create(vm, regexp_pattern.pattern());
|
||||
pattern_value = PrimitiveString::create(vm, regexp_pattern->pattern());
|
||||
|
||||
// b. If flags is undefined, let F be pattern.[[OriginalFlags]].
|
||||
if (flags.is_undefined())
|
||||
flags_value = PrimitiveString::create(vm, regexp_pattern.flags());
|
||||
flags_value = PrimitiveString::create(vm, regexp_pattern->flags());
|
||||
// c. Else, let F be flags.
|
||||
else
|
||||
flags_value = flags;
|
||||
|
|
|
|||
|
|
@ -462,11 +462,9 @@ ThrowCompletionOr<Value> regexp_exec(VM& vm, Object& regexp_object, GC::Ref<Prim
|
|||
auto* typed_regexp_object = as_if<RegExpObject>(regexp_object);
|
||||
|
||||
// 2. If IsCallable(exec) is true, then
|
||||
if (exec.is_function()) {
|
||||
auto& exec_function = exec.as_function();
|
||||
if (typed_regexp_object && exec_function.builtin() == Bytecode::Builtin::RegExpPrototypeExec) {
|
||||
if (auto exec_function = exec.as_if<FunctionObject>()) {
|
||||
if (typed_regexp_object && exec_function->builtin() == Bytecode::Builtin::RegExpPrototypeExec)
|
||||
return regexp_builtin_exec(vm, *typed_regexp_object, string);
|
||||
}
|
||||
|
||||
// a. Let result be ? Call(exec, R, « S »).
|
||||
auto result = TRY(call(vm, exec_function, ®exp_object, string));
|
||||
|
|
@ -1209,18 +1207,16 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::compile)
|
|||
return vm.throw_completion<TypeError>(ErrorType::RegExpCompileError, "legacy features is not enabled");
|
||||
|
||||
// 7. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot, then
|
||||
if (pattern.is_object() && is<RegExpObject>(pattern.as_object())) {
|
||||
if (auto regexp_pattern = pattern.as_if<RegExpObject>()) {
|
||||
// a. If flags is not undefined, throw a TypeError exception.
|
||||
if (!flags.is_undefined())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotUndefined, flags);
|
||||
|
||||
auto& regexp_pattern = static_cast<RegExpObject&>(pattern.as_object());
|
||||
|
||||
// b. Let P be pattern.[[OriginalSource]].
|
||||
pattern = PrimitiveString::create(vm, regexp_pattern.pattern());
|
||||
pattern = PrimitiveString::create(vm, regexp_pattern->pattern());
|
||||
|
||||
// c. Let F be pattern.[[OriginalFlags]].
|
||||
flags = PrimitiveString::create(vm, regexp_pattern.flags());
|
||||
flags = PrimitiveString::create(vm, regexp_pattern->flags());
|
||||
}
|
||||
// 8. Else,
|
||||
// a. Let P be pattern.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ ThrowCompletionOr<GC::Ref<Object>> SharedArrayBufferConstructor::construct(Funct
|
|||
|
||||
if (byte_length_or_error.is_error()) {
|
||||
auto error = byte_length_or_error.release_error();
|
||||
if (error.value().is_object() && is<RangeError>(error.value().as_object())) {
|
||||
if (error.value().is<RangeError>()) {
|
||||
// Re-throw more specific RangeError
|
||||
return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "shared array buffer");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,9 +203,9 @@ JS_DEFINE_NATIVE_FUNCTION(SharedArrayBufferPrototype::slice)
|
|||
auto new_array_buffer = TRY(construct(vm, *constructor, Value(new_length)));
|
||||
|
||||
// 16. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
|
||||
if (!is<ArrayBuffer>(new_array_buffer.ptr()))
|
||||
auto* new_array_buffer_object = as_if<ArrayBuffer>(*new_array_buffer);
|
||||
if (!new_array_buffer_object)
|
||||
return vm.throw_completion<TypeError>(ErrorType::SpeciesConstructorDidNotCreate, "an ArrayBuffer");
|
||||
auto* new_array_buffer_object = static_cast<ArrayBuffer*>(new_array_buffer.ptr());
|
||||
|
||||
// 17. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
|
||||
if (!new_array_buffer_object->is_shared_array_buffer())
|
||||
|
|
|
|||
|
|
@ -78,9 +78,7 @@ SharedFunctionInstanceData::SharedFunctionInstanceData(
|
|||
// and then reused in all subsequent function instantiations.
|
||||
|
||||
// 2. Let code be func.[[ECMAScriptCode]].
|
||||
ScopeNode const* scope_body = nullptr;
|
||||
if (is<ScopeNode>(*m_ecmascript_code))
|
||||
scope_body = static_cast<ScopeNode const*>(m_ecmascript_code.ptr());
|
||||
auto const* scope_body = as_if<ScopeNode>(*m_ecmascript_code);
|
||||
m_has_scope_body = scope_body != nullptr;
|
||||
|
||||
// 3. Let strict be func.[[Strict]].
|
||||
|
|
|
|||
|
|
@ -244,11 +244,11 @@ static ThrowCompletionOr<GC::Ref<PrimitiveString>> this_string_value(VM& vm, Val
|
|||
return value.as_string();
|
||||
|
||||
// 2. If value is an Object and value has a [[StringData]] internal slot, then
|
||||
if (value.is_object() && is<StringObject>(value.as_object())) {
|
||||
if (auto string = value.as_if<StringObject>()) {
|
||||
// a. Let s be value.[[StringData]].
|
||||
// b. Assert: s is a String.
|
||||
// c. Return s.
|
||||
return static_cast<StringObject&>(value.as_object()).primitive_string();
|
||||
return string->primitive_string();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ static ThrowCompletionOr<GC::Ref<Symbol>> this_symbol_value(VM& vm, Value value)
|
|||
return value.as_symbol();
|
||||
|
||||
// 2. If value is an Object and value has a [[SymbolData]] internal slot, then
|
||||
if (value.is_object() && is<SymbolObject>(value.as_object())) {
|
||||
if (auto symbol = value.as_if<SymbolObject>()) {
|
||||
// a. Let s be value.[[SymbolData]].
|
||||
// b. Assert: s is a Symbol.
|
||||
// c. Return s.
|
||||
return static_cast<SymbolObject&>(value.as_object()).primitive_symbol();
|
||||
return symbol->primitive_symbol();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
|
|
|
|||
|
|
@ -558,18 +558,14 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
|
|||
if (first_argument.as_object().is_typed_array()) { \
|
||||
auto& arg_typed_array = static_cast<TypedArrayBase&>(first_argument.as_object()); \
|
||||
TRY(initialize_typed_array_from_typed_array(vm, *typed_array, arg_typed_array)); \
|
||||
} else if (is<ArrayBuffer>(first_argument.as_object())) { \
|
||||
auto& array_buffer = static_cast<ArrayBuffer&>(first_argument.as_object()); \
|
||||
TRY(initialize_typed_array_from_array_buffer(vm, *typed_array, array_buffer, \
|
||||
} else if (auto* array_buffer = as_if<ArrayBuffer>(first_argument.as_object())) { \
|
||||
TRY(initialize_typed_array_from_array_buffer(vm, *typed_array, *array_buffer, \
|
||||
vm.argument(1), vm.argument(2))); \
|
||||
} else if (auto iterator = TRY(first_argument.get_method(vm, vm.well_known_symbol_iterator()))) { \
|
||||
auto values = TRY(iterator_to_list(vm, TRY(get_iterator_from_method(vm, first_argument, *iterator)))); \
|
||||
TRY(initialize_typed_array_from_list(vm, *typed_array, values)); \
|
||||
} else { \
|
||||
auto iterator = TRY(first_argument.get_method(vm, vm.well_known_symbol_iterator())); \
|
||||
if (iterator) { \
|
||||
auto values = TRY(iterator_to_list(vm, TRY(get_iterator_from_method(vm, first_argument, *iterator)))); \
|
||||
TRY(initialize_typed_array_from_list(vm, *typed_array, values)); \
|
||||
} else { \
|
||||
TRY(initialize_typed_array_from_array_like(vm, *typed_array, first_argument.as_object())); \
|
||||
} \
|
||||
TRY(initialize_typed_array_from_array_like(vm, *typed_array, first_argument.as_object())); \
|
||||
} \
|
||||
return typed_array; \
|
||||
} \
|
||||
|
|
|
|||
|
|
@ -1609,11 +1609,9 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set)
|
|||
return vm.throw_completion<RangeError>(ErrorType::TypedArrayInvalidTargetOffset, "positive");
|
||||
|
||||
// 6. If source is an Object that has a [[TypedArrayName]] internal slot, then
|
||||
if (source.is_object() && is<TypedArrayBase>(source.as_object())) {
|
||||
auto& source_typed_array = static_cast<TypedArrayBase&>(source.as_object());
|
||||
|
||||
if (auto source_typed_array = source.as_if<TypedArrayBase>()) {
|
||||
// a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source).
|
||||
TRY(set_typed_array_from_typed_array(vm, *typed_array, target_offset, source_typed_array));
|
||||
TRY(set_typed_array_from_typed_array(vm, *typed_array, target_offset, *source_typed_array));
|
||||
}
|
||||
// 7. Else,
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -255,8 +255,7 @@ ThrowCompletionOr<bool> Value::is_array(VM& vm) const
|
|||
|
||||
Array& Value::as_array()
|
||||
{
|
||||
ASSERT(is_object() && is<Array>(as_object()));
|
||||
return static_cast<Array&>(as_object());
|
||||
return *as_if<Array>();
|
||||
}
|
||||
|
||||
// 7.2.3 IsCallable ( argument ), https://tc39.es/ecma262/#sec-iscallable
|
||||
|
|
@ -270,14 +269,12 @@ bool Value::is_function() const
|
|||
|
||||
FunctionObject& Value::as_function()
|
||||
{
|
||||
ASSERT(is_function());
|
||||
return static_cast<FunctionObject&>(as_object());
|
||||
return *as_if<FunctionObject>();
|
||||
}
|
||||
|
||||
FunctionObject const& Value::as_function() const
|
||||
{
|
||||
ASSERT(is_function());
|
||||
return static_cast<FunctionObject const&>(as_object());
|
||||
return *as_if<FunctionObject>();
|
||||
}
|
||||
|
||||
// 7.2.4 IsConstructor ( argument ), https://tc39.es/ecma262/#sec-isconstructor
|
||||
|
|
@ -2183,12 +2180,10 @@ ThrowCompletionOr<Value> ordinary_has_instance(VM& vm, Value lhs, Value rhs)
|
|||
auto& rhs_function = rhs.as_function();
|
||||
|
||||
// 2. If C has a [[BoundTargetFunction]] internal slot, then
|
||||
if (is<BoundFunction>(rhs_function)) {
|
||||
auto const& bound_target = static_cast<BoundFunction const&>(rhs_function);
|
||||
|
||||
if (auto const* bound_target = as_if<BoundFunction>(rhs_function)) {
|
||||
// a. Let BC be C.[[BoundTargetFunction]].
|
||||
// b. Return ? InstanceofOperator(O, BC).
|
||||
return instance_of(vm, lhs, Value(&bound_target.bound_target_function()));
|
||||
return instance_of(vm, lhs, Value(&bound_target->bound_target_function()));
|
||||
}
|
||||
|
||||
// 3. If O is not an Object, return false.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue