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
16 changed files with 183 additions and 227 deletions

View File

@ -25,7 +25,7 @@ Executable::Executable(
size_t number_of_property_lookup_caches,
size_t number_of_global_variable_caches,
size_t number_of_registers,
bool is_strict_mode)
Strict strict)
: bytecode(move(bytecode))
, string_table(move(string_table))
, identifier_table(move(identifier_table))
@ -33,7 +33,7 @@ Executable::Executable(
, constants(move(constants))
, source_code(move(source_code))
, number_of_registers(number_of_registers)
, is_strict_mode(is_strict_mode)
, is_strict_mode(strict == Strict::Yes)
{
property_lookup_caches.resize(number_of_property_lookup_caches);
global_variable_caches.resize(number_of_global_variable_caches);

View File

@ -75,7 +75,7 @@ public:
size_t number_of_property_lookup_caches,
size_t number_of_global_variable_caches,
size_t number_of_registers,
bool is_strict_mode);
Strict);
virtual ~Executable() override;

View File

@ -218,6 +218,14 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr<ECMAScriptFunctionObject const> function, MustPropagateCompletion must_propagate_completion, Vector<LocalVariable> local_variable_names)
{
Generator generator(vm, function, must_propagate_completion);
if (is<Program>(node))
generator.m_strict = static_cast<Program const&>(node).is_strict_mode() ? Strict::Yes : Strict::No;
else if (is<FunctionBody>(node))
generator.m_strict = static_cast<FunctionBody const&>(node).in_strict_mode() ? Strict::Yes : Strict::No;
else if (is<FunctionDeclaration>(node))
generator.m_strict = static_cast<FunctionDeclaration const&>(node).is_strict_mode() ? Strict::Yes : Strict::No;
generator.m_local_variables = local_variable_names;
generator.switch_to_basic_block(generator.make_block());
@ -260,14 +268,6 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
}
}
bool is_strict_mode = false;
if (is<Program>(node))
is_strict_mode = static_cast<Program const&>(node).is_strict_mode();
else if (is<FunctionBody>(node))
is_strict_mode = static_cast<FunctionBody const&>(node).in_strict_mode();
else if (is<FunctionDeclaration>(node))
is_strict_mode = static_cast<FunctionDeclaration const&>(node).is_strict_mode();
size_t size_needed = 0;
for (auto& block : generator.m_root_basic_blocks) {
size_needed += block->size();
@ -453,7 +453,7 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
generator.m_next_property_lookup_cache,
generator.m_next_global_variable_cache,
generator.m_next_register,
is_strict_mode);
generator.m_strict);
Vector<Executable::ExceptionHandlers> linked_exception_handlers;

View File

@ -93,6 +93,7 @@ public:
grow(sizeof(OpType));
void* slot = m_current_basic_block->data() + slot_offset;
new (slot) OpType(forward<Args>(args)...);
static_cast<OpType*>(slot)->set_strict(m_strict);
if constexpr (OpType::IsTerminator)
m_current_basic_block->terminate({});
m_current_basic_block->add_source_map_entry(slot_offset, { m_current_ast_node->start_offset(), m_current_ast_node->end_offset() });
@ -110,6 +111,7 @@ public:
grow(size_to_allocate);
void* slot = m_current_basic_block->data() + slot_offset;
new (slot) OpType(forward<Args>(args)...);
static_cast<OpType*>(slot)->set_strict(m_strict);
if constexpr (OpType::IsTerminator)
m_current_basic_block->terminate({});
m_current_basic_block->add_source_map_entry(slot_offset, { m_current_ast_node->start_offset(), m_current_ast_node->end_offset() });
@ -383,6 +385,8 @@ private:
Vector<FlyString> language_label_set;
};
Strict m_strict { Strict::No };
BasicBlock* m_current_basic_block { nullptr };
ASTNode const* m_current_ast_node { nullptr };
UnwindContext const* m_current_unwind_context { nullptr };

View File

@ -188,7 +188,7 @@ public:
constexpr static bool IsTerminator = false;
static constexpr bool IsVariableLength = false;
enum class Type {
enum class Type : u8 {
#define __BYTECODE_OP(op) \
op,
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
@ -202,6 +202,9 @@ public:
void visit_operands(Function<void(Operand&)> visitor);
static void destroy(Instruction&);
Strict strict() const { return m_strict; }
void set_strict(Strict strict) { m_strict = strict; }
protected:
explicit Instruction(Type type)
: m_type(type)
@ -213,6 +216,7 @@ protected:
private:
Type m_type {};
Strict m_strict {};
};
class InstructionStreamIterator {

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

View File

@ -152,6 +152,11 @@
namespace JS {
enum class Strict : u8 {
No,
Yes,
};
class ASTNode;
class Accessor;
class Agent;

View File

@ -810,6 +810,8 @@ void Parser::parse_script(Program& program, bool starts_in_strict_mode)
void Parser::parse_module(Program& program)
{
program.set_strict_mode();
TemporaryChange strict_mode_rollback(m_state.strict_mode, true);
TemporaryChange await_expression_valid_rollback(m_state.await_expression_is_valid, true);

View File

@ -238,30 +238,6 @@ ThrowCompletionOr<Realm*> get_function_realm(VM& vm, FunctionObject const& funct
return vm.current_realm();
}
// 8.5.2.1 InitializeBoundName ( name, value, environment ), https://tc39.es/ecma262/#sec-initializeboundname
ThrowCompletionOr<void> initialize_bound_name(VM& vm, Utf16FlyString const& name, Value value, Environment* environment)
{
// 1. If environment is not undefined, then
if (environment) {
// FIXME: The normal is not included in the explicit resource management spec yet, so there is no spec link for it.
// a. Perform ! environment.InitializeBinding(name, value, normal).
MUST(environment->initialize_binding(vm, name, value, Environment::InitializeBindingHint::Normal));
// b. Return unused.
return {};
}
// 2. Else,
else {
// a. Let lhs be ? ResolveBinding(name).
auto lhs = TRY(vm.resolve_binding(name));
// b. Return ? PutValue(lhs, value).
return TRY(lhs.put_value(vm, value));
}
VERIFY_NOT_REACHED();
}
// 10.1.6.2 IsCompatiblePropertyDescriptor ( Extensible, Desc, Current ), https://tc39.es/ecma262/#sec-iscompatiblepropertydescriptor
bool is_compatible_property_descriptor(bool extensible, PropertyDescriptor& descriptor, Optional<PropertyDescriptor> const& current)
{

View File

@ -40,7 +40,6 @@ JS_API ThrowCompletionOr<size_t> length_of_array_like(VM&, Object const&);
ThrowCompletionOr<GC::RootVector<Value>> create_list_from_array_like(VM&, Value, Function<ThrowCompletionOr<void>(Value)> = {});
ThrowCompletionOr<FunctionObject*> species_constructor(VM&, Object const&, FunctionObject& default_constructor);
JS_API ThrowCompletionOr<Realm*> get_function_realm(VM&, FunctionObject const&);
ThrowCompletionOr<void> initialize_bound_name(VM&, Utf16FlyString const&, Value, Environment*);
bool is_compatible_property_descriptor(bool extensible, PropertyDescriptor&, Optional<PropertyDescriptor> const& current);
bool validate_and_apply_property_descriptor(Object*, PropertyKey const&, bool extensible, PropertyDescriptor&, Optional<PropertyDescriptor> const& current);
JS_API ThrowCompletionOr<Object*> get_prototype_from_constructor(VM&, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)());

View File

@ -25,7 +25,7 @@ ThrowCompletionOr<void> Reference::put_value(VM& vm, Value value)
// 4. If IsUnresolvableReference(V) is true, then
if (is_unresolvable()) {
// a. If V.[[Strict]] is true, throw a ReferenceError exception.
if (m_strict)
if (m_strict == Strict::Yes)
return throw_reference_error(vm);
// b. Let globalObj be GetGlobalObject().
@ -53,7 +53,7 @@ ThrowCompletionOr<void> Reference::put_value(VM& vm, Value value)
auto succeeded = TRY(base_obj->internal_set(name(), value, get_this_value()));
// d. If succeeded is false and V.[[Strict]] is true, throw a TypeError exception.
if (!succeeded && m_strict)
if (!succeeded && m_strict == Strict::Yes)
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name(), m_base_value.to_string_without_side_effects());
// e. Return unused.
@ -69,9 +69,9 @@ ThrowCompletionOr<void> Reference::put_value(VM& vm, Value value)
// c. Return ? base.SetMutableBinding(V.[[ReferencedName]], W, V.[[Strict]]) (see 9.1).
if (m_environment_coordinate.has_value())
return static_cast<DeclarativeEnvironment*>(m_base_environment)->set_mutable_binding_direct(vm, m_environment_coordinate->index, value, m_strict);
return static_cast<DeclarativeEnvironment*>(m_base_environment)->set_mutable_binding_direct(vm, m_environment_coordinate->index, value, m_strict == Strict::Yes);
else
return m_base_environment->set_mutable_binding(vm, name().as_string(), value, m_strict);
return m_base_environment->set_mutable_binding(vm, name().as_string(), value, m_strict == Strict::Yes);
}
Completion Reference::throw_reference_error(VM& vm) const
@ -144,7 +144,7 @@ ThrowCompletionOr<Value> Reference::get_value(VM& vm) const
// c. Return ? base.GetBindingValue(V.[[ReferencedName]], V.[[Strict]]) (see 9.1).
if (m_environment_coordinate.has_value())
return static_cast<DeclarativeEnvironment*>(m_base_environment)->get_binding_value_direct(vm, m_environment_coordinate->index);
return m_base_environment->get_binding_value(vm, name().as_string(), m_strict);
return m_base_environment->get_binding_value(vm, name().as_string(), m_strict == Strict::Yes);
}
// 13.5.1.2 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation
@ -161,7 +161,7 @@ ThrowCompletionOr<bool> Reference::delete_(VM& vm)
// 4. If IsUnresolvableReference(ref) is true, then
if (is_unresolvable()) {
// a. Assert: ref.[[Strict]] is false.
VERIFY(!m_strict);
VERIFY(m_strict == Strict::No);
// b. Return true.
return true;
}
@ -182,7 +182,7 @@ ThrowCompletionOr<bool> Reference::delete_(VM& vm)
bool delete_status = TRY(base_obj->internal_delete(name()));
// e. If deleteStatus is false and ref.[[Strict]] is true, throw a TypeError exception.
if (!delete_status && m_strict)
if (!delete_status && m_strict == Strict::Yes)
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishDeleteProperty, name(), m_base_value.to_string_without_side_effects());
// f. Return deleteStatus.

View File

@ -24,14 +24,14 @@ public:
Environment,
};
Reference(BaseType type, PropertyKey name, bool strict)
Reference(BaseType type, PropertyKey name, Strict strict)
: m_base_type(type)
, m_name(move(name))
, m_strict(strict)
{
}
Reference(Value base, PropertyKey name, Optional<Value> this_value, bool strict = false)
Reference(Value base, PropertyKey name, Optional<Value> this_value, Strict strict)
: m_base_type(BaseType::Value)
, m_base_value(base)
, m_name(move(name))
@ -40,7 +40,7 @@ public:
{
}
Reference(Environment& base, Utf16FlyString referenced_name, bool strict = false, Optional<EnvironmentCoordinate> environment_coordinate = {})
Reference(Environment& base, Utf16FlyString referenced_name, Strict strict, Optional<EnvironmentCoordinate> environment_coordinate = {})
: m_base_type(BaseType::Environment)
, m_base_environment(&base)
, m_name(move(referenced_name))
@ -53,7 +53,7 @@ public:
: m_base_type(BaseType::Value)
, m_base_value(base)
, m_name(move(name))
, m_strict(true)
, m_strict(Strict::Yes)
{
}
@ -71,7 +71,7 @@ public:
PropertyKey const& name() const { return m_name.get<PropertyKey>(); }
PrivateName const& private_name() const { return m_name.get<PrivateName>(); }
bool is_strict() const { return m_strict; }
bool is_strict() const { return m_strict == Strict::Yes; }
// 6.2.4.2 IsUnresolvableReference ( V ), https://tc39.es/ecma262/#sec-isunresolvablereference
bool is_unresolvable() const { return m_base_type == BaseType::Unresolvable; }
@ -133,7 +133,7 @@ private:
};
Variant<PropertyKey, PrivateName> m_name;
Optional<Value> m_this_value;
bool m_strict { false };
Strict m_strict { Strict::No };
Optional<EnvironmentCoordinate> m_environment_coordinate;
};

View File

@ -287,7 +287,7 @@ void VM::gather_roots(HashMap<GC::Cell*, GC::HeapRoot>& roots)
}
// 9.1.2.1 GetIdentifierReference ( env, name, strict ), https://tc39.es/ecma262/#sec-getidentifierreference
ThrowCompletionOr<Reference> VM::get_identifier_reference(Environment* environment, Utf16FlyString name, bool strict, size_t hops)
ThrowCompletionOr<Reference> VM::get_identifier_reference(Environment* environment, Utf16FlyString name, Strict strict, size_t hops)
{
// 1. If env is the value null, then
if (!environment) {
@ -321,7 +321,7 @@ ThrowCompletionOr<Reference> VM::get_identifier_reference(Environment* environme
}
// 9.4.2 ResolveBinding ( name [ , env ] ), https://tc39.es/ecma262/#sec-resolvebinding
ThrowCompletionOr<Reference> VM::resolve_binding(Utf16FlyString const& name, Environment* environment)
ThrowCompletionOr<Reference> VM::resolve_binding(Utf16FlyString const& name, Strict strict, Environment* environment)
{
// 1. If env is not present or if env is undefined, then
if (!environment) {
@ -333,7 +333,7 @@ ThrowCompletionOr<Reference> VM::resolve_binding(Utf16FlyString const& name, Env
VERIFY(environment);
// 3. If the source text matched by the syntactic production that is being evaluated is contained in strict mode code, let strict be true; else let strict be false.
bool strict = in_strict_mode();
// NOTE: We take this as a parameter.
// 4. Return ? GetIdentifierReference(env, name, strict).
return get_identifier_reference(environment, name, strict);

View File

@ -174,11 +174,6 @@ public:
FunctionObject const* active_function_object() const { return running_execution_context().function; }
FunctionObject* active_function_object() { return running_execution_context().function; }
bool in_strict_mode() const
{
return running_execution_context().is_strict_mode;
}
size_t argument_count() const
{
return running_execution_context().arguments.size();
@ -204,8 +199,8 @@ public:
u32 execution_generation() const { return m_execution_generation; }
void finish_execution_generation() { ++m_execution_generation; }
ThrowCompletionOr<Reference> resolve_binding(Utf16FlyString const&, Environment* = nullptr);
ThrowCompletionOr<Reference> get_identifier_reference(Environment*, Utf16FlyString, bool strict, size_t hops = 0);
ThrowCompletionOr<Reference> resolve_binding(Utf16FlyString const&, Strict, Environment* = nullptr);
ThrowCompletionOr<Reference> get_identifier_reference(Environment*, Utf16FlyString, Strict, size_t hops = 0);
// 5.2.3.2 Throw an Exception, https://tc39.es/ecma262/#sec-throw-an-exception
template<typename T, typename... Args>

View File

@ -19,7 +19,7 @@ TESTJS_PROGRAM_FLAG(test262_parser_tests, "Run test262 parser tests", "test262-p
TESTJS_GLOBAL_FUNCTION(is_strict_mode, isStrictMode, 0)
{
return JS::Value(vm.in_strict_mode());
return JS::Value(vm.running_execution_context().is_strict_mode);
}
TESTJS_GLOBAL_FUNCTION(can_parse_source, canParseSource)
@ -83,7 +83,7 @@ TESTJS_GLOBAL_FUNCTION(mark_as_garbage, markAsGarbage)
if (!outer_environment.has_value())
return vm.throw_completion<JS::ReferenceError>(JS::ErrorType::UnknownIdentifier, variable_name.utf8_string_view());
auto reference = TRY(vm.resolve_binding(variable_name.utf16_string(), outer_environment.value()->lexical_environment));
auto reference = TRY(vm.resolve_binding(variable_name.utf16_string(), JS::Strict::No, outer_environment.value()->lexical_environment));
auto value = TRY(reference.get_value(vm));

View File

@ -766,7 +766,7 @@ static ErrorOr<int> run_repl(bool gc_on_every_allocation, bool syntax_highlight)
switch (mode) {
case CompleteProperty: {
auto reference_or_error = g_vm->resolve_binding(variable_name, &global_environment);
auto reference_or_error = g_vm->resolve_binding(variable_name, JS::Strict::No, &global_environment);
if (reference_or_error.is_error())
return {};
auto value_or_error = reference_or_error.value().get_value(*g_vm);