diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index efa078f9142..290829b2b64 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace JS::Bytecode { @@ -26,21 +27,190 @@ Generator::Generator(VM& vm) { } -CodeGenerationErrorOr> Generator::generate(VM& vm, ASTNode const& node, ReadonlySpan parameters, FunctionKind enclosing_function_kind) +CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function) { - Generator generator(vm); + if (function.m_has_parameter_expressions) { + emit(); + } - for (auto const& parameter : parameters) { - if (auto const* identifier = parameter.binding.get_pointer>(); - identifier && (*identifier)->is_local()) { - generator.set_local_initialized((*identifier)->local_variable_index()); + for (auto const& parameter_name : function.m_parameter_names) { + if (parameter_name.value == ECMAScriptFunctionObject::ParameterIsLocal::No) { + auto id = intern_identifier(parameter_name.key); + emit(id, Op::EnvironmentMode::Lexical, false); + if (function.m_has_duplicates) { + emit(id, add_constant(js_undefined()), next_environment_variable_cache(), Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Lexical); + } } } + if (function.m_arguments_object_needed) { + if (function.m_strict || !function.has_simple_parameter_list()) { + emit(Op::CreateArguments::Kind::Unmapped, function.m_strict); + } else { + emit(Op::CreateArguments::Kind::Mapped, function.m_strict); + } + } + + auto const& formal_parameters = function.formal_parameters(); + for (u32 param_index = 0; param_index < formal_parameters.size(); ++param_index) { + auto const& parameter = formal_parameters[param_index]; + + if (parameter.is_rest) { + auto argument_reg = allocate_register(); + emit(argument_reg.operand(), param_index); + emit(param_index, argument_reg.operand()); + } else if (parameter.default_value) { + auto& if_undefined_block = make_block(); + auto& if_not_undefined_block = make_block(); + + auto argument_reg = allocate_register(); + emit(argument_reg.operand(), param_index); + + emit( + argument_reg.operand(), + Label { if_undefined_block }, + Label { if_not_undefined_block }); + + switch_to_basic_block(if_undefined_block); + auto operand = TRY(parameter.default_value->generate_bytecode(*this)); + emit(param_index, *operand); + emit(Label { if_not_undefined_block }); + + switch_to_basic_block(if_not_undefined_block); + } + + if (auto const* identifier = parameter.binding.get_pointer>(); identifier) { + if ((*identifier)->is_local()) { + auto local_variable_index = (*identifier)->local_variable_index(); + emit(local(local_variable_index), param_index); + set_local_initialized((*identifier)->local_variable_index()); + } else { + auto id = intern_identifier((*identifier)->string()); + auto init_mode = function.m_has_duplicates ? Op::SetVariable::InitializationMode::Set : Op::SetVariable::InitializationMode::Initialize; + auto argument_reg = allocate_register(); + emit(argument_reg.operand(), param_index); + emit(id, argument_reg.operand(), + next_environment_variable_cache(), + init_mode, + Op::EnvironmentMode::Lexical); + } + } else if (auto const* binding_pattern = parameter.binding.get_pointer>(); binding_pattern) { + auto input_operand = allocate_register(); + emit(input_operand.operand(), param_index); + auto init_mode = function.m_has_duplicates ? Op::SetVariable::InitializationMode::Set : Bytecode::Op::SetVariable::InitializationMode::Initialize; + TRY((*binding_pattern)->generate_bytecode(*this, init_mode, input_operand, false)); + } + } + + ScopeNode const* scope_body = nullptr; + if (is(*function.m_ecmascript_code)) + scope_body = static_cast(function.m_ecmascript_code.ptr()); + + if (!function.m_has_parameter_expressions) { + if (scope_body) { + for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) { + auto const& id = variable_to_initialize.identifier; + if (id.is_local()) { + emit(local(id.local_variable_index()), add_constant(js_undefined())); + } else { + auto intern_id = intern_identifier(id.string()); + emit(intern_id, Op::EnvironmentMode::Var, false); + emit(intern_id, add_constant(js_undefined()), next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var); + } + } + } + } else { + emit(); + + if (scope_body) { + for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) { + auto const& id = variable_to_initialize.identifier; + auto initial_value = allocate_register(); + if (!variable_to_initialize.parameter_binding || variable_to_initialize.function_name) { + emit(initial_value, add_constant(js_undefined())); + } else { + if (id.is_local()) { + emit(initial_value, local(id.local_variable_index())); + } else { + emit(initial_value, intern_identifier(id.string()), next_environment_variable_cache()); + } + } + + if (id.is_local()) { + emit(local(id.local_variable_index()), initial_value); + } else { + auto intern_id = intern_identifier(id.string()); + emit(intern_id, Op::EnvironmentMode::Var, false); + emit(intern_id, initial_value, next_environment_variable_cache(), Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var); + } + } + } + } + + if (!function.m_strict && scope_body) { + for (auto const& function_name : function.m_function_names_to_initialize_binding) { + auto intern_id = intern_identifier(function_name); + emit(intern_id, Op::EnvironmentMode::Var, false); + emit(intern_id, add_constant(js_undefined()), next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var); + } + } + + if (!function.m_strict) { + bool can_elide_declarative_environment = !function.m_contains_direct_call_to_eval && (!scope_body || !scope_body->has_non_local_lexical_declarations()); + if (!can_elide_declarative_environment) { + emit(); + } + } + + if (scope_body) { + MUST(scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) { + MUST(declaration.for_each_bound_identifier([&](auto const& id) { + if (id.is_local()) { + return; + } + + emit(intern_identifier(id.string()), + Op::EnvironmentMode::Lexical, + declaration.is_constant_declaration(), + false, + declaration.is_constant_declaration()); + })); + })); + } + + for (auto const& declaration : function.m_functions_to_initialize) { + auto function = allocate_register(); + emit(function, declaration, OptionalNone {}); + if (declaration.name_identifier()->is_local()) { + emit(local(declaration.name_identifier()->local_variable_index()), function); + } else { + emit(intern_identifier(declaration.name()), function, next_environment_variable_cache(), Op::SetVariable::InitializationMode::Set, Op::EnvironmentMode::Var); + } + } + + return {}; +} + +CodeGenerationErrorOr> Generator::emit_function_body_bytecode(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GCPtr function) +{ + Generator generator(vm); + generator.switch_to_basic_block(generator.make_block()); SourceLocationScope scope(generator, node); generator.m_enclosing_function_kind = enclosing_function_kind; - if (generator.is_in_generator_or_async_function()) { + if (generator.is_in_async_function() && !generator.is_in_generator_function()) { + // Immediately yield with no value. + auto& start_block = generator.make_block(); + generator.emit(Label { start_block }, generator.add_constant(js_undefined())); + generator.switch_to_basic_block(start_block); + // NOTE: This doesn't have to handle received throw/return completions, as GeneratorObject::resume_abrupt + // will not enter the generator from the SuspendedStart state and immediately completes the generator. + } + + if (function) + TRY(generator.emit_function_declaration_instantiation(*function)); + + if (generator.is_in_generator_function()) { // Immediately yield with no value. auto& start_block = generator.make_block(); generator.emit(Label { start_block }, generator.add_constant(js_undefined())); @@ -72,8 +242,6 @@ CodeGenerationErrorOr> Generator::generate(VM& vm, ASTN is_strict_mode = static_cast(node).in_strict_mode(); else if (is(node)) is_strict_mode = static_cast(node).is_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) { @@ -214,6 +382,16 @@ CodeGenerationErrorOr> Generator::generate(VM& vm, ASTN return executable; } +CodeGenerationErrorOr> Generator::generate_from_ast_node(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind) +{ + return emit_function_body_bytecode(vm, node, enclosing_function_kind, {}); +} + +CodeGenerationErrorOr> Generator::generate_from_function(VM& vm, ECMAScriptFunctionObject const& function) +{ + return emit_function_body_bytecode(vm, function.ecmascript_code(), function.kind(), &function); +} + void Generator::grow(size_t additional_size) { VERIFY(m_current_basic_block); diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index eb632ff4005..f1e54fa2a04 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -32,7 +32,11 @@ public: Function, Block, }; - static CodeGenerationErrorOr> generate(VM&, ASTNode const&, ReadonlySpan parameters, FunctionKind = FunctionKind::Normal); + + static CodeGenerationErrorOr> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal); + static CodeGenerationErrorOr> generate_from_function(VM&, ECMAScriptFunctionObject const& function); + + CodeGenerationErrorOr emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function); [[nodiscard]] ScopedOperand allocate_register(); [[nodiscard]] ScopedOperand local(u32 local_index); @@ -301,6 +305,8 @@ public: private: VM& m_vm; + static CodeGenerationErrorOr> emit_function_body_bytecode(VM&, ASTNode const&, FunctionKind, GCPtr); + enum class JumpType { Continue, Break, diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 7cfd3fee448..41281fb06ef 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -30,7 +30,10 @@ O(ContinuePendingUnwind) \ O(CopyObjectExcludingProperties) \ O(CreateLexicalEnvironment) \ + O(CreateVariableEnvironment) \ O(CreateVariable) \ + O(CreateRestParams) \ + O(CreateArguments) \ O(Decrement) \ O(DeleteById) \ O(DeleteByIdWithThis) \ @@ -43,6 +46,7 @@ O(EnterObjectEnvironment) \ O(EnterUnwindContext) \ O(Exp) \ + O(GetArgument) \ O(GetById) \ O(GetByIdWithThis) \ O(GetByValue) \ @@ -114,6 +118,7 @@ O(Return) \ O(RightShift) \ O(ScheduleJump) \ + O(SetArgument) \ O(SetVariable) \ O(SetLocal) \ O(StrictlyEquals) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index c06f7b37db7..22f2895c26e 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -228,7 +228,7 @@ ThrowCompletionOr Interpreter::run(Script& script_record, JS::GCPtraccumulator(); auto& executable = current_executable(); @@ -387,6 +388,18 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) DISPATCH_NEXT(SetLocal); } + handle_GetArgument: { + auto const& instruction = *reinterpret_cast(&bytecode[program_counter]); + set(instruction.dst(), arguments[instruction.index()]); + DISPATCH_NEXT(GetArgument); + } + + handle_SetArgument: { + auto const& instruction = *reinterpret_cast(&bytecode[program_counter]); + arguments[instruction.index()] = get(instruction.src()); + DISPATCH_NEXT(SetArgument); + } + handle_Mov: { auto& instruction = *reinterpret_cast(&bytecode[program_counter]); set(instruction.dst(), get(instruction.src())); @@ -547,7 +560,10 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION(ConcatString); HANDLE_INSTRUCTION(CopyObjectExcludingProperties); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateLexicalEnvironment); + HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateVariableEnvironment); HANDLE_INSTRUCTION(CreateVariable); + HANDLE_INSTRUCTION(CreateRestParams); + HANDLE_INSTRUCTION(CreateArguments); HANDLE_INSTRUCTION(Decrement); HANDLE_INSTRUCTION(DeleteById); HANDLE_INSTRUCTION(DeleteByIdWithThis); @@ -687,6 +703,7 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe if (running_execution_context.registers.size() < executable.number_of_registers) running_execution_context.registers.resize(executable.number_of_registers); + TemporaryChange restore_arguments { m_arguments, running_execution_context.arguments.span() }; TemporaryChange restore_registers { m_registers, running_execution_context.registers.span() }; TemporaryChange restore_locals { m_locals, running_execution_context.locals.span() }; TemporaryChange restore_constants { m_constants, executable.constants.span() }; @@ -776,9 +793,26 @@ void Interpreter::enter_object_environment(Object& object) running_execution_context.lexical_environment = new_object_environment(object, true, old_environment); } -ThrowCompletionOr> compile(VM& vm, ASTNode const& node, ReadonlySpan parameters, FunctionKind kind, DeprecatedFlyString const& name) +ThrowCompletionOr> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name) { - auto executable_result = Bytecode::Generator::generate(vm, node, parameters, kind); + auto executable_result = Bytecode::Generator::generate_from_ast_node(vm, node, kind); + if (executable_result.is_error()) + return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); + + auto bytecode_executable = executable_result.release_value(); + bytecode_executable->name = name; + + if (Bytecode::g_dump_bytecode) + bytecode_executable->dump(); + + return bytecode_executable; +} + +ThrowCompletionOr> compile(VM& vm, ECMAScriptFunctionObject const& function) +{ + auto const& name = function.name(); + + auto executable_result = Bytecode::Generator::generate_from_function(vm, function); if (executable_result.is_error()) return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); @@ -1230,6 +1264,15 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment)); } +ThrowCompletionOr CreateVariableEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& running_execution_context = interpreter.vm().running_execution_context(); + auto var_environment = new_declarative_environment(*running_execution_context.lexical_environment); + running_execution_context.variable_environment = var_environment; + running_execution_context.lexical_environment = var_environment; + return {}; +} + ThrowCompletionOr EnterObjectEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { auto object = TRY(interpreter.get(m_object).to_object(interpreter.vm())); @@ -1258,6 +1301,41 @@ ThrowCompletionOr CreateVariable::execute_impl(Bytecode::Interpreter& inte return create_variable(interpreter.vm(), name, m_mode, m_is_global, m_is_immutable, m_is_strict); } +ThrowCompletionOr CreateRestParams::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto const& arguments = interpreter.vm().running_execution_context().arguments; + auto arguments_count = interpreter.vm().running_execution_context().passed_argument_count; + auto array = MUST(Array::create(interpreter.realm(), 0)); + for (size_t rest_index = m_rest_index; rest_index < arguments_count; ++rest_index) + array->indexed_properties().append(arguments[rest_index]); + interpreter.set(m_dst, array); + return {}; +} + +ThrowCompletionOr CreateArguments::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto const& function = interpreter.vm().running_execution_context().function; + auto const& arguments = interpreter.vm().running_execution_context().arguments; + auto const& environment = interpreter.vm().running_execution_context().lexical_environment; + + auto passed_arguments = ReadonlySpan { arguments.data(), interpreter.vm().running_execution_context().passed_argument_count }; + Object* arguments_object; + if (m_kind == Kind::Mapped) { + arguments_object = create_mapped_arguments_object(interpreter.vm(), *function, function->formal_parameters(), passed_arguments, *environment); + } else { + arguments_object = create_unmapped_arguments_object(interpreter.vm(), passed_arguments); + } + + if (m_is_immutable) { + MUST(environment->create_immutable_binding(interpreter.vm(), interpreter.vm().names.arguments.as_string(), false)); + } else { + MUST(environment->create_mutable_binding(interpreter.vm(), interpreter.vm().names.arguments.as_string(), false)); + } + MUST(environment->initialize_binding(interpreter.vm(), interpreter.vm().names.arguments.as_string(), arguments_object, Environment::InitializeBindingHint::Normal)); + + return {}; +} + ThrowCompletionOr SetVariable::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -1277,6 +1355,18 @@ ThrowCompletionOr SetLocal::execute_impl(Bytecode::Interpreter&) const __builtin_unreachable(); } +ThrowCompletionOr SetArgument::execute_impl(Bytecode::Interpreter&) const +{ + // Handled in the interpreter loop. + __builtin_unreachable(); +} + +ThrowCompletionOr GetArgument::execute_impl(Bytecode::Interpreter&) const +{ + // Handled in the interpreter loop. + __builtin_unreachable(); +} + ThrowCompletionOr GetById::execute_impl(Bytecode::Interpreter& interpreter) const { auto base_identifier = interpreter.current_executable().get_identifier(m_base_identifier); @@ -1959,12 +2049,27 @@ ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable co return "CreateLexicalEnvironment"sv; } +ByteString CreateVariableEnvironment::to_byte_string_impl(Bytecode::Executable const&) const +{ + return "CreateVariableEnvironment"sv; +} + ByteString CreateVariable::to_byte_string_impl(Bytecode::Executable const& executable) const { auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable"; return ByteString::formatted("CreateVariable env:{} immutable:{} global:{} {}", mode_string, m_is_immutable, m_is_global, executable.identifier_table->get(m_identifier)); } +ByteString CreateRestParams::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("CreateRestParams {}, rest_index:{}", format_operand("dst"sv, m_dst, executable), m_rest_index); +} + +ByteString CreateArguments::to_byte_string_impl(Bytecode::Executable const&) const +{ + return ByteString::formatted("CreateArguments {} immutable:{}", m_kind == Kind::Mapped ? "mapped"sv : "unmapped"sv, m_is_immutable); +} + ByteString EnterObjectEnvironment::to_byte_string_impl(Executable const& executable) const { return ByteString::formatted("EnterObjectEnvironment {}", @@ -1988,6 +2093,16 @@ ByteString SetLocal::to_byte_string_impl(Bytecode::Executable const& executable) format_operand("src"sv, src(), executable)); } +ByteString GetArgument::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("GetArgument {}, {}", index(), format_operand("dst"sv, dst(), executable)); +} + +ByteString SetArgument::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("SetArgument {}, {}", index(), format_operand("src"sv, src(), executable)); +} + static StringView property_kind_to_string(PropertyKind kind) { switch (kind) { diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index 38b26ad4f6b..ae32e8fa4d5 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -96,6 +96,7 @@ private: GCPtr m_global_object { nullptr }; GCPtr m_global_declarative_environment { nullptr }; Optional m_program_counter; + Span m_arguments; Span m_registers; Span m_locals; Span m_constants; @@ -103,6 +104,7 @@ private: extern bool g_dump_bytecode; -ThrowCompletionOr> compile(VM&, ASTNode const&, ReadonlySpan, JS::FunctionKind kind, DeprecatedFlyString const& name); +ThrowCompletionOr> compile(VM&, ASTNode const&, JS::FunctionKind kind, DeprecatedFlyString const& name); +ThrowCompletionOr> compile(VM&, ECMAScriptFunctionObject const&); } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index d74eb53b6a0..661a7b25d98 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -32,6 +32,45 @@ class FunctionExpression; namespace JS::Bytecode::Op { +class CreateRestParams final : public Instruction { +public: + CreateRestParams(Operand dst, u32 rest_index) + : Instruction(Type::CreateRestParams) + , m_dst(dst) + , m_rest_index(rest_index) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + +private: + Operand m_dst; + u32 m_rest_index; +}; + +class CreateArguments final : public Instruction { +public: + enum class Kind { + Mapped, + Unmapped, + }; + + CreateArguments(Kind kind, bool is_immutable) + : Instruction(Type::CreateArguments) + , m_kind(kind) + , m_is_immutable(is_immutable) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + +private: + Kind m_kind; + bool m_is_immutable { false }; +}; + class Mov final : public Instruction { public: Mov(Operand dst, Operand src) @@ -413,6 +452,17 @@ public: ByteString to_byte_string_impl(Bytecode::Executable const&) const; }; +class CreateVariableEnvironment final : public Instruction { +public: + explicit CreateVariableEnvironment() + : Instruction(Type::CreateVariableEnvironment) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; +}; + class EnterObjectEnvironment final : public Instruction { public: explicit EnterObjectEnvironment(Operand object) @@ -552,6 +602,46 @@ private: Operand m_src; }; +class SetArgument final : public Instruction { +public: + SetArgument(size_t index, Operand src) + : Instruction(Type::SetArgument) + , m_index(index) + , m_src(src) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + size_t index() const { return m_index; } + Operand src() const { return m_src; } + +private: + u32 m_index; + Operand m_src; +}; + +class GetArgument final : public Instruction { +public: + GetArgument(Operand dst, size_t index) + : Instruction(Type::GetArgument) + , m_index(index) + , m_dst(dst) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + u32 index() const { return m_index; } + Operand dst() const { return m_dst; } + +private: + u32 m_index; + Operand m_dst; +}; + class GetCalleeAndThisFromEnvironment final : public Instruction { public: explicit GetCalleeAndThisFromEnvironment(Operand callee, Operand this_value, IdentifierTableIndex identifier, u32 cache_index) diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index 07a8000c854..070258ffcee 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -685,7 +685,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, // 29. If result.[[Type]] is normal, then // a. Set result to the result of evaluating body. - auto executable_result = Bytecode::Generator::generate(vm, program, {}); + auto executable_result = Bytecode::Generator::generate_from_ast_node(vm, program, {}); if (executable_result.is_error()) return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 2ff33159193..822450d0163 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -383,8 +383,14 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu callee_context->locals.resize(m_local_variables_names.size()); // Non-standard + callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters.size())); callee_context->arguments.append(arguments_list.data(), arguments_list.size()); callee_context->program_counter = vm.bytecode_interpreter().program_counter(); + callee_context->passed_argument_count = arguments_list.size(); + if (arguments_list.size() < m_formal_parameters.size()) { + for (size_t i = arguments_list.size(); i < m_formal_parameters.size(); ++i) + callee_context->arguments.append(js_undefined()); + } // 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined). // NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check. @@ -454,8 +460,14 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_const callee_context->locals.resize(m_local_variables_names.size()); // Non-standard + callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters.size())); callee_context->arguments.append(arguments_list.data(), arguments_list.size()); callee_context->program_counter = vm.bytecode_interpreter().program_counter(); + callee_context->passed_argument_count = arguments_list.size(); + if (arguments_list.size() < m_formal_parameters.size()) { + for (size_t i = arguments_list.size(); i < m_formal_parameters.size(); ++i) + callee_context->arguments.append(js_undefined()); + } // 4. Let calleeContext be PrepareForOrdinaryCall(F, newTarget). // NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check. @@ -541,7 +553,6 @@ void ECMAScriptFunctionObject::visit_edges(Visitor& visitor) visitor.visit(m_name_string); visitor.visit(m_bytecode_executable); - visitor.visit(m_default_parameter_bytecode_executables); for (auto& field : m_fields) { if (auto* property_key_ptr = field.name.get_pointer(); property_key_ptr && property_key_ptr->is_symbol()) @@ -567,391 +578,6 @@ void ECMAScriptFunctionObject::make_method(Object& home_object) // 2. Return unused. } -// 10.2.11 FunctionDeclarationInstantiation ( func, argumentsList ), https://tc39.es/ecma262/#sec-functiondeclarationinstantiation -ThrowCompletionOr ECMAScriptFunctionObject::function_declaration_instantiation() -{ - auto& vm = this->vm(); - auto& realm = *vm.current_realm(); - - // 1. Let calleeContext be the running execution context. - auto& callee_context = vm.running_execution_context(); - - // 2. Let code be func.[[ECMAScriptCode]]. - ScopeNode const* scope_body = nullptr; - if (is(*m_ecmascript_code)) - scope_body = static_cast(m_ecmascript_code.ptr()); - - // NOTE: Following steps were executed in ECMAScriptFunctionObject constructor. - // 3. Let strict be func.[[Strict]]. - // 4. Let formals be func.[[FormalParameters]]. - // 5. Let parameterNames be the BoundNames of formals. - // 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false. - - // 7. Let simpleParameterList be IsSimpleParameterList of formals. - bool const simple_parameter_list = has_simple_parameter_list(); - - // NOTE: Following steps were executed in ECMAScriptFunctionObject constructor. - // 8. Let hasParameterExpressions be ContainsExpression of formals. - // 9. Let varNames be the VarDeclaredNames of code. - // 10. Let varDeclarations be the VarScopedDeclarations of code. - // 11. Let lexicalNames be the LexicallyDeclaredNames of code. - // 12. Let functionNames be a new empty List. - // 13. Let functionsToInitialize be a new empty List. - // 14. For each element d of varDeclarations, in reverse List order, do - // 15. Let argumentsObjectNeeded be true. - // 16. If func.[[ThisMode]] is lexical, then - // 17. Else if parameterNames contains "arguments", then - // 18. Else if hasParameterExpressions is false, then - - GCPtr environment; - - // 19. If strict is true or hasParameterExpressions is false, then - if (m_strict || !m_has_parameter_expressions) { - // a. NOTE: Only a single Environment Record is needed for the parameters, since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval. - // b. Let env be the LexicalEnvironment of calleeContext. - environment = callee_context.lexical_environment; - } - // 20. Else, - else { - // a. NOTE: A separate Environment Record is needed to ensure that bindings created by direct eval calls in the formal parameter list are outside the environment where parameters are declared. - - // b. Let calleeEnv be the LexicalEnvironment of calleeContext. - auto callee_env = callee_context.lexical_environment; - - // c. Let env be NewDeclarativeEnvironment(calleeEnv). - environment = new_declarative_environment(*callee_env); - - // d. Assert: The VariableEnvironment of calleeContext is calleeEnv. - VERIFY(callee_context.variable_environment == callee_context.lexical_environment); - - // e. Set the LexicalEnvironment of calleeContext to env. - callee_context.lexical_environment = environment; - } - - // 21. For each String paramName of parameterNames, do - for (auto const& parameter_name : m_parameter_names) { - // OPTIMIZATION: Parameters that are locals don't need to be added to the environment. - if (parameter_name.value == ParameterIsLocal::Yes) - continue; - - // a. Let alreadyDeclared be ! env.HasBinding(paramName). - - // b. NOTE: Early errors ensure that duplicate parameter names can only occur in non-strict functions that do not have parameter default values or rest parameters. - - // c. If alreadyDeclared is false, then - // NOTE: alreadyDeclared is always false because we use hash table for parameterNames - // i. Perform ! env.CreateMutableBinding(paramName, false). - MUST(environment->create_mutable_binding(vm, parameter_name.key, false)); - - // ii. If hasDuplicates is true, then - if (m_has_duplicates) { - // 1. Perform ! env.InitializeBinding(paramName, undefined). - MUST(environment->initialize_binding(vm, parameter_name.key, js_undefined(), Environment::InitializeBindingHint::Normal)); - } - } - - // 22. If argumentsObjectNeeded is true, then - if (m_arguments_object_needed) { - Object* arguments_object; - - // a. If strict is true or simpleParameterList is false, then - if (m_strict || !simple_parameter_list) { - // i. Let ao be CreateUnmappedArgumentsObject(argumentsList). - arguments_object = create_unmapped_arguments_object(vm, vm.running_execution_context().arguments); - } - // b. Else, - else { - // i. NOTE: A mapped argument object is only provided for non-strict functions that don't have a rest parameter, any parameter default value initializers, or any destructured parameters. - - // ii. Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, env). - arguments_object = create_mapped_arguments_object(vm, *this, formal_parameters(), vm.running_execution_context().arguments, *environment); - } - - // c. If strict is true, then - if (m_strict) { - // i. Perform ! env.CreateImmutableBinding("arguments", false). - MUST(environment->create_immutable_binding(vm, vm.names.arguments.as_string(), false)); - - // ii. NOTE: In strict mode code early errors prevent attempting to assign to this binding, so its mutability is not observable. - } - // b. Else, - else { - // i. Perform ! env.CreateMutableBinding("arguments", false). - MUST(environment->create_mutable_binding(vm, vm.names.arguments.as_string(), false)); - } - - // c. Perform ! env.InitializeBinding("arguments", ao). - MUST(environment->initialize_binding(vm, vm.names.arguments.as_string(), arguments_object, Environment::InitializeBindingHint::Normal)); - - // f. Let parameterBindings be the list-concatenation of parameterNames and « "arguments" ». - } - // 23. Else, - else { - // a. Let parameterBindings be parameterNames. - } - - // NOTE: We now treat parameterBindings as parameterNames. - - // 24. Let iteratorRecord be CreateListIteratorRecord(argumentsList). - // 25. If hasDuplicates is true, then - // a. Perform ? IteratorBindingInitialization of formals with arguments iteratorRecord and undefined. - // 26. Else, - // a. Perform ? IteratorBindingInitialization of formals with arguments iteratorRecord and env. - // NOTE: The spec makes an iterator here to do IteratorBindingInitialization but we just do it manually - auto const& execution_context_arguments = vm.running_execution_context().arguments; - - size_t default_parameter_index = 0; - for (size_t i = 0; i < m_formal_parameters.size(); ++i) { - auto& parameter = m_formal_parameters[i]; - if (parameter.default_value) - ++default_parameter_index; - - TRY(parameter.binding.visit( - [&](auto const& param) -> ThrowCompletionOr { - Value argument_value; - if (parameter.is_rest) { - auto array = MUST(Array::create(realm, 0)); - for (size_t rest_index = i; rest_index < execution_context_arguments.size(); ++rest_index) - array->indexed_properties().append(execution_context_arguments[rest_index]); - argument_value = array; - } else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) { - argument_value = execution_context_arguments[i]; - } else if (parameter.default_value) { - auto& running_execution_context = vm.running_execution_context(); - - // NOTE: Registers have to be saved and restored because executable created for default parameter uses - // running execution context. - // FIXME: This is a hack and instead instructions for default parameters should be a part of the function's bytecode. - auto saved_registers = running_execution_context.registers; - for (size_t register_index = 0; register_index < saved_registers.size(); ++register_index) - saved_registers[register_index] = {}; - - auto result_and_return_register = vm.bytecode_interpreter().run_executable(*m_default_parameter_bytecode_executables[default_parameter_index - 1], {}); - - for (size_t register_index = 0; register_index < saved_registers.size(); ++register_index) - running_execution_context.registers[register_index] = saved_registers[register_index]; - - if (result_and_return_register.value.is_error()) - return result_and_return_register.value.release_error(); - argument_value = result_and_return_register.return_register_value; - } else { - argument_value = js_undefined(); - } - - Environment* used_environment = m_has_duplicates ? nullptr : environment; - - if constexpr (IsSame const&, decltype(param)>) { - if (param->is_local()) { - callee_context.locals[param->local_variable_index()] = argument_value; - return {}; - } - Reference reference = TRY(vm.resolve_binding(param->string(), used_environment)); - // Here the difference from hasDuplicates is important - if (m_has_duplicates) - return reference.put_value(vm, argument_value); - return reference.initialize_referenced_binding(vm, argument_value); - } - if constexpr (IsSame const&, decltype(param)>) { - // Here the difference from hasDuplicates is important - return vm.binding_initialization(param, argument_value, used_environment); - } - })); - } - - GCPtr var_environment; - - // 27. If hasParameterExpressions is false, then - if (!m_has_parameter_expressions) { - // a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars. - - // b. Let instantiatedVarNames be a copy of the List parameterBindings. - // NOTE: Done in implementation of step 27.c.i.1 below - - if (scope_body) { - // NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below, - // an exception should not result from `for_each_var_declared_name`. - - // c. For each element n of varNames, do - for (auto const& variable_to_initialize : m_var_names_to_initialize_binding) { - auto const& id = variable_to_initialize.identifier; - // NOTE: Following steps were executed in ECMAScriptFunctionObject constructor. - // i. If instantiatedVarNames does not contain n, then - // 1. Append n to instantiatedVarNames. - if (id.is_local()) { - callee_context.locals[id.local_variable_index()] = js_undefined(); - } else { - // 2. Perform ! env.CreateMutableBinding(n, false). - // 3. Perform ! env.InitializeBinding(n, undefined). - MUST(environment->create_mutable_binding(vm, id.string(), false)); - MUST(environment->initialize_binding(vm, id.string(), js_undefined(), Environment::InitializeBindingHint::Normal)); - } - } - } - - // d.Let varEnv be env - var_environment = environment; - } - // 28. Else, - else { - // a. NOTE: A separate Environment Record is needed to ensure that closures created by expressions in the formal parameter list do not have visibility of declarations in the function body. - - // b. Let varEnv be NewDeclarativeEnvironment(env). - var_environment = new_declarative_environment(*environment); - static_cast(var_environment.ptr())->ensure_capacity(m_var_environment_bindings_count); - - // c. Set the VariableEnvironment of calleeContext to varEnv. - callee_context.variable_environment = var_environment; - - // d. Let instantiatedVarNames be a new empty List. - // NOTE: Already done above. - - if (scope_body) { - // NOTE: Due to the use of MUST with `create_mutable_binding`, `get_binding_value` and `initialize_binding` below, - // an exception should not result from `for_each_var_declared_name`. - - // e. For each element n of varNames, do - for (auto const& variable_to_initialize : m_var_names_to_initialize_binding) { - auto const& id = variable_to_initialize.identifier; - - // NOTE: Following steps were executed in ECMAScriptFunctionObject constructor. - // i. If instantiatedVarNames does not contain n, then - // 1. Append n to instantiatedVarNames. - - // 2. Perform ! varEnv.CreateMutableBinding(n, false). - // NOTE: We ignore locals because they are stored in ExecutionContext instead of environment. - if (!id.is_local()) - MUST(var_environment->create_mutable_binding(vm, id.string(), false)); - - Value initial_value; - - // 3. If parameterBindings does not contain n, or if functionNames contains n, then - if (!variable_to_initialize.parameter_binding || variable_to_initialize.function_name) { - // a. Let initialValue be undefined. - initial_value = js_undefined(); - } - // 4. Else, - else { - // a. Let initialValue be ! env.GetBindingValue(n, false). - if (id.is_local()) { - initial_value = callee_context.locals[id.local_variable_index()]; - } else { - initial_value = MUST(environment->get_binding_value(vm, id.string(), false)); - } - } - - // 5. Perform ! varEnv.InitializeBinding(n, initialValue). - if (id.is_local()) { - // NOTE: Local variables are supported only in bytecode interpreter - callee_context.locals[id.local_variable_index()] = initial_value; - } else { - MUST(var_environment->initialize_binding(vm, id.string(), initial_value, Environment::InitializeBindingHint::Normal)); - } - - // 6. NOTE: A var with the same name as a formal parameter initially has the same value as the corresponding initialized parameter. - } - } - } - - // 29. NOTE: Annex B.3.2.1 adds additional steps at this point. - // B.3.2.1 Changes to FunctionDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation - if (!m_strict && scope_body) { - // NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below, - // an exception should not result from `for_each_function_hoistable_with_annexB_extension`. - for (auto const& function_name : m_function_names_to_initialize_binding) { - MUST(var_environment->create_mutable_binding(vm, function_name, false)); - MUST(var_environment->initialize_binding(vm, function_name, js_undefined(), Environment::InitializeBindingHint::Normal)); - } - } - - GCPtr lex_environment; - - // 30. If strict is false, then - if (!m_strict) { - // Optimization: We avoid creating empty top-level declarative environments in non-strict mode, if both of these conditions are true: - // 1. there is no direct call to eval() within this function - // 2. there are no lexical declarations that would go into the environment - bool can_elide_declarative_environment = !m_contains_direct_call_to_eval && (!scope_body || !scope_body->has_non_local_lexical_declarations()); - if (can_elide_declarative_environment) { - lex_environment = var_environment; - } else { - // a. Let lexEnv be NewDeclarativeEnvironment(varEnv). - // b. NOTE: Non-strict functions use a separate Environment Record for top-level lexical declarations so that a direct eval - // can determine whether any var scoped declarations introduced by the eval code conflict with pre-existing top-level - // lexically scoped declarations. This is not needed for strict functions because a strict direct eval always places - // all declarations into a new Environment Record. - lex_environment = new_declarative_environment(*var_environment); - static_cast(lex_environment.ptr())->ensure_capacity(m_lex_environment_bindings_count); - } - } - // 31. Else, - else { - // a. let lexEnv be varEnv. - lex_environment = var_environment; - } - - // 32. Set the LexicalEnvironment of calleeContext to lexEnv. - callee_context.lexical_environment = lex_environment; - - if (!scope_body) - return {}; - - // 33. Let lexDeclarations be the LexicallyScopedDeclarations of code. - // 34. For each element d of lexDeclarations, do - // NOTE: Due to the use of MUST in the callback, an exception should not result from `for_each_lexically_scoped_declaration`. - if (scope_body->has_lexical_declarations()) { - MUST(scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) { - // NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below, - // an exception should not result from `for_each_bound_name`. - - // a. NOTE: A lexically declared name cannot be the same as a function/generator declaration, formal parameter, or a var name. Lexically declared names are only instantiated here but not initialized. - - // b. For each element dn of the BoundNames of d, do - MUST(declaration.for_each_bound_identifier([&](auto const& id) { - if (id.is_local()) { - // NOTE: Local variables are supported only in bytecode interpreter - return; - } - - // i. If IsConstantDeclaration of d is true, then - if (declaration.is_constant_declaration()) { - // 1. Perform ! lexEnv.CreateImmutableBinding(dn, true). - MUST(lex_environment->create_immutable_binding(vm, id.string(), true)); - } - // ii. Else, - else { - // 1. Perform ! lexEnv.CreateMutableBinding(dn, false). - MUST(lex_environment->create_mutable_binding(vm, id.string(), false)); - } - })); - })); - } - - // 35. Let privateEnv be the PrivateEnvironment of calleeContext. - auto private_environment = callee_context.private_environment; - - // 36. For each Parse Node f of functionsToInitialize, do - for (auto& declaration : m_functions_to_initialize) { - // a. Let fn be the sole element of the BoundNames of f. - // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. - auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), lex_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.uses_this(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval()); - - // c. Perform ! varEnv.SetMutableBinding(fn, fo, false). - if (declaration.name_identifier()->is_local()) { - callee_context.locals[declaration.name_identifier()->local_variable_index()] = function; - } else { - MUST(var_environment->set_mutable_binding(vm, declaration.name(), function, false)); - } - } - - if (is(*lex_environment)) - static_cast(lex_environment.ptr())->shrink_to_fit(); - if (lex_environment != var_environment && is(*var_environment)) - static_cast(var_environment.ptr())->shrink_to_fit(); - - // 37. Return unused. - return {}; -} - // 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall ThrowCompletionOr ECMAScriptFunctionObject::prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target) { @@ -1120,7 +746,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro if constexpr (!IsCallableWithArguments) { // a. Let result be the result of evaluating asyncBody. // FIXME: Cache this executable somewhere. - auto maybe_executable = Bytecode::compile(vm, async_body, {}, FunctionKind::Async, "AsyncBlockStart"sv); + auto maybe_executable = Bytecode::compile(vm, async_body, FunctionKind::Async, "AsyncBlockStart"sv); if (maybe_executable.is_error()) result = maybe_executable.release_error(); else @@ -1204,53 +830,17 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() auto& vm = this->vm(); auto& realm = *vm.current_realm(); - // NOTE: There's a subtle ordering issue here: - // - We have to compile the default parameter values before instantiating the function. - // - We have to instantiate the function before compiling the function body. - // This is why FunctionDeclarationInstantiation is invoked in the middle. - // The issue is that FunctionDeclarationInstantiation may mark certain functions as hoisted - // per Annex B. This affects code generation for FunctionDeclaration nodes. - if (!m_bytecode_executable) { - size_t default_parameter_index = 0; - for (auto& parameter : m_formal_parameters) { - if (!parameter.default_value) - continue; - if (parameter.bytecode_executable.is_null()) { - auto executable = TRY(Bytecode::compile(vm, *parameter.default_value, {}, FunctionKind::Normal, ByteString::formatted("default parameter #{} for {}", default_parameter_index++, m_name))); - const_cast(parameter).bytecode_executable = executable; - m_default_parameter_bytecode_executables.append(move(executable)); + if (!m_ecmascript_code->bytecode_executable()) { + if (is_module_wrapper()) { + const_cast(*m_ecmascript_code).set_bytecode_executable(TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name))); } else { - m_default_parameter_bytecode_executables.append(*parameter.bytecode_executable); + const_cast(*m_ecmascript_code).set_bytecode_executable(TRY(Bytecode::compile(vm, *this))); } } - } - - auto declaration_result = [&]() -> ThrowCompletionOr { - if (is_module_wrapper()) - return {}; - return function_declaration_instantiation(); - }(); - - if (m_kind == FunctionKind::Normal || m_kind == FunctionKind::Generator || m_kind == FunctionKind::AsyncGenerator) { - if (declaration_result.is_error()) - return declaration_result.release_error(); - } - - if (!m_bytecode_executable) { - if (!m_ecmascript_code->bytecode_executable()) - const_cast(*m_ecmascript_code).set_bytecode_executable(TRY(Bytecode::compile(vm, *m_ecmascript_code, m_formal_parameters, m_kind, m_name))); m_bytecode_executable = m_ecmascript_code->bytecode_executable(); } - if (m_kind == FunctionKind::Async) { - if (declaration_result.is_throw_completion()) { - auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor())); - MUST(call(vm, *promise_capability->reject(), js_undefined(), *declaration_result.throw_completion().value())); - return Completion { Completion::Type::Return, promise_capability->promise() }; - } - } - auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, {}); if (result_and_frame.value.is_error()) diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index 425c126368f..360e2f4deaa 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -53,7 +54,7 @@ public: void set_is_module_wrapper(bool b) { m_is_module_wrapper = b; } Statement const& ecmascript_code() const { return m_ecmascript_code; } - Vector const& formal_parameters() const { return m_formal_parameters; } + Vector const& formal_parameters() const override { return m_formal_parameters; } virtual DeprecatedFlyString const& name() const override { return m_name; } void set_name(DeprecatedFlyString const& name); @@ -98,6 +99,8 @@ public: Variant const& class_field_initializer_name() const { return m_class_field_initializer_name; } + friend class Bytecode::Generator; + protected: virtual bool is_strict_mode() const final { return m_strict; } @@ -112,13 +115,10 @@ private: ThrowCompletionOr prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target); void ordinary_call_bind_this(ExecutionContext&, Value this_argument); - ThrowCompletionOr function_declaration_instantiation(); - DeprecatedFlyString m_name; GCPtr m_name_string; GCPtr m_bytecode_executable; - Vector> m_default_parameter_bytecode_executables; i32 m_function_length { 0 }; Vector m_local_variables_names; diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp b/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp index 52ed6025763..7208d0d5a31 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp @@ -42,6 +42,7 @@ NonnullOwnPtr ExecutionContext::copy() const copy->is_strict_mode = is_strict_mode; copy->executable = executable; copy->arguments = arguments; + copy->passed_argument_count = passed_argument_count; copy->locals = locals; copy->registers = registers; copy->unwind_contexts = unwind_contexts; diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h index f5c9780bf1c..55bfb3e51b1 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h @@ -73,6 +73,8 @@ public: return locals[index]; } + u32 passed_argument_count { 0 }; + Vector arguments; Vector locals; Vector registers; diff --git a/Userland/Libraries/LibJS/Runtime/FunctionObject.h b/Userland/Libraries/LibJS/Runtime/FunctionObject.h index 238be5c5de6..a5be65df29d 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/FunctionObject.h @@ -40,6 +40,8 @@ public: virtual Vector const& local_variables_names() const { VERIFY_NOT_REACHED(); } + virtual Vector const& formal_parameters() const { VERIFY_NOT_REACHED(); } + protected: explicit FunctionObject(Realm&, Object* prototype, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No); explicit FunctionObject(Object& prototype, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No); diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp index 142b11a0a34..97f153cb0ed 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -174,7 +174,7 @@ ThrowCompletionOr perform_shadow_realm_eval(VM& vm, StringView source_tex // 17. If result.[[Type]] is normal, then if (!eval_result.is_throw_completion()) { // a. Set result to the result of evaluating body. - auto maybe_executable = Bytecode::compile(vm, program, {}, FunctionKind::Normal, "ShadowRealmEval"sv); + auto maybe_executable = Bytecode::compile(vm, program, FunctionKind::Normal, "ShadowRealmEval"sv); if (maybe_executable.is_error()) result = maybe_executable.release_error(); else { diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 9ad9d49211c..7baa92ad0d7 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -291,7 +291,7 @@ ThrowCompletionOr VM::execute_ast_node(ASTNode const& node) { // FIXME: This function should be gone once we will emit bytecode for everything before executing instructions. - auto executable = TRY(Bytecode::compile(*this, node, {}, FunctionKind::Normal, ""sv)); + auto executable = TRY(Bytecode::compile(*this, node, FunctionKind::Normal, ""sv)); auto& running_execution_context = this->running_execution_context(); // Registers have to be saved and restored because executable for compiled ASTNode does not have its own execution context diff --git a/Userland/Libraries/LibJS/SourceTextModule.cpp b/Userland/Libraries/LibJS/SourceTextModule.cpp index 90843971c49..65da8ffd6ce 100644 --- a/Userland/Libraries/LibJS/SourceTextModule.cpp +++ b/Userland/Libraries/LibJS/SourceTextModule.cpp @@ -711,7 +711,7 @@ ThrowCompletionOr SourceTextModule::execute_module(VM& vm, GCPtr