LibJS: Let bytecode instructions know whether they are in strict mode

This commits puts the strict mode flag in the header of every bytecode
instruction. This allows us to check for strict mode without looking at
the currently running execution context.
This commit is contained in:
Andreas Kling 2025-10-28 20:25:12 +01:00 committed by Andreas Kling
parent 3fb678b376
commit fb05063dde
Notes: github-actions[bot] 2025-10-29 20:22:27 +00:00
16 changed files with 183 additions and 227 deletions

View file

@ -1023,7 +1023,7 @@ inline ThrowCompletionOr<Value> get_by_value(VM& vm, Optional<IdentifierTableInd
return TRY(object->internal_get(property_key, base_value));
}
inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierTableIndex identifier_index, GlobalVariableCache& cache)
inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierTableIndex identifier_index, Strict strict, GlobalVariableCache& cache)
{
auto& vm = interpreter.vm();
auto& binding_object = interpreter.global_object();
@ -1068,7 +1068,7 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
cache.in_module_environment = true;
return TRY(module_environment.get_binding_value_direct(vm, index.value()));
}
return TRY(module_environment.get_binding_value(vm, identifier, vm.in_strict_mode()));
return TRY(module_environment.get_binding_value(vm, identifier, strict == Strict::Yes));
}
}
@ -1077,7 +1077,7 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
cache.environment_binding_index = static_cast<u32>(offset.value());
cache.has_environment_binding_index = true;
cache.in_module_environment = false;
return TRY(declarative_record.get_binding_value(vm, identifier, vm.in_strict_mode()));
return TRY(declarative_record.get_binding_value(vm, identifier, strict == Strict::Yes));
}
if (TRY(binding_object.has_property(identifier))) {
@ -1098,10 +1098,10 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
}
template<PutKind kind>
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional<Utf16FlyString const&> const& base_identifier, PropertyKey name, PropertyLookupCache* caches = nullptr)
ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional<Utf16FlyString const&> const& base_identifier, PropertyKey name, Strict strict, PropertyLookupCache* caches = nullptr)
{
// Better error message than to_object would give
if (vm.in_strict_mode() && base.is_nullish()) [[unlikely]]
if (strict == Strict::Yes && base.is_nullish()) [[unlikely]]
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
// a. Let baseObj be ? ToObject(V.[[Base]]).
@ -1277,7 +1277,7 @@ ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value
}
}
if (!succeeded && vm.in_strict_mode()) [[unlikely]] {
if (!succeeded && strict == Strict::Yes) [[unlikely]] {
if (base.is_object())
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
return vm.throw_completion<TypeError>(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof_(vm)->utf8_string(), base.to_string_without_side_effects());
@ -1296,14 +1296,14 @@ ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value this_value
return {};
}
inline ThrowCompletionOr<Value> perform_call(Interpreter& interpreter, Value this_value, Op::CallType call_type, Value callee, ReadonlySpan<Value> argument_values)
inline ThrowCompletionOr<Value> perform_call(Interpreter& interpreter, Value this_value, Op::CallType call_type, Value callee, ReadonlySpan<Value> argument_values, Strict strict)
{
auto& vm = interpreter.vm();
auto& function = callee.as_function();
Value return_value;
if (call_type == Op::CallType::DirectEval) {
if (callee == interpreter.realm().intrinsics().eval_function())
return_value = TRY(perform_eval(vm, !argument_values.is_empty() ? argument_values[0] : js_undefined(), vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct));
return_value = TRY(perform_eval(vm, !argument_values.is_empty() ? argument_values[0] : js_undefined(), strict == Strict::Yes ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct));
else
return_value = TRY(JS::call(vm, function, this_value, argument_values));
} else if (call_type == Op::CallType::Call)
@ -1389,7 +1389,7 @@ inline Value new_function(Interpreter& interpreter, FunctionNode const& function
}
template<PutKind kind>
inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16FlyString const&> const& base_identifier, Value property_key_value, Value value)
inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16FlyString const&> const& base_identifier, Value property_key_value, Value value, Strict strict)
{
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
if (kind == PutKind::Normal
@ -1501,7 +1501,7 @@ inline ThrowCompletionOr<void> put_by_value(VM& vm, Value base, Optional<Utf16Fl
}
auto property_key = TRY(property_key_value.to_property_key(vm));
TRY(put_by_property_key<kind>(vm, base, base, value, base_identifier, property_key));
TRY(put_by_property_key<kind>(vm, base, base, value, base_identifier, property_key, strict));
return {};
}
@ -1510,7 +1510,7 @@ struct CalleeAndThis {
Value this_value;
};
inline ThrowCompletionOr<CalleeAndThis> get_callee_and_this_from_environment(Bytecode::Interpreter& interpreter, Utf16FlyString const& name, EnvironmentCoordinate& cache)
inline ThrowCompletionOr<CalleeAndThis> get_callee_and_this_from_environment(Interpreter& interpreter, Utf16FlyString const& name, Strict strict, EnvironmentCoordinate& cache)
{
auto& vm = interpreter.vm();
@ -1534,7 +1534,7 @@ inline ThrowCompletionOr<CalleeAndThis> get_callee_and_this_from_environment(Byt
cache = {};
}
auto reference = TRY(vm.resolve_binding(name));
auto reference = TRY(vm.resolve_binding(name, strict));
if (reference.environment_coordinate().has_value())
cache = reference.environment_coordinate().value();
@ -1694,39 +1694,6 @@ inline ThrowCompletionOr<void> append(VM& vm, Value lhs, Value rhs, bool is_spre
return {};
}
inline ThrowCompletionOr<Value> delete_by_id(Bytecode::Interpreter& interpreter, Value base, IdentifierTableIndex property)
{
auto& vm = interpreter.vm();
auto const& identifier = interpreter.get_identifier(property);
bool strict = vm.in_strict_mode();
auto reference = Reference { base, identifier, {}, strict };
return TRY(reference.delete_(vm));
}
inline ThrowCompletionOr<Value> delete_by_value(Bytecode::Interpreter& interpreter, Value base, Value property_key_value)
{
auto& vm = interpreter.vm();
auto property_key = TRY(property_key_value.to_property_key(vm));
bool strict = vm.in_strict_mode();
auto reference = Reference { base, property_key, {}, strict };
return Value(TRY(reference.delete_(vm)));
}
inline ThrowCompletionOr<Value> delete_by_value_with_this(Bytecode::Interpreter& interpreter, Value base, Value property_key_value, Value this_value)
{
auto& vm = interpreter.vm();
auto property_key = TRY(property_key_value.to_property_key(vm));
bool strict = vm.in_strict_mode();
auto reference = Reference { base, property_key, this_value, strict };
return Value(TRY(reference.delete_(vm)));
}
class JS_API PropertyNameIterator final
: public Object
, public BuiltinIterator {
@ -2290,7 +2257,7 @@ enum class BindingIsKnownToBeInitialized {
};
template<BindingIsKnownToBeInitialized binding_is_known_to_be_initialized>
static ThrowCompletionOr<void> get_binding(Interpreter& interpreter, Operand dst, IdentifierTableIndex identifier, EnvironmentCoordinate& cache)
static ThrowCompletionOr<void> get_binding(Interpreter& interpreter, Operand dst, IdentifierTableIndex identifier, Strict strict, EnvironmentCoordinate& cache)
{
auto& vm = interpreter.vm();
@ -2312,7 +2279,7 @@ static ThrowCompletionOr<void> get_binding(Interpreter& interpreter, Operand dst
}
auto& executable = interpreter.current_executable();
auto reference = TRY(vm.resolve_binding(executable.get_identifier(identifier)));
auto reference = TRY(vm.resolve_binding(executable.get_identifier(identifier), strict));
if (reference.environment_coordinate().has_value())
cache = reference.environment_coordinate().value();
interpreter.set(dst, TRY(reference.get_value(vm)));
@ -2321,12 +2288,12 @@ static ThrowCompletionOr<void> get_binding(Interpreter& interpreter, Operand dst
ThrowCompletionOr<void> GetBinding::execute_impl(Bytecode::Interpreter& interpreter) const
{
return get_binding<BindingIsKnownToBeInitialized::No>(interpreter, m_dst, m_identifier, m_cache);
return get_binding<BindingIsKnownToBeInitialized::No>(interpreter, m_dst, m_identifier, strict(), m_cache);
}
ThrowCompletionOr<void> GetInitializedBinding::execute_impl(Bytecode::Interpreter& interpreter) const
{
return get_binding<BindingIsKnownToBeInitialized::Yes>(interpreter, m_dst, m_identifier, m_cache);
return get_binding<BindingIsKnownToBeInitialized::Yes>(interpreter, m_dst, m_identifier, strict(), m_cache);
}
ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
@ -2334,6 +2301,7 @@ ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::
auto callee_and_this = TRY(get_callee_and_this_from_environment(
interpreter,
interpreter.get_identifier(m_identifier),
strict(),
m_cache));
interpreter.set(m_callee, callee_and_this.callee);
interpreter.set(m_this_value, callee_and_this.this_value);
@ -2342,7 +2310,7 @@ ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::
ThrowCompletionOr<void> GetGlobal::execute_impl(Bytecode::Interpreter& interpreter) const
{
interpreter.set(dst(), TRY(get_global(interpreter, m_identifier, interpreter.current_executable().global_variable_caches[m_cache_index])));
interpreter.set(dst(), TRY(get_global(interpreter, m_identifier, strict(), interpreter.current_executable().global_variable_caches[m_cache_index])));
return {};
}
@ -2373,9 +2341,9 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(Bytecode::Interpreter& interpret
if (cache.has_environment_binding_index) {
if (cache.in_module_environment) {
auto module = vm.running_execution_context().script_or_module.get_pointer<GC::Ref<Module>>();
TRY((*module)->environment()->set_mutable_binding_direct(vm, cache.environment_binding_index, src, vm.in_strict_mode()));
TRY((*module)->environment()->set_mutable_binding_direct(vm, cache.environment_binding_index, src, strict() == Strict::Yes));
} else {
TRY(declarative_record.set_mutable_binding_direct(vm, cache.environment_binding_index, src, vm.in_strict_mode()));
TRY(declarative_record.set_mutable_binding_direct(vm, cache.environment_binding_index, src, strict() == Strict::Yes));
}
return {};
}
@ -2395,9 +2363,9 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(Bytecode::Interpreter& interpret
cache.environment_binding_index = static_cast<u32>(index.value());
cache.has_environment_binding_index = true;
cache.in_module_environment = true;
return TRY(module_environment.set_mutable_binding_direct(vm, index.value(), src, vm.in_strict_mode()));
return TRY(module_environment.set_mutable_binding_direct(vm, index.value(), src, strict() == Strict::Yes));
}
return TRY(module_environment.set_mutable_binding(vm, identifier, src, vm.in_strict_mode()));
return TRY(module_environment.set_mutable_binding(vm, identifier, src, strict() == Strict::Yes));
}
}
@ -2406,14 +2374,14 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(Bytecode::Interpreter& interpret
cache.environment_binding_index = static_cast<u32>(offset.value());
cache.has_environment_binding_index = true;
cache.in_module_environment = false;
TRY(declarative_record.set_mutable_binding(vm, identifier, src, vm.in_strict_mode()));
TRY(declarative_record.set_mutable_binding(vm, identifier, src, strict() == Strict::Yes));
return {};
}
if (TRY(binding_object.has_property(identifier))) {
CacheableSetPropertyMetadata cacheable_metadata;
auto success = TRY(binding_object.internal_set(identifier, src, &binding_object, &cacheable_metadata));
if (!success && vm.in_strict_mode()) {
if (!success && strict() == Strict::Yes) {
// Note: Nothing like this in the spec, this is here to produce nicer errors instead of the generic one thrown by Object::set().
auto property_or_error = binding_object.internal_get_own_property(identifier);
@ -2436,7 +2404,7 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(Bytecode::Interpreter& interpret
return {};
}
auto reference = TRY(vm.resolve_binding(identifier, &declarative_record));
auto reference = TRY(vm.resolve_binding(identifier, strict(), &declarative_record));
TRY(reference.put_value(vm, src));
return {};
@ -2446,7 +2414,7 @@ ThrowCompletionOr<void> DeleteVariable::execute_impl(Bytecode::Interpreter& inte
{
auto& vm = interpreter.vm();
auto const& string = interpreter.get_identifier(m_identifier);
auto reference = TRY(vm.resolve_binding(string));
auto reference = TRY(vm.resolve_binding(string, strict()));
interpreter.set(dst(), Value(TRY(reference.delete_(vm))));
return {};
}
@ -2548,7 +2516,7 @@ void CreateArguments::execute_impl(Bytecode::Interpreter& interpreter) const
}
template<EnvironmentMode environment_mode, BindingInitializationMode initialization_mode>
static ThrowCompletionOr<void> initialize_or_set_binding(Interpreter& interpreter, IdentifierTableIndex identifier_index, Value value, EnvironmentCoordinate& cache)
static ThrowCompletionOr<void> initialize_or_set_binding(Interpreter& interpreter, IdentifierTableIndex identifier_index, Strict strict, Value value, EnvironmentCoordinate& cache)
{
auto& vm = interpreter.vm();
@ -2563,14 +2531,14 @@ static ThrowCompletionOr<void> initialize_or_set_binding(Interpreter& interprete
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, vm.in_strict_mode()));
TRY(static_cast<DeclarativeEnvironment&>(*environment).set_mutable_binding_direct(vm, cache.index, value, strict == Strict::Yes));
}
return {};
}
cache = {};
}
auto reference = TRY(vm.resolve_binding(interpreter.get_identifier(identifier_index), environment));
auto reference = TRY(vm.resolve_binding(interpreter.get_identifier(identifier_index), strict, environment));
if (reference.environment_coordinate().has_value())
cache = reference.environment_coordinate().value();
if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
@ -2583,22 +2551,22 @@ static ThrowCompletionOr<void> initialize_or_set_binding(Interpreter& interprete
ThrowCompletionOr<void> InitializeLexicalBinding::execute_impl(Bytecode::Interpreter& interpreter) const
{
return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache);
}
ThrowCompletionOr<void> InitializeVariableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
{
return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache);
}
ThrowCompletionOr<void> SetLexicalBinding::execute_impl(Bytecode::Interpreter& interpreter) const
{
return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache);
}
ThrowCompletionOr<void> SetVariableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
{
return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache);
}
ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter) const
@ -2678,90 +2646,90 @@ ThrowCompletionOr<void> PutBySpread::execute_impl(Bytecode::Interpreter& interpr
return {};
}
#define DEFINE_PUT_KIND_BY_ID(kind) \
ThrowCompletionOr<void> Put##kind##ById::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ById::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ById {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
executable.identifier_table->get(m_property), \
format_operand("src"sv, m_src, executable)); \
#define DEFINE_PUT_KIND_BY_ID(kind) \
ThrowCompletionOr<void> Put##kind##ById::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, strict(), &cache)); \
return {}; \
} \
ByteString Put##kind##ById::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ById {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
executable.identifier_table->get(m_property), \
format_operand("src"sv, m_src, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_ID)
#define DEFINE_PUT_KIND_BY_NUMERIC_ID(kind) \
ThrowCompletionOr<void> Put##kind##ByNumericId::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
PropertyKey name { m_property }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ByNumericId::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByNumericId {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
m_property, \
format_operand("src"sv, m_src, executable)); \
#define DEFINE_PUT_KIND_BY_NUMERIC_ID(kind) \
ThrowCompletionOr<void> Put##kind##ByNumericId::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
PropertyKey name { m_property }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, base, value, base_identifier, name, strict(), &cache)); \
return {}; \
} \
ByteString Put##kind##ByNumericId::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByNumericId {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
m_property, \
format_operand("src"sv, m_src, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_NUMERIC_ID)
#define DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) \
ThrowCompletionOr<void> Put##kind##ByNumericIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
PropertyKey name { m_property }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ByNumericIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByNumericIdWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
m_property, \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
#define DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS(kind) \
ThrowCompletionOr<void> Put##kind##ByNumericIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
PropertyKey name { m_property }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, strict(), &cache)); \
return {}; \
} \
ByteString Put##kind##ByNumericIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByNumericIdWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
m_property, \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_NUMERIC_ID_WITH_THIS)
#define DEFINE_PUT_KIND_BY_ID_WITH_THIS(kind) \
ThrowCompletionOr<void> Put##kind##ByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, &cache)); \
return {}; \
} \
ByteString Put##kind##ByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByIdWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
executable.identifier_table->get(m_property), \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
#define DEFINE_PUT_KIND_BY_ID_WITH_THIS(kind) \
ThrowCompletionOr<void> Put##kind##ByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const \
{ \
auto& vm = interpreter.vm(); \
auto value = interpreter.get(m_src); \
auto base = interpreter.get(m_base); \
PropertyKey name { interpreter.get_identifier(m_property), PropertyKey::StringMayBeNumber::No }; \
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index]; \
TRY(put_by_property_key<PutKind::kind>(vm, base, interpreter.get(m_this_value), value, {}, name, strict(), &cache)); \
return {}; \
} \
ByteString Put##kind##ByIdWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
{ \
return ByteString::formatted("Put" #kind "ByIdWithThis {}, {}, {}, {}", \
format_operand("base"sv, m_base, executable), \
executable.identifier_table->get(m_property), \
format_operand("src"sv, m_src, executable), \
format_operand("this"sv, m_this_value, executable)); \
}
JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_ID_WITH_THIS)
@ -2779,8 +2747,10 @@ ThrowCompletionOr<void> PutPrivateById::execute_impl(Bytecode::Interpreter& inte
ThrowCompletionOr<void> DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto base_value = interpreter.get(m_base);
interpreter.set(dst(), TRY(Bytecode::delete_by_id(interpreter, base_value, m_property)));
auto& vm = interpreter.vm();
auto const& identifier = interpreter.get_identifier(m_property);
auto reference = Reference { interpreter.get(m_base), identifier, {}, strict() };
interpreter.set(dst(), Value(TRY(reference.delete_(vm))));
return {};
}
@ -2789,8 +2759,7 @@ ThrowCompletionOr<void> DeleteByIdWithThis::execute_impl(Bytecode::Interpreter&
auto& vm = interpreter.vm();
auto base_value = interpreter.get(m_base);
auto const& identifier = interpreter.get_identifier(m_property);
bool strict = vm.in_strict_mode();
auto reference = Reference { base_value, identifier, interpreter.get(m_this_value), strict };
auto reference = Reference { base_value, identifier, interpreter.get(m_this_value), strict() };
interpreter.set(dst(), Value(TRY(reference.delete_(vm))));
return {};
}
@ -2888,7 +2857,8 @@ static ThrowCompletionOr<void> execute_call(
Value this_value,
ReadonlySpan<Operand> arguments,
Operand dst,
Optional<StringTableIndex> const& expression_string)
Optional<StringTableIndex> const& expression_string,
Strict strict)
{
TRY(throw_if_needed_for_call(interpreter, callee, call_type, expression_string));
@ -2912,7 +2882,7 @@ static ThrowCompletionOr<void> execute_call(
Value retval;
if (call_type == CallType::DirectEval && callee == interpreter.realm().intrinsics().eval_function()) {
retval = TRY(perform_eval(interpreter.vm(), !callee_context->arguments.is_empty() ? callee_context->arguments[0] : js_undefined(), interpreter.vm().in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct));
retval = TRY(perform_eval(interpreter.vm(), !callee_context->arguments.is_empty() ? callee_context->arguments[0] : js_undefined(), strict == Strict::Yes ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct));
} else if (call_type == CallType::Construct) {
retval = TRY(function.internal_construct(*callee_context, function));
} else {
@ -2924,17 +2894,17 @@ static ThrowCompletionOr<void> execute_call(
ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) const
{
return execute_call<CallType::Call>(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string);
return execute_call<CallType::Call>(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict());
}
NEVER_INLINE ThrowCompletionOr<void> CallConstruct::execute_impl(Bytecode::Interpreter& interpreter) const
{
return execute_call<CallType::Construct>(interpreter, interpreter.get(m_callee), js_undefined(), { m_arguments, m_argument_count }, m_dst, m_expression_string);
return execute_call<CallType::Construct>(interpreter, interpreter.get(m_callee), js_undefined(), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict());
}
ThrowCompletionOr<void> CallDirectEval::execute_impl(Bytecode::Interpreter& interpreter) const
{
return execute_call<CallType::DirectEval>(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string);
return execute_call<CallType::DirectEval>(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict());
}
ThrowCompletionOr<void> CallBuiltin::execute_impl(Bytecode::Interpreter& interpreter) const
@ -2946,7 +2916,7 @@ ThrowCompletionOr<void> CallBuiltin::execute_impl(Bytecode::Interpreter& interpr
return {};
}
return execute_call<CallType::Call>(interpreter, callee, interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string);
return execute_call<CallType::Call>(interpreter, callee, interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict());
}
template<CallType call_type>
@ -2956,7 +2926,8 @@ static ThrowCompletionOr<void> call_with_argument_array(
Value this_value,
Value arguments,
Operand dst,
Optional<StringTableIndex> const& expression_string)
Optional<StringTableIndex> const& expression_string,
Strict strict)
{
TRY(throw_if_needed_for_call(interpreter, callee, call_type, expression_string));
@ -2988,7 +2959,7 @@ static ThrowCompletionOr<void> call_with_argument_array(
Value retval;
if (call_type == CallType::DirectEval && callee == interpreter.realm().intrinsics().eval_function()) {
auto& vm = interpreter.vm();
retval = TRY(perform_eval(vm, !callee_context->arguments.is_empty() ? callee_context->arguments[0] : js_undefined(), vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct));
retval = TRY(perform_eval(vm, !callee_context->arguments.is_empty() ? callee_context->arguments[0] : js_undefined(), strict == Strict::Yes ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct));
} else if (call_type == CallType::Construct) {
retval = TRY(function.internal_construct(*callee_context, function));
} else {
@ -3001,17 +2972,17 @@ static ThrowCompletionOr<void> call_with_argument_array(
ThrowCompletionOr<void> CallWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const
{
return call_with_argument_array<CallType::Call>(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string());
return call_with_argument_array<CallType::Call>(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string(), strict());
}
ThrowCompletionOr<void> CallDirectEvalWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const
{
return call_with_argument_array<CallType::DirectEval>(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string());
return call_with_argument_array<CallType::DirectEval>(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string(), strict());
}
ThrowCompletionOr<void> CallConstructWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const
{
return call_with_argument_array<CallType::Construct>(interpreter, interpreter.get(callee()), js_undefined(), interpreter.get(arguments()), dst(), expression_string());
return call_with_argument_array<CallType::Construct>(interpreter, interpreter.get(callee()), js_undefined(), interpreter.get(arguments()), dst(), expression_string(), strict());
}
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
@ -3276,7 +3247,7 @@ ThrowCompletionOr<void> GetByValueWithThis::execute_impl(Bytecode::Interpreter&
auto base = interpreter.get(m_base); \
auto base_identifier = interpreter.get_identifier(m_base_identifier); \
auto property = interpreter.get(m_property); \
TRY(put_by_value<PutKind::kind>(vm, base, base_identifier, property, value)); \
TRY(put_by_value<PutKind::kind>(vm, base, base_identifier, property, value, strict())); \
return {}; \
} \
ByteString Put##kind##ByValue::to_byte_string_impl(Bytecode::Executable const& executable) const \
@ -3297,7 +3268,7 @@ JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_VALUE)
auto base = interpreter.get(m_base); \
auto this_value = interpreter.get(m_this_value); \
auto property_key = TRY(interpreter.get(m_property).to_property_key(vm)); \
TRY(put_by_property_key<PutKind::kind>(vm, base, this_value, value, {}, property_key)); \
TRY(put_by_property_key<PutKind::kind>(vm, base, this_value, value, {}, property_key, strict())); \
return {}; \
} \
ByteString Put##kind##ByValueWithThis::to_byte_string_impl(Bytecode::Executable const& executable) const \
@ -3313,20 +3284,20 @@ JS_ENUMERATE_PUT_KINDS(DEFINE_PUT_KIND_BY_VALUE_WITH_THIS)
ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto base_value = interpreter.get(m_base);
auto property_key_value = interpreter.get(m_property);
interpreter.set(dst(), TRY(delete_by_value(interpreter, base_value, property_key_value)));
auto& vm = interpreter.vm();
auto property_key = TRY(interpreter.get(m_property).to_property_key(vm));
auto reference = Reference { interpreter.get(m_base), property_key, {}, strict() };
interpreter.set(m_dst, Value(TRY(reference.delete_(vm))));
return {};
}
ThrowCompletionOr<void> DeleteByValueWithThis::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto property_key_value = interpreter.get(m_property);
auto base_value = interpreter.get(m_base);
auto this_value = interpreter.get(m_this_value);
interpreter.set(dst(), TRY(delete_by_value_with_this(interpreter, base_value, property_key_value, this_value)));
auto property_key = TRY(property_key_value.to_property_key(vm));
auto reference = Reference { interpreter.get(m_base), property_key, interpreter.get(m_this_value), strict() };
interpreter.set(dst(), Value(TRY(reference.delete_(vm))));
return {};
}
@ -3442,7 +3413,7 @@ ThrowCompletionOr<void> TypeofBinding::execute_impl(Bytecode::Interpreter& inter
}
// 1. Let val be the result of evaluating UnaryExpression.
auto reference = TRY(vm.resolve_binding(interpreter.get_identifier(m_identifier)));
auto reference = TRY(vm.resolve_binding(interpreter.get_identifier(m_identifier), strict()));
// 2. If val is a Reference Record, then
// a. If IsUnresolvableReference(val) is true, return "undefined".