mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:32:46 +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
				
			
		|  | @ -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,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(); | ||||
|     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 | ||||
|  |  | |||
|  | @ -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; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling