LibJS: Move control and binding handlers into AsmInt
Move the remaining control-flow, conversion, creation, delete, binding, private-name and environment-related fallback handlers into AsmInt. This keeps the generic fallback path shrinking while leaving complex behavior in C++ slow paths.
This commit is contained in:
parent
fff128a8cc
commit
058f63efd2
3 changed files with 869 additions and 728 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -438,17 +438,18 @@ macro dispatch_current()
|
|||
end
|
||||
|
||||
# Walk the environment chain using a statically computed EnvironmentCoordinate.
|
||||
# Input: m_cache_field is the offset of the EnvironmentCoordinate inside
|
||||
# the bytecode instruction.
|
||||
# Input: m_environment_field is the offset of the starting environment inside
|
||||
# ExecutionContext. m_cache_field is the offset of the EnvironmentCoordinate
|
||||
# inside the bytecode instruction.
|
||||
# Output: target_env points at the resolved environment, bind_index holds
|
||||
# the binding index within it.
|
||||
# On failure (the caller's binding operation fails): jumps to fail_label.
|
||||
macro walk_env_chain(m_cache_field, target_env, bind_index, fail_label)
|
||||
macro walk_env_chain(m_environment_field, m_cache_field, target_env, bind_index, fail_label)
|
||||
temp coord_addr, hops
|
||||
lea coord_addr, [pb, pc]
|
||||
add coord_addr, m_cache_field
|
||||
load_pair32 hops, bind_index, [coord_addr, ENVIRONMENT_COORDINATE_HOPS], [coord_addr, ENVIRONMENT_COORDINATE_INDEX]
|
||||
load64 target_env, [exec_ctx, EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT]
|
||||
load64 target_env, [exec_ctx, m_environment_field]
|
||||
assert_nonzero target_env
|
||||
branch_zero hops, .walk_done
|
||||
.walk_loop:
|
||||
|
|
@ -1121,7 +1122,7 @@ end
|
|||
# Inline environment chain walk + binding value load with TDZ check.
|
||||
handler GetBinding
|
||||
temp env, idx, binding_values, value, empty
|
||||
walk_env_chain m_cache, env, idx, .slow
|
||||
walk_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
store_operand m_dst, value
|
||||
dispatch_next
|
||||
|
|
@ -1142,7 +1143,7 @@ end
|
|||
# Inline environment chain walk + direct binding value load.
|
||||
handler GetInitializedBinding
|
||||
temp env, idx, binding_values, value
|
||||
walk_env_chain m_cache, env, idx, .slow
|
||||
walk_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
load_binding_value env, idx, binding_values, value
|
||||
store_operand m_dst, value
|
||||
dispatch_next
|
||||
|
|
@ -1163,7 +1164,7 @@ end
|
|||
# Inline environment chain walk + initialize binding.
|
||||
handler InitializeLexicalBinding
|
||||
temp env, idx, value, binding_values
|
||||
walk_env_chain m_cache, env, idx, .slow
|
||||
walk_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
|
|
@ -1171,6 +1172,16 @@ handler InitializeLexicalBinding
|
|||
call_slow_path asm_slow_path_initialize_lexical_binding
|
||||
end
|
||||
|
||||
handler InitializeVariableBinding
|
||||
temp env, idx, value, binding_values
|
||||
walk_env_chain EXECUTION_CONTEXT_VARIABLE_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_initialize_variable_binding
|
||||
end
|
||||
|
||||
handler DynamicInitializeLexicalBinding
|
||||
temp env, idx, value, binding_values
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
|
|
@ -1194,7 +1205,7 @@ end
|
|||
# Inline environment chain walk + set mutable binding.
|
||||
handler SetLexicalBinding
|
||||
temp env, idx, flag, value, binding_values, flags, empty
|
||||
walk_env_chain m_cache, env, idx, .slow
|
||||
walk_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
# Check mutable
|
||||
load_binding_flags env, idx, flags, flag
|
||||
|
|
@ -1207,6 +1218,21 @@ handler SetLexicalBinding
|
|||
call_slow_path asm_slow_path_set_lexical_binding
|
||||
end
|
||||
|
||||
handler SetVariableBinding
|
||||
temp env, idx, flag, value, binding_values, flags, empty
|
||||
walk_env_chain EXECUTION_CONTEXT_VARIABLE_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
# Check mutable
|
||||
load_binding_flags env, idx, flags, flag
|
||||
and flag, BINDING_FLAG_MUTABLE
|
||||
branch_zero flag, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_set_variable_binding
|
||||
end
|
||||
|
||||
handler DynamicSetLexicalBinding
|
||||
temp env, idx, flag, value, binding_values, flags, empty
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
|
|
@ -1237,6 +1263,50 @@ handler DynamicSetVariableBinding
|
|||
call_slow_path asm_slow_path_dynamic_set_variable_binding
|
||||
end
|
||||
|
||||
handler ResolveBinding
|
||||
call_slow_path asm_slow_path_resolve_binding
|
||||
end
|
||||
|
||||
handler ResolveSuperBase
|
||||
call_slow_path asm_slow_path_resolve_super_base
|
||||
end
|
||||
|
||||
handler SetResolvedBinding
|
||||
call_slow_path asm_slow_path_set_resolved_binding
|
||||
end
|
||||
|
||||
handler TypeofBinding
|
||||
call_slow_path asm_slow_path_typeof_binding
|
||||
end
|
||||
|
||||
handler DynamicTypeofBinding
|
||||
call_slow_path asm_slow_path_dynamic_typeof_binding
|
||||
end
|
||||
|
||||
handler HasPrivateId
|
||||
call_slow_path asm_slow_path_has_private_id
|
||||
end
|
||||
|
||||
handler SetFunctionName
|
||||
call_slow_path asm_slow_path_set_function_name
|
||||
end
|
||||
|
||||
handler NewArrayWithLength
|
||||
call_slow_path asm_slow_path_new_array_with_length
|
||||
end
|
||||
|
||||
handler ArrayAppend
|
||||
call_slow_path asm_slow_path_array_append
|
||||
end
|
||||
|
||||
handler CreateVariable
|
||||
call_slow_path asm_slow_path_create_variable
|
||||
end
|
||||
|
||||
handler EnterObjectEnvironment
|
||||
call_slow_path asm_slow_path_enter_object_environment
|
||||
end
|
||||
|
||||
# x++: save original to dst first, then increment src in-place.
|
||||
handler PostfixIncrement
|
||||
temp value, tag, int_value, dst
|
||||
|
|
@ -1410,6 +1480,42 @@ handler ThrowIfNullish
|
|||
call_slow_path asm_slow_path_throw_if_nullish
|
||||
end
|
||||
|
||||
handler ThrowConstAssignment
|
||||
call_slow_path asm_slow_path_throw_const_assignment
|
||||
end
|
||||
|
||||
handler Throw
|
||||
call_slow_path asm_slow_path_throw
|
||||
end
|
||||
|
||||
handler Await
|
||||
call_slow_path asm_slow_path_await
|
||||
end
|
||||
|
||||
handler Yield
|
||||
call_slow_path asm_slow_path_yield
|
||||
end
|
||||
|
||||
handler YieldIteratorResult
|
||||
call_slow_path asm_slow_path_yield_iterator_result
|
||||
end
|
||||
|
||||
handler ToString
|
||||
call_slow_path asm_slow_path_to_string
|
||||
end
|
||||
|
||||
handler ToPrimitiveWithStringHint
|
||||
call_slow_path asm_slow_path_to_primitive_with_string_hint
|
||||
end
|
||||
|
||||
handler ToObject
|
||||
call_slow_path asm_slow_path_to_object
|
||||
end
|
||||
|
||||
handler ToLength
|
||||
call_slow_path asm_slow_path_to_length
|
||||
end
|
||||
|
||||
# Fast path for int32: ~value
|
||||
handler BitwiseNot
|
||||
temp value, tag, dst
|
||||
|
|
@ -1554,7 +1660,7 @@ end
|
|||
# Inline environment chain walk + get callee and this.
|
||||
handler GetCalleeAndThisFromEnvironment
|
||||
temp env, idx, binding_values, value, empty
|
||||
walk_env_chain m_cache, env, idx, .slow
|
||||
walk_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, m_cache, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
store_operand m_callee, value
|
||||
# this = undefined (DeclarativeEnvironment.with_base_object() always returns nullptr)
|
||||
|
|
@ -3076,6 +3182,18 @@ handler CreateAsyncFromSyncIterator
|
|||
call_slow_path asm_slow_path_create_async_from_sync_iterator
|
||||
end
|
||||
|
||||
handler CreateDataPropertyOrThrow
|
||||
call_slow_path asm_slow_path_create_data_property_or_throw
|
||||
end
|
||||
|
||||
handler CreateImmutableBinding
|
||||
call_slow_path asm_slow_path_create_immutable_binding
|
||||
end
|
||||
|
||||
handler CreateMutableBinding
|
||||
call_slow_path asm_slow_path_create_mutable_binding
|
||||
end
|
||||
|
||||
handler CreateRestParams
|
||||
call_slow_path asm_slow_path_create_rest_params
|
||||
end
|
||||
|
|
@ -3096,6 +3214,18 @@ handler CreateVariableEnvironment
|
|||
call_slow_path asm_slow_path_create_variable_environment
|
||||
end
|
||||
|
||||
handler DeleteById
|
||||
call_slow_path asm_slow_path_delete_by_id
|
||||
end
|
||||
|
||||
handler DeleteByValue
|
||||
call_slow_path asm_slow_path_delete_by_value
|
||||
end
|
||||
|
||||
handler DeleteVariable
|
||||
call_slow_path asm_slow_path_delete_variable
|
||||
end
|
||||
|
||||
handler GetCompletionFields
|
||||
call_slow_path asm_slow_path_get_completion_fields
|
||||
end
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include <LibJS/Runtime/MathObject.h>
|
||||
#include <LibJS/Runtime/ModuleEnvironment.h>
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibJS/Runtime/ObjectEnvironment.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/Reference.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
|
|
@ -925,64 +924,6 @@ static void update_environment_coordinate_cache(EnvironmentPointer environment,
|
|||
cache = candidate;
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<CalleeAndThis> dynamically_get_callee_and_this_from_environment(VM& vm, Utf16FlyString const& name, Strict strict, EnvironmentCoordinate& cache)
|
||||
{
|
||||
auto const* current_environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
if (auto const* environment = get_cached_environment(current_environment, cache)) [[likely]] {
|
||||
auto callee = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, cache.index));
|
||||
return CalleeAndThis {
|
||||
.callee = callee,
|
||||
.this_value = js_undefined(),
|
||||
};
|
||||
}
|
||||
|
||||
auto reference = TRY(vm.resolve_binding(name, strict));
|
||||
update_environment_coordinate_cache(current_environment, reference, cache);
|
||||
|
||||
auto callee = TRY(reference.get_value(vm));
|
||||
|
||||
Value this_value;
|
||||
if (reference.is_property_reference()) {
|
||||
this_value = reference.get_this_value();
|
||||
} else {
|
||||
if (reference.is_environment_reference()) {
|
||||
if (auto base_object = reference.base_environment().with_base_object()) [[unlikely]]
|
||||
this_value = base_object;
|
||||
}
|
||||
}
|
||||
|
||||
return CalleeAndThis {
|
||||
.callee = callee,
|
||||
.this_value = this_value,
|
||||
};
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<void> create_variable(VM& vm, Utf16FlyString const& name, Op::EnvironmentMode mode, bool is_global, bool is_immutable, bool is_strict)
|
||||
{
|
||||
if (mode == Op::EnvironmentMode::Lexical) {
|
||||
VERIFY(!is_global);
|
||||
|
||||
// Note: This is papering over an issue where "FunctionDeclarationInstantiation" creates these bindings for us.
|
||||
// Instead of crashing in there, we'll just raise an exception here.
|
||||
if (TRY(vm.lexical_environment()->has_binding(name))) [[unlikely]]
|
||||
return vm.throw_completion<InternalError>(TRY_OR_THROW_OOM(vm, String::formatted("Lexical environment already has binding '{}'", name)));
|
||||
|
||||
if (is_immutable)
|
||||
return vm.lexical_environment()->create_immutable_binding(vm, name, is_strict);
|
||||
return vm.lexical_environment()->create_mutable_binding(vm, name, is_strict);
|
||||
}
|
||||
|
||||
if (!is_global) {
|
||||
if (is_immutable)
|
||||
return vm.variable_environment()->create_immutable_binding(vm, name, is_strict);
|
||||
return vm.variable_environment()->create_mutable_binding(vm, name, is_strict);
|
||||
}
|
||||
|
||||
// NOTE: CreateVariable with m_is_global set to true is expected to only be used in GlobalDeclarationInstantiation currently, which only uses "false" for "can_be_deleted".
|
||||
// The only area that sets "can_be_deleted" to true is EvalDeclarationInstantiation, which is currently fully implemented in C++ and not in Bytecode.
|
||||
return as<GlobalEnvironment>(vm.variable_environment())->create_global_var_binding(name, false);
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<GC::Ref<Array>> iterator_to_array(VM& vm, Value iterator)
|
||||
{
|
||||
auto& iterator_record = static_cast<IteratorRecord&>(iterator.as_cell());
|
||||
|
|
@ -1000,63 +941,6 @@ inline ThrowCompletionOr<GC::Ref<Array>> iterator_to_array(VM& vm, Value iterato
|
|||
}
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<void> append(VM& vm, Value lhs, Value rhs, bool is_spread)
|
||||
{
|
||||
// Note: This OpCode is used to construct array literals and argument arrays for calls,
|
||||
// containing at least one spread element,
|
||||
// Iterating over such a spread element to unpack it has to be visible by
|
||||
// the user courtesy of
|
||||
// (1) https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
|
||||
// SpreadElement : ... AssignmentExpression
|
||||
// 1. Let spreadRef be ? Evaluation of AssignmentExpression.
|
||||
// 2. Let spreadObj be ? GetValue(spreadRef).
|
||||
// 3. Let iteratorRecord be ? GetIterator(spreadObj).
|
||||
// 4. Repeat,
|
||||
// a. Let next be ? IteratorStep(iteratorRecord).
|
||||
// b. If next is false, return nextIndex.
|
||||
// c. Let nextValue be ? IteratorValue(next).
|
||||
// d. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(nextIndex)), nextValue).
|
||||
// e. Set nextIndex to nextIndex + 1.
|
||||
// (2) https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
|
||||
// ArgumentList : ... AssignmentExpression
|
||||
// 1. Let list be a new empty List.
|
||||
// 2. Let spreadRef be ? Evaluation of AssignmentExpression.
|
||||
// 3. Let spreadObj be ? GetValue(spreadRef).
|
||||
// 4. Let iteratorRecord be ? GetIterator(spreadObj).
|
||||
// 5. Repeat,
|
||||
// a. Let next be ? IteratorStep(iteratorRecord).
|
||||
// b. If next is false, return list.
|
||||
// c. Let nextArg be ? IteratorValue(next).
|
||||
// d. Append nextArg to list.
|
||||
// ArgumentList : ArgumentList , ... AssignmentExpression
|
||||
// 1. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList.
|
||||
// 2. Let spreadRef be ? Evaluation of AssignmentExpression.
|
||||
// 3. Let iteratorRecord be ? GetIterator(? GetValue(spreadRef)).
|
||||
// 4. Repeat,
|
||||
// a. Let next be ? IteratorStep(iteratorRecord).
|
||||
// b. If next is false, return precedingArgs.
|
||||
// c. Let nextArg be ? IteratorValue(next).
|
||||
// d. Append nextArg to precedingArgs.
|
||||
|
||||
// Note: We know from codegen, that lhs is a plain array with only indexed properties
|
||||
auto& lhs_array = lhs.as_array_exotic_object();
|
||||
auto lhs_size = lhs_array.indexed_array_like_size();
|
||||
|
||||
if (is_spread) {
|
||||
// ...rhs
|
||||
size_t i = lhs_size;
|
||||
TRY(get_iterator_values(vm, rhs, [&i, &lhs_array](Value iterator_value) -> Optional<Completion> {
|
||||
lhs_array.indexed_put(i, iterator_value);
|
||||
++i;
|
||||
return {};
|
||||
}));
|
||||
} else {
|
||||
lhs_array.indexed_put(lhs_size, rhs);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
struct FastPropertyNameIteratorData {
|
||||
Vector<PropertyKey> properties;
|
||||
PropertyNameIterator::FastPath fast_path { PropertyNameIterator::FastPath::None };
|
||||
|
|
@ -1529,18 +1413,6 @@ ThrowCompletionOr<void> ToInt32::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ToString::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(m_dst, Value { TRY(vm.get(m_value).to_primitive_string(vm)) });
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ToPrimitiveWithStringHint::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(m_dst, TRY(vm.get(m_value).to_primitive(vm, Value::PreferredType::String)));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> BitwiseOr::execute_impl(VM& vm) const
|
||||
{
|
||||
auto const lhs = vm.get(m_lhs);
|
||||
|
|
@ -1679,19 +1551,6 @@ void NewArray::execute_impl(VM& vm) const
|
|||
vm.set(dst(), array);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> NewArrayWithLength::execute_impl(VM& vm) const
|
||||
{
|
||||
auto length = static_cast<u64>(vm.get(m_array_length).as_double());
|
||||
auto array = TRY(Array::create(vm.realm(), length));
|
||||
vm.set(m_dst, array);
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ArrayAppend::execute_impl(VM& vm) const
|
||||
{
|
||||
return append(vm, vm.get(dst()), vm.get(src()), m_is_spread);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ImportCall::execute_impl(VM& vm) const
|
||||
{
|
||||
auto specifier = vm.get(m_specifier);
|
||||
|
|
@ -1726,66 +1585,6 @@ ThrowCompletionOr<void> IteratorToArray::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
void NewObject::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
if (m_cache != NumericLimits<u32>::max()) {
|
||||
auto& cache = vm.current_executable().object_shape_caches[m_cache];
|
||||
auto cached_shape = cache.shape.ptr();
|
||||
if (cached_shape) {
|
||||
vm.set(dst(), Object::create_with_premade_shape(*cached_shape));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vm.set(dst(), Object::create(realm, realm.intrinsics().object_prototype()));
|
||||
}
|
||||
|
||||
void CacheObjectShape::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& cache = vm.current_executable().object_shape_caches[m_cache];
|
||||
if (!cache.shape) {
|
||||
auto& object = vm.get(m_object).as_object();
|
||||
if (!object.shape().is_dictionary())
|
||||
cache.shape = &object.shape();
|
||||
}
|
||||
}
|
||||
|
||||
COLD static void init_object_literal_property_slow(Object& object, PropertyKey const& property_key, Value value, ObjectShapeCache& cache, u32 property_slot)
|
||||
{
|
||||
object.define_direct_property(property_key, value, JS::Attribute::Enumerable | JS::Attribute::Writable | JS::Attribute::Configurable);
|
||||
|
||||
// Cache the property offset for future fast-path use
|
||||
// Note: lookup may fail if the shape is in dictionary mode or for other edge cases.
|
||||
// We only cache if we're not in dictionary mode and the lookup succeeds.
|
||||
if (!object.shape().is_dictionary()) {
|
||||
auto metadata = object.shape().lookup(property_key);
|
||||
if (metadata.has_value()) {
|
||||
if (property_slot >= cache.property_offsets.size())
|
||||
cache.property_offsets.resize(property_slot + 1);
|
||||
cache.property_offsets[property_slot] = metadata->offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitObjectLiteralProperty::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& object = vm.get(m_object).as_object();
|
||||
auto value = vm.get(m_src);
|
||||
auto& cache = vm.current_executable().object_shape_caches[m_shape_cache_index];
|
||||
|
||||
// Fast path: if we have a cached shape and it matches, write directly to the cached offset
|
||||
auto cached_shape = cache.shape.ptr();
|
||||
if (cached_shape && &object.shape() == cached_shape && m_property_slot < cache.property_offsets.size()) {
|
||||
object.put_direct(cache.property_offsets[m_property_slot], value);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const& property_key = vm.current_executable().get_property_key(m_property);
|
||||
init_object_literal_property_slow(object, property_key, value, cache, m_property_slot);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CopyObjectExcludingProperties::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
|
@ -1836,29 +1635,6 @@ static ThrowCompletionOr<void> get_binding(VM& vm, Operand dst, EnvironmentCoord
|
|||
return {};
|
||||
}
|
||||
|
||||
template<BindingIsKnownToBeInitialized binding_is_known_to_be_initialized>
|
||||
static ThrowCompletionOr<void> dynamically_get_binding(VM& vm, Operand dst, IdentifierTableIndex identifier, Strict strict, EnvironmentCoordinate& cache)
|
||||
{
|
||||
auto const* current_environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
if (auto const* environment = get_cached_environment(current_environment, cache)) [[likely]] {
|
||||
Value value;
|
||||
if constexpr (binding_is_known_to_be_initialized == BindingIsKnownToBeInitialized::No) {
|
||||
value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, cache.index));
|
||||
} else {
|
||||
value = static_cast<DeclarativeEnvironment const&>(*environment).get_initialized_binding_value_direct(cache.index);
|
||||
}
|
||||
vm.set(dst, value);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& executable = vm.current_executable();
|
||||
auto reference = TRY(vm.resolve_binding(executable.get_identifier(identifier), strict));
|
||||
update_environment_coordinate_cache(current_environment, reference, cache);
|
||||
|
||||
vm.set(dst, TRY(reference.get_value(vm)));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return get_binding<BindingIsKnownToBeInitialized::No>(vm, m_dst, m_cache);
|
||||
|
|
@ -1869,16 +1645,6 @@ ThrowCompletionOr<void> GetInitializedBinding::execute_impl(VM& vm) const
|
|||
return get_binding<BindingIsKnownToBeInitialized::Yes>(vm, m_dst, m_cache);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicGetBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_get_binding<BindingIsKnownToBeInitialized::No>(vm, m_dst, m_identifier, strict(), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicGetInitializedBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_get_binding<BindingIsKnownToBeInitialized::Yes>(vm, m_dst, m_identifier, strict(), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(VM& vm) const
|
||||
{
|
||||
auto callee_and_this = TRY(get_callee_and_this_from_environment(
|
||||
|
|
@ -1889,18 +1655,6 @@ ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(VM& vm) co
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicGetCalleeAndThisFromEnvironment::execute_impl(VM& vm) const
|
||||
{
|
||||
auto callee_and_this = TRY(dynamically_get_callee_and_this_from_environment(
|
||||
vm,
|
||||
vm.get_identifier(m_identifier),
|
||||
strict(),
|
||||
vm.current_executable().environment_coordinate_caches[m_cache]));
|
||||
vm.set(m_callee, callee_and_this.callee);
|
||||
vm.set(m_this_value, callee_and_this.this_value);
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetGlobal::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(dst(), TRY(get_global(vm, m_identifier, strict(), vm.current_executable().global_variable_caches[m_cache])));
|
||||
|
|
@ -2005,126 +1759,6 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
COLD ThrowCompletionOr<void> DeleteVariable::execute_impl(VM& vm) const
|
||||
{
|
||||
auto const& string = vm.get_identifier(m_identifier);
|
||||
auto reference = TRY(vm.resolve_binding(string, strict()));
|
||||
vm.set(dst(), Value(TRY(reference.delete_(vm))));
|
||||
return {};
|
||||
}
|
||||
|
||||
COLD ThrowCompletionOr<void> EnterObjectEnvironment::execute_impl(VM& vm) const
|
||||
{
|
||||
auto object = TRY(vm.get(m_object).to_object(vm));
|
||||
auto& old_environment = vm.running_execution_context().lexical_environment;
|
||||
auto new_environment = new_object_environment(*object, true, old_environment);
|
||||
vm.set(m_dst, new_environment);
|
||||
vm.running_execution_context().lexical_environment = new_environment;
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CreateVariable::execute_impl(VM& vm) const
|
||||
{
|
||||
auto const& name = vm.get_identifier(m_identifier);
|
||||
return create_variable(vm, name, m_mode, m_is_global, m_is_immutable, m_is_strict);
|
||||
}
|
||||
|
||||
template<EnvironmentMode environment_mode, BindingInitializationMode initialization_mode>
|
||||
static ThrowCompletionOr<void> initialize_or_set_binding(VM& vm, Strict strict, Value value, EnvironmentCoordinate const& cache)
|
||||
{
|
||||
VERIFY(cache.is_valid());
|
||||
|
||||
auto* environment = environment_mode == EnvironmentMode::Lexical
|
||||
? vm.running_execution_context().lexical_environment.ptr()
|
||||
: vm.running_execution_context().variable_environment.ptr();
|
||||
|
||||
for (size_t i = 0; i < cache.hops; ++i)
|
||||
environment = environment->outer_environment();
|
||||
|
||||
if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
|
||||
TRY(static_cast<DeclarativeEnvironment&>(*environment).initialize_binding_direct(vm, cache.index, value, Environment::InitializeBindingHint::Normal));
|
||||
} else {
|
||||
TRY(static_cast<DeclarativeEnvironment&>(*environment).set_mutable_binding_direct(vm, cache.index, value, strict == Strict::Yes));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<EnvironmentMode environment_mode, BindingInitializationMode initialization_mode>
|
||||
static ThrowCompletionOr<void> dynamically_initialize_or_set_binding(VM& vm, IdentifierTableIndex identifier_index, Strict strict, Value value, EnvironmentCoordinate& cache)
|
||||
{
|
||||
auto* environment = environment_mode == EnvironmentMode::Lexical
|
||||
? vm.running_execution_context().lexical_environment.ptr()
|
||||
: vm.running_execution_context().variable_environment.ptr();
|
||||
|
||||
if (auto* cached_environment = get_cached_environment(environment, cache)) [[likely]] {
|
||||
if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
|
||||
TRY(static_cast<DeclarativeEnvironment&>(*cached_environment).initialize_binding_direct(vm, cache.index, value, Environment::InitializeBindingHint::Normal));
|
||||
} else if (initialization_mode == BindingInitializationMode::Set) {
|
||||
TRY(static_cast<DeclarativeEnvironment&>(*cached_environment).set_mutable_binding_direct(vm, cache.index, value, strict == Strict::Yes));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto reference = TRY(vm.resolve_binding(vm.get_identifier(identifier_index), strict, environment));
|
||||
update_environment_coordinate_cache(environment, reference, cache);
|
||||
if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
|
||||
TRY(reference.initialize_referenced_binding(vm, value));
|
||||
} else if (initialization_mode == BindingInitializationMode::Set) {
|
||||
TRY(reference.put_value(vm, value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> InitializeLexicalBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(vm, strict(), vm.get(m_src), m_cache);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> InitializeVariableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(vm, strict(), vm.get(m_src), m_cache);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicInitializeLexicalBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicInitializeVariableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> SetLexicalBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(vm, strict(), vm.get(m_src), m_cache);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> SetResolvedBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto const& identifier = vm.get_identifier(m_identifier);
|
||||
auto environment = vm.get(m_environment);
|
||||
auto reference = environment.is_null()
|
||||
? Reference { Reference::BaseType::Unresolvable, PropertyKey { identifier }, strict() }
|
||||
: Reference { as<Environment>(environment.as_cell()), identifier, strict() };
|
||||
return reference.put_value(vm, vm.get(m_src));
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> SetVariableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(vm, strict(), vm.get(m_src), m_cache);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicSetLexicalBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicSetVariableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetById::execute_impl(VM& vm) const
|
||||
{
|
||||
auto base_value = vm.get(base());
|
||||
|
|
@ -2171,20 +1805,6 @@ ThrowCompletionOr<void> GetPrivateById::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> HasPrivateId::execute_impl(VM& vm) const
|
||||
{
|
||||
|
||||
auto base = vm.get(m_base);
|
||||
if (!base.is_object()) [[unlikely]]
|
||||
return vm.throw_completion<TypeError>(ErrorType::InOperatorWithObject);
|
||||
|
||||
auto private_environment = vm.running_execution_context().private_environment;
|
||||
VERIFY(private_environment);
|
||||
auto private_name = private_environment->resolve_private_identifier(vm.get_identifier(m_property));
|
||||
vm.set(dst(), Value(base.as_object().private_element_find(private_name) != nullptr));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> PutBySpread::execute_impl(VM& vm) const
|
||||
{
|
||||
auto value = vm.get(m_src);
|
||||
|
|
@ -2229,28 +1849,6 @@ ThrowCompletionOr<void> PutPrivateById::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
COLD ThrowCompletionOr<void> DeleteById::execute_impl(VM& vm) const
|
||||
{
|
||||
auto const& property_key = vm.get_property_key(m_property);
|
||||
auto reference = Reference { vm.get(m_base), property_key, {}, strict() };
|
||||
vm.set(dst(), Value(TRY(reference.delete_(vm))));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ResolveBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto const& identifier = vm.get_identifier(m_identifier);
|
||||
auto reference = TRY(vm.resolve_binding(identifier, strict()));
|
||||
if (reference.is_unresolvable()) {
|
||||
vm.set(dst(), js_null());
|
||||
return {};
|
||||
}
|
||||
|
||||
VERIFY(reference.is_environment_reference());
|
||||
vm.set(dst(), &reference.base_environment());
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ResolveThisBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& cached_this_value = vm.reg(Register::this_value());
|
||||
|
|
@ -2270,32 +1868,11 @@ ThrowCompletionOr<void> ResolveThisBinding::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-makesuperpropertyreference
|
||||
ThrowCompletionOr<void> ResolveSuperBase::execute_impl(VM& vm) const
|
||||
{
|
||||
|
||||
// 1. Let env be GetThisEnvironment().
|
||||
auto& env = as<FunctionEnvironment>(*get_this_environment(vm));
|
||||
|
||||
// 2. Assert: env.HasSuperBinding() is true.
|
||||
VERIFY(env.has_super_binding());
|
||||
|
||||
// 3. Let baseValue be ? env.GetSuperBase().
|
||||
vm.set(dst(), TRY(env.get_super_base()));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void GetLexicalEnvironment::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(dst(), vm.running_execution_context().lexical_environment);
|
||||
}
|
||||
|
||||
void SetLexicalEnvironment::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.running_execution_context().lexical_environment = &as<Environment>(vm.get(m_environment).as_cell());
|
||||
}
|
||||
|
||||
template<CallType call_type>
|
||||
NEVER_INLINE static ThrowCompletionOr<void> execute_call(
|
||||
VM& vm,
|
||||
|
|
@ -2612,30 +2189,6 @@ ThrowCompletionOr<void> SuperCallWithArgumentArray::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
static Optional<StringView> function_name_prefix_to_string(Op::FunctionNamePrefix prefix)
|
||||
{
|
||||
switch (prefix) {
|
||||
case Op::FunctionNamePrefix::None:
|
||||
return {};
|
||||
case Op::FunctionNamePrefix::Get:
|
||||
return "get"sv;
|
||||
case Op::FunctionNamePrefix::Set:
|
||||
return "set"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> SetFunctionName::execute_impl(VM& vm) const
|
||||
{
|
||||
auto function = vm.get(m_function).as_if<ECMAScriptFunctionObject>();
|
||||
if (!function || !function->name().is_empty())
|
||||
return {};
|
||||
|
||||
auto property_key = TRY(vm.get(m_name).to_property_key(vm));
|
||||
function->set_inferred_name(Variant<PropertyKey, PrivateName> { move(property_key) }, function_name_prefix_to_string(m_prefix));
|
||||
return {};
|
||||
}
|
||||
|
||||
void Return::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.do_return(vm.get(m_value));
|
||||
|
|
@ -2714,64 +2267,6 @@ ThrowCompletionOr<void> PostfixDecrement::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
COLD ThrowCompletionOr<void> Throw::execute_impl(VM& vm) const
|
||||
{
|
||||
return throw_completion(vm.get(src()));
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ThrowIfNotObject::execute_impl(VM& vm) const
|
||||
{
|
||||
auto src = vm.get(m_src);
|
||||
if (!src.is_object()) [[unlikely]]
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, src);
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ThrowIfNullish::execute_impl(VM& vm) const
|
||||
{
|
||||
auto value = vm.get(m_src);
|
||||
if (value.is_nullish()) [[unlikely]]
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotObjectCoercible, value);
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ThrowIfTDZ::execute_impl(VM& vm) const
|
||||
{
|
||||
auto value = vm.get(m_src);
|
||||
if (value.is_special_empty_value()) [[unlikely]]
|
||||
return vm.throw_completion<ReferenceError>(ErrorType::BindingNotInitialized, value);
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ThrowConstAssignment::execute_impl(VM& vm) const
|
||||
{
|
||||
return vm.throw_completion<TypeError>(ErrorType::InvalidAssignToConst);
|
||||
}
|
||||
|
||||
void Yield::execute_impl(VM& vm) const
|
||||
{
|
||||
auto yielded_value = vm.get(m_value).is_special_empty_value() ? js_undefined() : vm.get(m_value);
|
||||
vm.do_return(
|
||||
vm.do_yield(yielded_value, m_continuation_label));
|
||||
}
|
||||
|
||||
void YieldIteratorResult::execute_impl(VM& vm) const
|
||||
{
|
||||
auto yielded_value = vm.get(m_value).is_special_empty_value() ? js_undefined() : vm.get(m_value);
|
||||
vm.do_return(
|
||||
vm.do_yield(yielded_value, m_continuation_label, true));
|
||||
}
|
||||
|
||||
void Await::execute_impl(VM& vm) const
|
||||
{
|
||||
auto yielded_value = vm.get(m_argument).is_special_empty_value() ? js_undefined() : vm.get(m_argument);
|
||||
auto& context = vm.running_execution_context();
|
||||
context.yield_continuation = m_continuation_label.address();
|
||||
context.yield_is_await = true;
|
||||
context.yield_value_is_iterator_result = false;
|
||||
vm.do_return(yielded_value);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetByValue::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(dst(), TRY(get_by_value(vm, m_base_identifier, vm.get(m_base), vm.get(m_property), vm.current_executable())));
|
||||
|
|
@ -2807,14 +2302,6 @@ ThrowCompletionOr<void> PutByValueWithThis::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
COLD ThrowCompletionOr<void> DeleteByValue::execute_impl(VM& vm) const
|
||||
{
|
||||
auto property_key = TRY(vm.get(m_property).to_property_key(vm));
|
||||
auto reference = Reference { vm.get(m_base), property_key, {}, strict() };
|
||||
vm.set(m_dst, Value(TRY(reference.delete_(vm))));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetIterator::execute_impl(VM& vm) const
|
||||
{
|
||||
auto iterator_record = TRY(get_iterator_impl(vm, vm.get(iterable()), m_hint));
|
||||
|
|
@ -2933,82 +2420,4 @@ NEVER_INLINE ThrowCompletionOr<void> NewClass::execute_impl(VM& vm) const
|
|||
return {};
|
||||
}
|
||||
|
||||
// 13.5.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-typeof-operator-runtime-semantics-evaluation
|
||||
ThrowCompletionOr<void> TypeofBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
VERIFY(m_cache.is_valid());
|
||||
|
||||
auto const* environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
for (size_t i = 0; i < m_cache.hops; ++i)
|
||||
environment = environment->outer_environment();
|
||||
|
||||
auto value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, m_cache.index));
|
||||
vm.set(dst(), value.typeof_(vm));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicTypeofBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& cache = vm.current_executable().environment_coordinate_caches[m_cache];
|
||||
auto const* current_environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
if (auto const* environment = get_cached_environment(current_environment, cache)) [[likely]] {
|
||||
auto value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, cache.index));
|
||||
vm.set(dst(), value.typeof_(vm));
|
||||
return {};
|
||||
}
|
||||
|
||||
// 1. Let val be the result of evaluating UnaryExpression.
|
||||
auto reference = TRY(vm.resolve_binding(vm.get_identifier(m_identifier), strict()));
|
||||
|
||||
// 2. If val is a Reference Record, then
|
||||
// a. If IsUnresolvableReference(val) is true, return "undefined".
|
||||
if (reference.is_unresolvable()) {
|
||||
vm.set(dst(), PrimitiveString::create(vm, "undefined"_string));
|
||||
return {};
|
||||
}
|
||||
|
||||
// 3. Set val to ? GetValue(val).
|
||||
update_environment_coordinate_cache(current_environment, reference, cache);
|
||||
|
||||
auto value = TRY(reference.get_value(vm));
|
||||
|
||||
// 4. NOTE: This step is replaced in section B.3.6.3.
|
||||
// 5. Return a String according to Table 41.
|
||||
vm.set(dst(), value.typeof_(vm));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CreateImmutableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& environment = as<Environment>(vm.get(m_environment).as_cell());
|
||||
return environment.create_immutable_binding(vm, vm.get_identifier(m_identifier), m_strict_binding);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CreateMutableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& environment = as<Environment>(vm.get(m_environment).as_cell());
|
||||
return environment.create_mutable_binding(vm, vm.get_identifier(m_identifier), m_can_be_deleted);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ToObject::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(m_dst, TRY(vm.get(m_value).to_object(vm)));
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> ToLength::execute_impl(VM& vm) const
|
||||
{
|
||||
vm.set(m_dst, Value { TRY(vm.get(m_value).to_length(vm)) });
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CreateDataPropertyOrThrow::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& object = vm.get(m_object).as_object();
|
||||
auto property = TRY(vm.get(m_property).to_property_key(vm));
|
||||
auto value = vm.get(m_value);
|
||||
TRY(object.create_data_property_or_throw(property, value));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue