1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-28 04:45:09 +00:00

LibJS/Bytecode: Add codegen for "named evaluation if anonymous function"

This gives anonymous functions the name from the LHS they are being
assigned to.

171 new passes on test262. :^)
This commit is contained in:
Andreas Kling 2023-06-23 14:27:42 +02:00
parent a92d94f4e9
commit 85a3a1c085
8 changed files with 109 additions and 27 deletions

View file

@ -302,13 +302,12 @@ Completion FunctionExpression::execute(Interpreter& interpreter) const
InterpreterNodeScope node_scope { interpreter, *this };
// 1. Return InstantiateOrdinaryFunctionExpression of FunctionExpression.
return instantiate_ordinary_function_expression(interpreter, name());
return instantiate_ordinary_function_expression(interpreter.vm(), name());
}
// 15.2.5 Runtime Semantics: InstantiateOrdinaryFunctionExpression, https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
Value FunctionExpression::instantiate_ordinary_function_expression(Interpreter& interpreter, DeprecatedFlyString given_name) const
Value FunctionExpression::instantiate_ordinary_function_expression(VM& vm, DeprecatedFlyString given_name) const
{
auto& vm = interpreter.vm();
auto& realm = *vm.current_realm();
if (given_name.is_empty())
@ -316,7 +315,7 @@ Value FunctionExpression::instantiate_ordinary_function_expression(Interpreter&
auto has_own_name = !name().is_empty();
auto const& used_name = has_own_name ? name() : given_name;
auto environment = NonnullGCPtr { *interpreter.lexical_environment() };
auto environment = NonnullGCPtr { *vm.running_execution_context().lexical_environment };
if (has_own_name) {
VERIFY(environment);
environment = new_declarative_environment(*environment);

View file

@ -734,10 +734,11 @@ public:
virtual void dump(int indent) const override;
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional<DeprecatedFlyString const&> lhs_name) const;
bool has_name() const { return !name().is_empty(); }
Value instantiate_ordinary_function_expression(Interpreter&, DeprecatedFlyString given_name) const;
Value instantiate_ordinary_function_expression(VM&, DeprecatedFlyString given_name) const;
private:
virtual bool is_function_expression() const override { return true; }
@ -1414,6 +1415,7 @@ public:
virtual Completion execute(Interpreter&) const override;
virtual void dump(int indent) const override;
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional<DeprecatedFlyString const&> lhs_name) const;
bool has_name() const { return !m_name.is_empty(); }

View file

@ -403,7 +403,11 @@ Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(By
// d. Else,
// i. Let rref be the result of evaluating AssignmentExpression.
// ii. Let rval be ? GetValue(rref).
if (lhs->is_identifier()) {
TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, static_cast<Identifier const&>(*lhs).string()));
} else {
TRY(m_rhs->generate_bytecode(generator));
}
// e. Perform ? PutValue(lref, rval).
if (is<Identifier>(*lhs)) {
@ -960,7 +964,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionDeclaration::generate_bytecode(Byt
return {};
}
Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode(Bytecode::Generator& generator) const
Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<DeprecatedFlyString const&> lhs_name) const
{
bool has_name = !name().is_empty();
Optional<Bytecode::IdentifierTableIndex> name_identifier;
@ -972,7 +976,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode(Byte
generator.emit<Bytecode::Op::CreateVariable>(*name_identifier, Bytecode::Op::EnvironmentMode::Lexical, true);
}
generator.emit_new_function(*this);
generator.emit_new_function(*this, lhs_name);
if (has_name) {
generator.emit<Bytecode::Op::SetVariable>(*name_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
@ -982,6 +986,11 @@ Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode(Byte
return {};
}
Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode(Bytecode::Generator& generator) const
{
return generate_bytecode_with_lhs_name(generator, {});
}
static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Register const& value_reg)
{
Vector<Bytecode::Register> excluded_property_names;
@ -1041,7 +1050,11 @@ static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_byt
Bytecode::Label { if_not_undefined_block });
generator.switch_to_basic_block(if_undefined_block);
if (auto const* lhs = name.get_pointer<NonnullRefPtr<Identifier const>>()) {
TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, (*lhs)->string()));
} else {
TRY(initializer->generate_bytecode(generator));
}
generator.emit<Bytecode::Op::Jump>().set_targets(
Bytecode::Label { if_not_undefined_block },
{});
@ -1231,7 +1244,12 @@ static Bytecode::CodeGenerationErrorOr<void> generate_array_binding_pattern_byte
Bytecode::Label { value_is_not_undefined_block });
generator.switch_to_basic_block(value_is_undefined_block);
if (auto const* lhs = name.get_pointer<NonnullRefPtr<Identifier const>>()) {
TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, (*lhs)->string()));
} else {
TRY(initializer->generate_bytecode(generator));
}
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { value_is_not_undefined_block });
generator.switch_to_basic_block(value_is_not_undefined_block);
@ -1273,7 +1291,11 @@ Bytecode::CodeGenerationErrorOr<void> VariableDeclaration::generate_bytecode(Byt
{
for (auto& declarator : m_declarations) {
if (declarator->init()) {
if (auto const* lhs = declarator->target().get_pointer<NonnullRefPtr<Identifier const>>()) {
TRY(generator.emit_named_evaluation_if_anonymous_function(*declarator->init(), (*lhs)->string()));
} else {
TRY(declarator->init()->generate_bytecode(generator));
}
TRY(assign_accumulator_to_variable_declarator(generator, declarator, *this));
} else if (m_declaration_kind != DeclarationKind::Var) {
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
@ -2166,10 +2188,15 @@ Bytecode::CodeGenerationErrorOr<void> ClassDeclaration::generate_bytecode(Byteco
return {};
}
Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<DeprecatedFlyString const&> lhs_name) const
{
generator.emit<Bytecode::Op::NewClass>(*this, lhs_name);
return {};
}
Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode(Bytecode::Generator& generator) const
{
generator.emit<Bytecode::Op::NewClass>(*this);
return {};
return generate_bytecode_with_lhs_name(generator, {});
}
Bytecode::CodeGenerationErrorOr<void> SpreadExpression::generate_bytecode(Bytecode::Generator& generator) const
@ -2616,7 +2643,7 @@ Bytecode::CodeGenerationErrorOr<void> MetaProperty::generate_bytecode(Bytecode::
Bytecode::CodeGenerationErrorOr<void> ClassFieldInitializerStatement::generate_bytecode(Bytecode::Generator& generator) const
{
TRY(m_expression->generate_bytecode(generator));
TRY(generator.emit_named_evaluation_if_anonymous_function(*m_expression, m_class_field_identifier_name));
generator.perform_needed_unwinds<Bytecode::Op::Return>();
generator.emit<Bytecode::Op::Return>();
return {};

View file

@ -452,12 +452,34 @@ void Generator::pop_home_object()
m_home_objects.take_last();
}
void Generator::emit_new_function(FunctionNode const& function_node)
void Generator::emit_new_function(FunctionExpression const& function_node, Optional<DeprecatedFlyString const&> lhs_name)
{
if (m_home_objects.is_empty())
emit<Op::NewFunction>(function_node);
emit<Op::NewFunction>(function_node, lhs_name);
else
emit<Op::NewFunction>(function_node, m_home_objects.last());
emit<Op::NewFunction>(function_node, lhs_name, m_home_objects.last());
}
CodeGenerationErrorOr<void> Generator::emit_named_evaluation_if_anonymous_function(Expression const& expression, Optional<DeprecatedFlyString const&> lhs_name)
{
if (is<FunctionExpression>(expression)) {
auto const& function_expression = static_cast<FunctionExpression const&>(expression);
if (!function_expression.has_name()) {
TRY(function_expression.generate_bytecode_with_lhs_name(*this, move(lhs_name)));
return {};
}
}
if (is<ClassExpression>(expression)) {
auto const& class_expression = static_cast<ClassExpression const&>(expression);
if (!class_expression.has_name()) {
TRY(class_expression.generate_bytecode_with_lhs_name(*this, move(lhs_name)));
return {};
}
}
TRY(expression.generate_bytecode(*this));
return {};
}
}

View file

@ -85,7 +85,9 @@ public:
void push_home_object(Register);
void pop_home_object();
void emit_new_function(JS::FunctionNode const&);
void emit_new_function(JS::FunctionExpression const&, Optional<DeprecatedFlyString const&> lhs_name);
CodeGenerationErrorOr<void> emit_named_evaluation_if_anonymous_function(Expression const&, Optional<DeprecatedFlyString const&> lhs_name);
void begin_continuable_scope(Label continue_target, Vector<DeprecatedFlyString> const& language_label_set);
void end_continuable_scope();

View file

@ -768,7 +768,13 @@ ThrowCompletionOr<void> SuperCall::execute_impl(Bytecode::Interpreter& interpret
ThrowCompletionOr<void> NewFunction::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
if (!m_function_node.has_name()) {
interpreter.accumulator() = m_function_node.instantiate_ordinary_function_expression(vm, m_lhs_name.value_or({}));
} else {
interpreter.accumulator() = ECMAScriptFunctionObject::create(interpreter.realm(), m_function_node.name(), m_function_node.source_text(), m_function_node.body(), m_function_node.parameters(), m_function_node.function_length(), vm.lexical_environment(), vm.running_execution_context().private_environment, m_function_node.kind(), m_function_node.is_strict_mode(), m_function_node.might_need_arguments_object(), m_function_node.contains_direct_call_to_eval(), m_function_node.is_arrow_function());
}
if (m_home_object.has_value()) {
auto home_object_value = interpreter.reg(m_home_object.value());
static_cast<ECMAScriptFunctionObject&>(interpreter.accumulator().as_function()).set_home_object(&home_object_value.as_object());
@ -1105,7 +1111,13 @@ ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interprete
auto scope = interpreter.ast_interpreter_scope(interpreter.realm());
auto& ast_interpreter = scope.interpreter();
auto* class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, name, name.is_null() ? ""sv : name));
ECMAScriptFunctionObject* class_object = nullptr;
if (!m_class_expression.has_name() && m_lhs_name.has_value())
class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, {}, m_lhs_name.value()));
else
class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, name, name.is_null() ? ""sv : name));
class_object->set_source_text(m_class_expression.source_text());
interpreter.accumulator() = class_object;
@ -1355,15 +1367,25 @@ DeprecatedString SuperCall::to_deprecated_string_impl(Bytecode::Executable const
DeprecatedString NewFunction::to_deprecated_string_impl(Bytecode::Executable const&) const
{
StringBuilder builder;
builder.append("NewFunction"sv);
if (m_function_node.has_name())
builder.appendff(" name:{}"sv, m_function_node.name());
if (m_lhs_name.has_value())
builder.appendff(" lhs_name:{}"sv, m_lhs_name.value());
if (m_home_object.has_value())
return DeprecatedString::formatted("NewFunction home_object:{}", m_home_object.value());
return "NewFunction"sv;
builder.appendff(" home_object:{}"sv, m_home_object.value());
return builder.to_deprecated_string();
}
DeprecatedString NewClass::to_deprecated_string_impl(Bytecode::Executable const&) const
{
StringBuilder builder;
auto name = m_class_expression.name();
return DeprecatedString::formatted("NewClass '{}'", name.is_null() ? ""sv : name);
builder.appendff("NewClass '{}'"sv, name.is_null() ? ""sv : name);
if (m_lhs_name.has_value())
builder.appendff(" lhs_name:{}"sv, m_lhs_name.value());
return builder.to_deprecated_string();
}
DeprecatedString Return::to_deprecated_string_impl(Bytecode::Executable const&) const

View file

@ -21,6 +21,10 @@
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueTraits.h>
namespace JS {
class FunctionExpression;
}
namespace JS::Bytecode::Op {
class Load final : public Instruction {
@ -798,9 +802,10 @@ private:
class NewClass final : public Instruction {
public:
explicit NewClass(ClassExpression const& class_expression)
explicit NewClass(ClassExpression const& class_expression, Optional<DeprecatedFlyString const&> lhs_name)
: Instruction(Type::NewClass)
, m_class_expression(class_expression)
, m_lhs_name(lhs_name.has_value() ? *lhs_name : Optional<DeprecatedFlyString> {})
{
}
@ -811,13 +816,15 @@ public:
private:
ClassExpression const& m_class_expression;
Optional<DeprecatedFlyString> m_lhs_name;
};
class NewFunction final : public Instruction {
public:
explicit NewFunction(FunctionNode const& function_node, Optional<Register> home_object = {})
explicit NewFunction(FunctionExpression const& function_node, Optional<DeprecatedFlyString const&> lhs_name, Optional<Register> home_object = {})
: Instruction(Type::NewFunction)
, m_function_node(function_node)
, m_lhs_name(lhs_name.has_value() ? *lhs_name : Optional<DeprecatedFlyString> {})
, m_home_object(move(home_object))
{
}
@ -828,7 +835,8 @@ public:
void replace_references_impl(Register, Register);
private:
FunctionNode const& m_function_node;
FunctionExpression const& m_function_node;
Optional<DeprecatedFlyString> m_lhs_name;
Optional<Register> m_home_object;
};

View file

@ -286,7 +286,7 @@ ThrowCompletionOr<Value> VM::named_evaluation_if_anonymous_function(ASTNode cons
if (is<FunctionExpression>(expression)) {
auto& function = static_cast<FunctionExpression const&>(expression);
if (!function.has_name()) {
return function.instantiate_ordinary_function_expression(interpreter(), name);
return function.instantiate_ordinary_function_expression(*this, name);
}
} else if (is<ClassExpression>(expression)) {
auto& class_expression = static_cast<ClassExpression const&>(expression);