LibJS: Implement default values for function parameters in BC

This commit is contained in:
Ali Mohammad Pur 2022-03-14 23:32:13 +03:30 committed by Andreas Kling
parent 0b8d2fb62f
commit d75cf27e02
Notes: sideshowbarker 2024-07-17 17:26:27 +09:00
2 changed files with 40 additions and 16 deletions

View file

@ -426,8 +426,12 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
// The spec makes an iterator here to do IteratorBindingInitialization but we just do it manually // The spec makes an iterator here to do IteratorBindingInitialization but we just do it manually
auto& execution_context_arguments = vm.running_execution_context().arguments; auto& 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) { for (size_t i = 0; i < m_formal_parameters.size(); ++i) {
auto& parameter = m_formal_parameters[i]; auto& parameter = m_formal_parameters[i];
if (parameter.default_value)
++default_parameter_index;
TRY(parameter.binding.visit( TRY(parameter.binding.visit(
[&](auto const& param) -> ThrowCompletionOr<void> { [&](auto const& param) -> ThrowCompletionOr<void> {
Value argument_value; Value argument_value;
@ -439,9 +443,15 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
} else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) { } else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) {
argument_value = execution_context_arguments[i]; argument_value = execution_context_arguments[i];
} else if (parameter.default_value) { } else if (parameter.default_value) {
// FIXME: Support default arguments in the bytecode world! if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) {
if (interpreter) auto value_and_frame = bytecode_interpreter->run_and_return_frame(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr);
if (value_and_frame.value.is_error())
return value_and_frame.value.release_error();
// Resulting value is in the accumulator.
argument_value = value_and_frame.frame->registers.at(0);
} else if (interpreter) {
argument_value = TRY(parameter.default_value->execute(*interpreter, global_object())).release_value(); argument_value = TRY(parameter.default_value->execute(*interpreter, global_object())).release_value();
}
} else { } else {
argument_value = js_undefined(); argument_value = js_undefined();
} }
@ -769,24 +779,37 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
return vm.throw_completion<InternalError>(global_object(), ErrorType::NotImplemented, "Async Generator function execution"); return vm.throw_completion<InternalError>(global_object(), ErrorType::NotImplemented, "Async Generator function execution");
if (bytecode_interpreter) { if (bytecode_interpreter) {
// FIXME: pass something to evaluate default arguments with
TRY(function_declaration_instantiation(nullptr));
if (!m_bytecode_executable) { if (!m_bytecode_executable) {
auto executable_result = JS::Bytecode::Generator::generate(m_ecmascript_code, m_kind); auto compile = [&](auto& node, auto kind, auto name) -> ThrowCompletionOr<NonnullOwnPtr<Bytecode::Executable>> {
auto executable_result = JS::Bytecode::Generator::generate(node, kind);
if (executable_result.is_error()) if (executable_result.is_error())
return vm.throw_completion<InternalError>(bytecode_interpreter->global_object(), ErrorType::NotImplemented, executable_result.error().to_string()); return vm.throw_completion<InternalError>(bytecode_interpreter->global_object(), ErrorType::NotImplemented, executable_result.error().to_string());
m_bytecode_executable = executable_result.release_value(); auto bytecode_executable = executable_result.release_value();
m_bytecode_executable->name = m_name; bytecode_executable->name = name;
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
passes.perform(*m_bytecode_executable); passes.perform(*bytecode_executable);
if constexpr (JS_BYTECODE_DEBUG) { if constexpr (JS_BYTECODE_DEBUG) {
dbgln("Optimisation passes took {}us", passes.elapsed()); dbgln("Optimisation passes took {}us", passes.elapsed());
dbgln("Compiled Bytecode::Block for function '{}':", m_name); dbgln("Compiled Bytecode::Block for function '{}':", m_name);
} }
if (JS::Bytecode::g_dump_bytecode) if (JS::Bytecode::g_dump_bytecode)
m_bytecode_executable->dump(); bytecode_executable->dump();
return bytecode_executable;
};
m_bytecode_executable = TRY(compile(*m_ecmascript_code, m_kind, m_name));
size_t default_parameter_index = 0;
for (auto& parameter : m_formal_parameters) {
if (!parameter.default_value)
continue;
auto executable = TRY(compile(*parameter.default_value, FunctionKind::Normal, String::formatted("default parameter #{} for {}", default_parameter_index, m_name)));
m_default_parameter_bytecode_executables.append(move(executable));
} }
}
TRY(function_declaration_instantiation(nullptr));
auto result_and_frame = bytecode_interpreter->run_and_return_frame(*m_bytecode_executable, nullptr); auto result_and_frame = bytecode_interpreter->run_and_return_frame(*m_bytecode_executable, nullptr);
VERIFY(result_and_frame.frame != nullptr); VERIFY(result_and_frame.frame != nullptr);

View file

@ -109,6 +109,7 @@ private:
FlyString m_name; FlyString m_name;
OwnPtr<Bytecode::Executable> m_bytecode_executable; OwnPtr<Bytecode::Executable> m_bytecode_executable;
Vector<OwnPtr<Bytecode::Executable>> m_default_parameter_bytecode_executables;
i32 m_function_length { 0 }; i32 m_function_length { 0 };
// Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects