Browse Source

LibJS/Bytecode: Set "home object" of functions within object expression

We manage this by having a stack of home objects in Generator, and then
adding an optional home object parameter to the NewFunction instruction.
Andreas Kling 2 năm trước cách đây
mục cha
commit
c9bd324369

+ 5 - 1
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -876,6 +876,8 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
     auto object_reg = generator.allocate_register();
     generator.emit<Bytecode::Op::Store>(object_reg);
 
+    generator.push_home_object(object_reg);
+
     for (auto& property : m_properties) {
         Bytecode::Op::PropertyKind property_kind;
         switch (property->type()) {
@@ -917,6 +919,8 @@ Bytecode::CodeGenerationErrorOr<void> ObjectExpression::generate_bytecode(Byteco
     }
 
     generator.emit<Bytecode::Op::Load>(object_reg);
+
+    generator.pop_home_object();
     return {};
 }
 
@@ -996,7 +1000,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode(Byte
         generator.emit<Bytecode::Op::CreateVariable>(*name_identifier, Bytecode::Op::EnvironmentMode::Lexical, true);
     }
 
-    generator.emit<Bytecode::Op::NewFunction>(*this);
+    generator.emit_new_function(*this);
 
     if (has_name) {
         generator.emit<Bytecode::Op::SetVariable>(*name_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);

+ 18 - 0
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -448,4 +448,22 @@ void Generator::generate_continue(DeprecatedFlyString const& continue_label)
     VERIFY_NOT_REACHED();
 }
 
+void Generator::push_home_object(Register register_)
+{
+    m_home_objects.append(register_);
+}
+
+void Generator::pop_home_object()
+{
+    m_home_objects.take_last();
+}
+
+void Generator::emit_new_function(FunctionNode const& function_node)
+{
+    if (m_home_objects.is_empty())
+        emit<Op::NewFunction>(function_node);
+    else
+        emit<Op::NewFunction>(function_node, m_home_objects.last());
+}
+
 }

+ 5 - 0
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -83,6 +83,10 @@ public:
     CodeGenerationErrorOr<void> emit_store_to_reference(JS::ASTNode const&);
     CodeGenerationErrorOr<void> emit_delete_reference(JS::ASTNode const&);
 
+    void push_home_object(Register);
+    void pop_home_object();
+    void emit_new_function(JS::FunctionNode const&);
+
     void begin_continuable_scope(Label continue_target, Vector<DeprecatedFlyString> const& language_label_set);
     void end_continuable_scope();
     void begin_breakable_scope(Label breakable_target, Vector<DeprecatedFlyString> const& language_label_set);
@@ -237,6 +241,7 @@ private:
     Vector<LabelableScope> m_breakable_scopes;
     Vector<LexicalScope> m_variable_scopes;
     Vector<BlockBoundaryType> m_boundaries;
+    Vector<Register> m_home_objects;
 };
 
 }

+ 13 - 1
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -732,6 +732,10 @@ ThrowCompletionOr<void> NewFunction::execute_impl(Bytecode::Interpreter& interpr
 {
     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_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());
+    }
     return {};
 }
 
@@ -785,6 +789,12 @@ ThrowCompletionOr<void> EnterUnwindContext::execute_impl(Bytecode::Interpreter&
     return {};
 }
 
+void NewFunction::replace_references_impl(Register from, Register to)
+{
+    if (m_home_object == from)
+        m_home_object = to;
+}
+
 void EnterUnwindContext::replace_references_impl(BasicBlock const& from, BasicBlock const& to)
 {
     if (&m_entry_point.block() == &from)
@@ -1270,7 +1280,9 @@ DeprecatedString SuperCall::to_deprecated_string_impl(Bytecode::Executable const
 
 DeprecatedString NewFunction::to_deprecated_string_impl(Bytecode::Executable const&) const
 {
-    return "NewFunction";
+    if (m_home_object.has_value())
+        return DeprecatedString::formatted("NewFunction home_object:{}", m_home_object.value());
+    return "NewFunction"sv;
 }
 
 DeprecatedString NewClass::to_deprecated_string_impl(Bytecode::Executable const&) const

+ 4 - 2
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -777,19 +777,21 @@ private:
 
 class NewFunction final : public Instruction {
 public:
-    explicit NewFunction(FunctionNode const& function_node)
+    explicit NewFunction(FunctionNode const& function_node, Optional<Register> home_object = {})
         : Instruction(Type::NewFunction)
         , m_function_node(function_node)
+        , m_home_object(move(home_object))
     {
     }
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
     void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
-    void replace_references_impl(Register, Register) { }
+    void replace_references_impl(Register, Register);
 
 private:
     FunctionNode const& m_function_node;
+    Optional<Register> m_home_object;
 };
 
 class Return final : public Instruction {