Explorar o código

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. :^)
Andreas Kling %!s(int64=2) %!d(string=hai) anos
pai
achega
85a3a1c085

+ 3 - 4
Userland/Libraries/LibJS/AST.cpp

@@ -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);

+ 3 - 1
Userland/Libraries/LibJS/AST.h

@@ -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(); }
 

+ 36 - 9
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -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).
-                TRY(m_rhs->generate_bytecode(generator));
+                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);
-            TRY(initializer->generate_bytecode(generator));
+            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);
-            TRY(initializer->generate_bytecode(generator));
+
+            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()) {
-            TRY(declarator->init()->generate_bytecode(generator));
+            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,12 +2188,17 @@ Bytecode::CodeGenerationErrorOr<void> ClassDeclaration::generate_bytecode(Byteco
     return {};
 }
 
-Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode(Bytecode::Generator& generator) const
+Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<DeprecatedFlyString const&> lhs_name) const
 {
-    generator.emit<Bytecode::Op::NewClass>(*this);
+    generator.emit<Bytecode::Op::NewClass>(*this, lhs_name);
     return {};
 }
 
+Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode(Bytecode::Generator& generator) const
+{
+    return generate_bytecode_with_lhs_name(generator, {});
+}
+
 Bytecode::CodeGenerationErrorOr<void> SpreadExpression::generate_bytecode(Bytecode::Generator& generator) const
 {
     // NOTE: All users of this should handle the behaviour of this on their own,
@@ -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 {};

+ 25 - 3
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -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 {};
 }
 
 }

+ 3 - 1
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -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();

+ 27 - 5
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -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();
-    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_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

+ 11 - 3
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -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;
 };
 

+ 1 - 1
Userland/Libraries/LibJS/Runtime/VM.cpp

@@ -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);