From fb05063dde0167d36e79d56e8122ad9e4cb48265 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 28 Oct 2025 20:25:12 +0100 Subject: [PATCH] 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. --- Libraries/LibJS/Bytecode/Executable.cpp | 4 +- Libraries/LibJS/Bytecode/Executable.h | 2 +- Libraries/LibJS/Bytecode/Generator.cpp | 18 +- Libraries/LibJS/Bytecode/Generator.h | 4 + Libraries/LibJS/Bytecode/Instruction.h | 6 +- Libraries/LibJS/Bytecode/Interpreter.cpp | 297 ++++++++---------- Libraries/LibJS/Forward.h | 5 + Libraries/LibJS/Parser.cpp | 2 + .../LibJS/Runtime/AbstractOperations.cpp | 24 -- Libraries/LibJS/Runtime/AbstractOperations.h | 1 - Libraries/LibJS/Runtime/Reference.cpp | 14 +- Libraries/LibJS/Runtime/Reference.h | 12 +- Libraries/LibJS/Runtime/VM.cpp | 6 +- Libraries/LibJS/Runtime/VM.h | 9 +- Tests/LibJS/test-js.cpp | 4 +- Utilities/js.cpp | 2 +- 16 files changed, 183 insertions(+), 227 deletions(-) diff --git a/Libraries/LibJS/Bytecode/Executable.cpp b/Libraries/LibJS/Bytecode/Executable.cpp index e012256418..826caf52a0 100644 --- a/Libraries/LibJS/Bytecode/Executable.cpp +++ b/Libraries/LibJS/Bytecode/Executable.cpp @@ -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); diff --git a/Libraries/LibJS/Bytecode/Executable.h b/Libraries/LibJS/Bytecode/Executable.h index 9b7a451971..9d4c9128cd 100644 --- a/Libraries/LibJS/Bytecode/Executable.h +++ b/Libraries/LibJS/Bytecode/Executable.h @@ -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; diff --git a/Libraries/LibJS/Bytecode/Generator.cpp b/Libraries/LibJS/Bytecode/Generator.cpp index 0cf1c835e1..2d727f397d 100644 --- a/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Libraries/LibJS/Bytecode/Generator.cpp @@ -218,6 +218,14 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E CodeGenerationErrorOr> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr function, MustPropagateCompletion must_propagate_completion, Vector local_variable_names) { Generator generator(vm, function, must_propagate_completion); + + if (is(node)) + generator.m_strict = static_cast(node).is_strict_mode() ? Strict::Yes : Strict::No; + else if (is(node)) + generator.m_strict = static_cast(node).in_strict_mode() ? Strict::Yes : Strict::No; + else if (is(node)) + generator.m_strict = static_cast(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> Generator::compile(VM& vm, ASTNode co } } - bool is_strict_mode = false; - if (is(node)) - is_strict_mode = static_cast(node).is_strict_mode(); - else if (is(node)) - is_strict_mode = static_cast(node).in_strict_mode(); - else if (is(node)) - is_strict_mode = static_cast(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> 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 linked_exception_handlers; diff --git a/Libraries/LibJS/Bytecode/Generator.h b/Libraries/LibJS/Bytecode/Generator.h index 9cc0512d4a..87c7d7b415 100644 --- a/Libraries/LibJS/Bytecode/Generator.h +++ b/Libraries/LibJS/Bytecode/Generator.h @@ -93,6 +93,7 @@ public: grow(sizeof(OpType)); void* slot = m_current_basic_block->data() + slot_offset; new (slot) OpType(forward(args)...); + static_cast(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)...); + static_cast(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 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 }; diff --git a/Libraries/LibJS/Bytecode/Instruction.h b/Libraries/LibJS/Bytecode/Instruction.h index 679540dbbb..1b768eeb0f 100644 --- a/Libraries/LibJS/Bytecode/Instruction.h +++ b/Libraries/LibJS/Bytecode/Instruction.h @@ -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 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 { diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 6fa3ebe5cc..cf5191cc28 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -1023,7 +1023,7 @@ inline ThrowCompletionOr get_by_value(VM& vm, Optionalinternal_get(property_key, base_value)); } -inline ThrowCompletionOr get_global(Interpreter& interpreter, IdentifierTableIndex identifier_index, GlobalVariableCache& cache) +inline ThrowCompletionOr 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 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 get_global(Interpreter& interpreter, IdentifierT cache.environment_binding_index = static_cast(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 get_global(Interpreter& interpreter, IdentifierT } template -ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional const& base_identifier, PropertyKey name, PropertyLookupCache* caches = nullptr) +ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value this_value, Value value, Optional 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(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects()); // a. Let baseObj be ? ToObject(V.[[Base]]). @@ -1277,7 +1277,7 @@ ThrowCompletionOr 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(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects()); return vm.throw_completion(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof_(vm)->utf8_string(), base.to_string_without_side_effects()); @@ -1296,14 +1296,14 @@ ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value this_value return {}; } -inline ThrowCompletionOr perform_call(Interpreter& interpreter, Value this_value, Op::CallType call_type, Value callee, ReadonlySpan argument_values) +inline ThrowCompletionOr perform_call(Interpreter& interpreter, Value this_value, Op::CallType call_type, Value callee, ReadonlySpan 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 -inline ThrowCompletionOr put_by_value(VM& vm, Value base, Optional const& base_identifier, Value property_key_value, Value value) +inline ThrowCompletionOr put_by_value(VM& vm, Value base, Optional 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 put_by_value(VM& vm, Value base, Optional(vm, base, base, value, base_identifier, property_key)); + TRY(put_by_property_key(vm, base, base, value, base_identifier, property_key, strict)); return {}; } @@ -1510,7 +1510,7 @@ struct CalleeAndThis { Value this_value; }; -inline ThrowCompletionOr get_callee_and_this_from_environment(Bytecode::Interpreter& interpreter, Utf16FlyString const& name, EnvironmentCoordinate& cache) +inline ThrowCompletionOr 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 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 append(VM& vm, Value lhs, Value rhs, bool is_spre return {}; } -inline ThrowCompletionOr 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 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 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 -static ThrowCompletionOr get_binding(Interpreter& interpreter, Operand dst, IdentifierTableIndex identifier, EnvironmentCoordinate& cache) +static ThrowCompletionOr get_binding(Interpreter& interpreter, Operand dst, IdentifierTableIndex identifier, Strict strict, EnvironmentCoordinate& cache) { auto& vm = interpreter.vm(); @@ -2312,7 +2279,7 @@ static ThrowCompletionOr 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 get_binding(Interpreter& interpreter, Operand dst ThrowCompletionOr GetBinding::execute_impl(Bytecode::Interpreter& interpreter) const { - return get_binding(interpreter, m_dst, m_identifier, m_cache); + return get_binding(interpreter, m_dst, m_identifier, strict(), m_cache); } ThrowCompletionOr GetInitializedBinding::execute_impl(Bytecode::Interpreter& interpreter) const { - return get_binding(interpreter, m_dst, m_identifier, m_cache); + return get_binding(interpreter, m_dst, m_identifier, strict(), m_cache); } ThrowCompletionOr GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const @@ -2334,6 +2301,7 @@ ThrowCompletionOr 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 GetCalleeAndThisFromEnvironment::execute_impl(Bytecode:: ThrowCompletionOr 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 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>(); - 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 SetGlobal::execute_impl(Bytecode::Interpreter& interpret cache.environment_binding_index = static_cast(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 SetGlobal::execute_impl(Bytecode::Interpreter& interpret cache.environment_binding_index = static_cast(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 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 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 -static ThrowCompletionOr initialize_or_set_binding(Interpreter& interpreter, IdentifierTableIndex identifier_index, Value value, EnvironmentCoordinate& cache) +static ThrowCompletionOr 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 initialize_or_set_binding(Interpreter& interprete if constexpr (initialization_mode == BindingInitializationMode::Initialize) { TRY(static_cast(*environment).initialize_binding_direct(vm, cache.index, value, Environment::InitializeBindingHint::Normal)); } else { - TRY(static_cast(*environment).set_mutable_binding_direct(vm, cache.index, value, vm.in_strict_mode())); + TRY(static_cast(*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 initialize_or_set_binding(Interpreter& interprete ThrowCompletionOr InitializeLexicalBinding::execute_impl(Bytecode::Interpreter& interpreter) const { - return initialize_or_set_binding(interpreter, m_identifier, interpreter.get(m_src), m_cache); + return initialize_or_set_binding(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache); } ThrowCompletionOr InitializeVariableBinding::execute_impl(Bytecode::Interpreter& interpreter) const { - return initialize_or_set_binding(interpreter, m_identifier, interpreter.get(m_src), m_cache); + return initialize_or_set_binding(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache); } ThrowCompletionOr SetLexicalBinding::execute_impl(Bytecode::Interpreter& interpreter) const { - return initialize_or_set_binding(interpreter, m_identifier, interpreter.get(m_src), m_cache); + return initialize_or_set_binding(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache); } ThrowCompletionOr SetVariableBinding::execute_impl(Bytecode::Interpreter& interpreter) const { - return initialize_or_set_binding(interpreter, m_identifier, interpreter.get(m_src), m_cache); + return initialize_or_set_binding(interpreter, m_identifier, strict(), interpreter.get(m_src), m_cache); } ThrowCompletionOr GetById::execute_impl(Bytecode::Interpreter& interpreter) const @@ -2678,90 +2646,90 @@ ThrowCompletionOr PutBySpread::execute_impl(Bytecode::Interpreter& interpr return {}; } -#define DEFINE_PUT_KIND_BY_ID(kind) \ - ThrowCompletionOr 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 PutPrivateById::execute_impl(Bytecode::Interpreter& inte ThrowCompletionOr 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 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 execute_call( Value this_value, ReadonlySpan arguments, Operand dst, - Optional const& expression_string) + Optional const& expression_string, + Strict strict) { TRY(throw_if_needed_for_call(interpreter, callee, call_type, expression_string)); @@ -2912,7 +2882,7 @@ static ThrowCompletionOr 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 execute_call( ThrowCompletionOr Call::execute_impl(Bytecode::Interpreter& interpreter) const { - return execute_call(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string); + return execute_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 CallConstruct::execute_impl(Bytecode::Interpreter& interpreter) const { - return execute_call(interpreter, interpreter.get(m_callee), js_undefined(), { m_arguments, m_argument_count }, m_dst, m_expression_string); + return execute_call(interpreter, interpreter.get(m_callee), js_undefined(), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict()); } ThrowCompletionOr CallDirectEval::execute_impl(Bytecode::Interpreter& interpreter) const { - return execute_call(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string); + return execute_call(interpreter, interpreter.get(m_callee), interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict()); } ThrowCompletionOr CallBuiltin::execute_impl(Bytecode::Interpreter& interpreter) const @@ -2946,7 +2916,7 @@ ThrowCompletionOr CallBuiltin::execute_impl(Bytecode::Interpreter& interpr return {}; } - return execute_call(interpreter, callee, interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string); + return execute_call(interpreter, callee, interpreter.get(m_this_value), { m_arguments, m_argument_count }, m_dst, m_expression_string, strict()); } template @@ -2956,7 +2926,8 @@ static ThrowCompletionOr call_with_argument_array( Value this_value, Value arguments, Operand dst, - Optional const& expression_string) + Optional const& expression_string, + Strict strict) { TRY(throw_if_needed_for_call(interpreter, callee, call_type, expression_string)); @@ -2988,7 +2959,7 @@ static ThrowCompletionOr 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 call_with_argument_array( ThrowCompletionOr CallWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const { - return call_with_argument_array(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string()); + return call_with_argument_array(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string(), strict()); } ThrowCompletionOr CallDirectEvalWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const { - return call_with_argument_array(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string()); + return call_with_argument_array(interpreter, interpreter.get(callee()), interpreter.get(this_value()), interpreter.get(arguments()), dst(), expression_string(), strict()); } ThrowCompletionOr CallConstructWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const { - return call_with_argument_array(interpreter, interpreter.get(callee()), js_undefined(), interpreter.get(arguments()), dst(), expression_string()); + return call_with_argument_array(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 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(vm, base, base_identifier, property, value)); \ + TRY(put_by_value(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(vm, base, this_value, value, {}, property_key)); \ + TRY(put_by_property_key(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 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 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 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". diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 80aa8cbe79..9828ca4b64 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -152,6 +152,11 @@ namespace JS { +enum class Strict : u8 { + No, + Yes, +}; + class ASTNode; class Accessor; class Agent; diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 2c9e780146..3b723f00d5 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index 67dbd58322..f2e020cc59 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -238,30 +238,6 @@ ThrowCompletionOr 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 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 const& current) { diff --git a/Libraries/LibJS/Runtime/AbstractOperations.h b/Libraries/LibJS/Runtime/AbstractOperations.h index 814eaaf9d2..50981c97ea 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.h +++ b/Libraries/LibJS/Runtime/AbstractOperations.h @@ -40,7 +40,6 @@ JS_API ThrowCompletionOr length_of_array_like(VM&, Object const&); ThrowCompletionOr> create_list_from_array_like(VM&, Value, Function(Value)> = {}); ThrowCompletionOr species_constructor(VM&, Object const&, FunctionObject& default_constructor); JS_API ThrowCompletionOr get_function_realm(VM&, FunctionObject const&); -ThrowCompletionOr initialize_bound_name(VM&, Utf16FlyString const&, Value, Environment*); bool is_compatible_property_descriptor(bool extensible, PropertyDescriptor&, Optional const& current); bool validate_and_apply_property_descriptor(Object*, PropertyKey const&, bool extensible, PropertyDescriptor&, Optional const& current); JS_API ThrowCompletionOr get_prototype_from_constructor(VM&, FunctionObject const& constructor, GC::Ref (Intrinsics::*intrinsic_default_prototype)()); diff --git a/Libraries/LibJS/Runtime/Reference.cpp b/Libraries/LibJS/Runtime/Reference.cpp index d4d500734f..c4e8b40a9f 100644 --- a/Libraries/LibJS/Runtime/Reference.cpp +++ b/Libraries/LibJS/Runtime/Reference.cpp @@ -25,7 +25,7 @@ ThrowCompletionOr 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 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(ErrorType::ReferenceNullishSetProperty, name(), m_base_value.to_string_without_side_effects()); // e. Return unused. @@ -69,9 +69,9 @@ ThrowCompletionOr 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(m_base_environment)->set_mutable_binding_direct(vm, m_environment_coordinate->index, value, m_strict); + return static_cast(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 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(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 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 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(ErrorType::ReferenceNullishDeleteProperty, name(), m_base_value.to_string_without_side_effects()); // f. Return deleteStatus. diff --git a/Libraries/LibJS/Runtime/Reference.h b/Libraries/LibJS/Runtime/Reference.h index 1b20017e23..0004062a9f 100644 --- a/Libraries/LibJS/Runtime/Reference.h +++ b/Libraries/LibJS/Runtime/Reference.h @@ -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 this_value, bool strict = false) + Reference(Value base, PropertyKey name, Optional 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 environment_coordinate = {}) + Reference(Environment& base, Utf16FlyString referenced_name, Strict strict, Optional 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(); } PrivateName const& private_name() const { return m_name.get(); } - 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 m_name; Optional m_this_value; - bool m_strict { false }; + Strict m_strict { Strict::No }; Optional m_environment_coordinate; }; diff --git a/Libraries/LibJS/Runtime/VM.cpp b/Libraries/LibJS/Runtime/VM.cpp index 4cd097d073..2498f1bb37 100644 --- a/Libraries/LibJS/Runtime/VM.cpp +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -287,7 +287,7 @@ void VM::gather_roots(HashMap& roots) } // 9.1.2.1 GetIdentifierReference ( env, name, strict ), https://tc39.es/ecma262/#sec-getidentifierreference -ThrowCompletionOr VM::get_identifier_reference(Environment* environment, Utf16FlyString name, bool strict, size_t hops) +ThrowCompletionOr 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 VM::get_identifier_reference(Environment* environme } // 9.4.2 ResolveBinding ( name [ , env ] ), https://tc39.es/ecma262/#sec-resolvebinding -ThrowCompletionOr VM::resolve_binding(Utf16FlyString const& name, Environment* environment) +ThrowCompletionOr 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 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); diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h index 6470d01ebc..8d138b8492 100644 --- a/Libraries/LibJS/Runtime/VM.h +++ b/Libraries/LibJS/Runtime/VM.h @@ -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 resolve_binding(Utf16FlyString const&, Environment* = nullptr); - ThrowCompletionOr get_identifier_reference(Environment*, Utf16FlyString, bool strict, size_t hops = 0); + ThrowCompletionOr resolve_binding(Utf16FlyString const&, Strict, Environment* = nullptr); + ThrowCompletionOr 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 diff --git a/Tests/LibJS/test-js.cpp b/Tests/LibJS/test-js.cpp index f2ccf88e93..386697f792 100644 --- a/Tests/LibJS/test-js.cpp +++ b/Tests/LibJS/test-js.cpp @@ -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::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)); diff --git a/Utilities/js.cpp b/Utilities/js.cpp index b07d78f69b..0c5fe40f63 100644 --- a/Utilities/js.cpp +++ b/Utilities/js.cpp @@ -766,7 +766,7 @@ static ErrorOr 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);