LibJS: Make more use of Value::is and Value::as_if in JS runtime

This commit is contained in:
Timothy Flynn 2026-02-27 10:05:55 -05:00
parent e450f2ea16
commit 4287da7d3a
31 changed files with 109 additions and 145 deletions

View file

@ -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)

View file

@ -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

View file

@ -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())

View file

@ -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));

View file

@ -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 {};

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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)

View file

@ -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())>, \

View file

@ -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)

View file

@ -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));
}

View file

@ -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.

View file

@ -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>();
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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));
}

View file

@ -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>();
}
}

View file

@ -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.

View file

@ -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).

View file

@ -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:

View file

@ -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;

View file

@ -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, &regexp_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.

View file

@ -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");
}

View file

@ -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())

View file

@ -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]].

View file

@ -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.

View file

@ -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.

View file

@ -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; \
} \

View file

@ -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 {

View file

@ -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.