mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 02:45:07 +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:
parent
a92d94f4e9
commit
85a3a1c085
8 changed files with 109 additions and 27 deletions
|
@ -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);
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue