mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:27:45 +00:00
LibJS: Stop using execute_ast_node in NewClass instruction
This change replaces usage of `execute_ast_node` to evaluate super expression in NewClass by generating instructions instead.
This commit is contained in:
parent
55faff80df
commit
1550e7c421
4 changed files with 81 additions and 45 deletions
|
@ -1913,55 +1913,28 @@ Completion ClassDeclaration::execute(Interpreter& interpreter) const
|
||||||
return Optional<Value> {};
|
return Optional<Value> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
|
ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_constructor(VM& vm, Environment* class_environment, Environment* environment, Value super_class, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const
|
||||||
ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_evaluation(VM& vm, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const
|
|
||||||
{
|
{
|
||||||
auto& realm = *vm.current_realm();
|
auto& realm = *vm.current_realm();
|
||||||
|
|
||||||
auto* environment = vm.lexical_environment();
|
|
||||||
VERIFY(environment);
|
|
||||||
auto class_environment = new_declarative_environment(*environment);
|
|
||||||
|
|
||||||
// We might not set the lexical environment but we always want to restore it eventually.
|
// We might not set the lexical environment but we always want to restore it eventually.
|
||||||
ArmedScopeGuard restore_environment = [&] {
|
ArmedScopeGuard restore_environment = [&] {
|
||||||
vm.running_execution_context().lexical_environment = environment;
|
vm.running_execution_context().lexical_environment = environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!binding_name.is_null())
|
|
||||||
MUST(class_environment->create_immutable_binding(vm, binding_name, true));
|
|
||||||
|
|
||||||
auto outer_private_environment = vm.running_execution_context().private_environment;
|
auto outer_private_environment = vm.running_execution_context().private_environment;
|
||||||
auto class_private_environment = new_private_environment(vm, outer_private_environment);
|
auto class_private_environment = new_private_environment(vm, outer_private_environment);
|
||||||
|
|
||||||
|
auto proto_parent = GCPtr { realm.intrinsics().object_prototype() };
|
||||||
|
auto constructor_parent = realm.intrinsics().function_prototype();
|
||||||
|
|
||||||
for (auto const& element : m_elements) {
|
for (auto const& element : m_elements) {
|
||||||
auto opt_private_name = element->private_bound_identifier();
|
auto opt_private_name = element->private_bound_identifier();
|
||||||
if (opt_private_name.has_value())
|
if (opt_private_name.has_value())
|
||||||
class_private_environment->add_private_name({}, opt_private_name.release_value());
|
class_private_environment->add_private_name({}, opt_private_name.release_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto proto_parent = GCPtr { realm.intrinsics().object_prototype() };
|
|
||||||
auto constructor_parent = realm.intrinsics().function_prototype();
|
|
||||||
|
|
||||||
if (!m_super_class.is_null()) {
|
if (!m_super_class.is_null()) {
|
||||||
vm.running_execution_context().lexical_environment = class_environment;
|
|
||||||
|
|
||||||
// Note: Since our execute does evaluation and GetValue in once we must check for a valid reference first
|
|
||||||
|
|
||||||
Value super_class;
|
|
||||||
|
|
||||||
if (vm.bytecode_interpreter_if_exists()) {
|
|
||||||
super_class = TRY(vm.execute_ast_node(*m_super_class));
|
|
||||||
} else {
|
|
||||||
auto reference = TRY(m_super_class->to_reference(vm.interpreter()));
|
|
||||||
if (reference.is_valid_reference()) {
|
|
||||||
super_class = TRY(reference.get_value(vm));
|
|
||||||
} else {
|
|
||||||
super_class = TRY(vm.execute_ast_node(*m_super_class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.running_execution_context().lexical_environment = environment;
|
|
||||||
|
|
||||||
if (super_class.is_null()) {
|
if (super_class.is_null()) {
|
||||||
proto_parent = nullptr;
|
proto_parent = nullptr;
|
||||||
} else if (!super_class.is_constructor()) {
|
} else if (!super_class.is_constructor()) {
|
||||||
|
@ -1990,12 +1963,23 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype%
|
// FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype%
|
||||||
auto class_constructor_value = TRY(vm.execute_ast_node(*m_constructor));
|
auto const& constructor = *m_constructor;
|
||||||
|
auto class_constructor = ECMAScriptFunctionObject::create(
|
||||||
|
realm,
|
||||||
|
constructor.name(),
|
||||||
|
constructor.source_text(),
|
||||||
|
constructor.body(),
|
||||||
|
constructor.parameters(),
|
||||||
|
constructor.function_length(),
|
||||||
|
vm.lexical_environment(),
|
||||||
|
vm.running_execution_context().private_environment,
|
||||||
|
constructor.kind(),
|
||||||
|
constructor.is_strict_mode(),
|
||||||
|
constructor.might_need_arguments_object(),
|
||||||
|
constructor.contains_direct_call_to_eval(),
|
||||||
|
constructor.is_arrow_function());
|
||||||
|
|
||||||
update_function_name(class_constructor_value, class_name);
|
class_constructor->set_name(class_name);
|
||||||
|
|
||||||
VERIFY(class_constructor_value.is_function() && is<ECMAScriptFunctionObject>(class_constructor_value.as_function()));
|
|
||||||
auto* class_constructor = static_cast<ECMAScriptFunctionObject*>(&class_constructor_value.as_function());
|
|
||||||
class_constructor->set_home_object(prototype);
|
class_constructor->set_home_object(prototype);
|
||||||
class_constructor->set_is_class_constructor();
|
class_constructor->set_is_class_constructor();
|
||||||
class_constructor->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
|
class_constructor->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
|
||||||
|
@ -2076,12 +2060,43 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
|
||||||
[&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
|
[&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
|
||||||
VERIFY(!static_block_function.is_null());
|
VERIFY(!static_block_function.is_null());
|
||||||
// We discard any value returned here.
|
// We discard any value returned here.
|
||||||
TRY(call(vm, *static_block_function.cell(), class_constructor_value));
|
TRY(call(vm, *static_block_function.cell(), class_constructor));
|
||||||
return {};
|
return {};
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return class_constructor;
|
class_constructor->set_source_text(source_text());
|
||||||
|
|
||||||
|
return { class_constructor };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
|
||||||
|
ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_evaluation(VM& vm, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const
|
||||||
|
{
|
||||||
|
auto* environment = vm.lexical_environment();
|
||||||
|
VERIFY(environment);
|
||||||
|
auto class_environment = new_declarative_environment(*environment);
|
||||||
|
|
||||||
|
Value super_class;
|
||||||
|
|
||||||
|
if (!binding_name.is_null())
|
||||||
|
MUST(class_environment->create_immutable_binding(vm, binding_name, true));
|
||||||
|
|
||||||
|
if (!m_super_class.is_null()) {
|
||||||
|
vm.running_execution_context().lexical_environment = class_environment;
|
||||||
|
|
||||||
|
// Note: Since our execute does evaluation and GetValue in once we must check for a valid reference first
|
||||||
|
auto reference = TRY(m_super_class->to_reference(vm.interpreter()));
|
||||||
|
if (reference.is_valid_reference()) {
|
||||||
|
super_class = TRY(reference.get_value(vm));
|
||||||
|
} else {
|
||||||
|
super_class = TRY(vm.execute_ast_node(*m_super_class));
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.running_execution_context().lexical_environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_class_constructor(vm, class_environment, environment, super_class, binding_name, class_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASTNode::dump(int indent) const
|
void ASTNode::dump(int indent) const
|
||||||
|
|
|
@ -1427,6 +1427,7 @@ public:
|
||||||
bool has_name() const { return !m_name.is_empty(); }
|
bool has_name() const { return !m_name.is_empty(); }
|
||||||
|
|
||||||
ThrowCompletionOr<ECMAScriptFunctionObject*> class_definition_evaluation(VM&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
|
ThrowCompletionOr<ECMAScriptFunctionObject*> class_definition_evaluation(VM&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
|
||||||
|
ThrowCompletionOr<ECMAScriptFunctionObject*> create_class_constructor(VM&, Environment* class_environment, Environment* environment, Value super_class, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool is_class_expression() const override { return true; }
|
virtual bool is_class_expression() const override { return true; }
|
||||||
|
|
|
@ -2273,9 +2273,23 @@ Bytecode::CodeGenerationErrorOr<void> ClassDeclaration::generate_bytecode(Byteco
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
|
||||||
Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<Bytecode::IdentifierTableIndex> lhs_name) const
|
Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<Bytecode::IdentifierTableIndex> lhs_name) const
|
||||||
{
|
{
|
||||||
|
// NOTE: Step 2 is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
|
||||||
|
generator.emit<Bytecode::Op::CreateLexicalEnvironment>();
|
||||||
|
|
||||||
|
if (has_name() || !lhs_name.has_value()) {
|
||||||
|
// NOTE: Step 3.a is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
|
||||||
|
auto interned_index = generator.intern_identifier(m_name);
|
||||||
|
generator.emit<Bytecode::Op::CreateVariable>(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_super_class)
|
||||||
|
TRY(m_super_class->generate_bytecode(generator));
|
||||||
|
|
||||||
generator.emit<Bytecode::Op::NewClass>(*this, lhs_name);
|
generator.emit<Bytecode::Op::NewClass>(*this, lhs_name);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1148,17 +1148,23 @@ ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interprete
|
||||||
{
|
{
|
||||||
auto& vm = interpreter.vm();
|
auto& vm = interpreter.vm();
|
||||||
auto name = m_class_expression.name();
|
auto name = m_class_expression.name();
|
||||||
|
auto super_class = interpreter.accumulator();
|
||||||
|
|
||||||
ECMAScriptFunctionObject* class_object = nullptr;
|
// NOTE: NewClass expects classEnv to be active lexical environment
|
||||||
|
auto class_environment = vm.lexical_environment();
|
||||||
|
vm.running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last();
|
||||||
|
|
||||||
if (!m_class_expression.has_name() && m_lhs_name.has_value())
|
DeprecatedFlyString binding_name;
|
||||||
class_object = TRY(m_class_expression.class_definition_evaluation(vm, {}, interpreter.current_executable().get_identifier(m_lhs_name.value())));
|
DeprecatedFlyString class_name;
|
||||||
else
|
if (!m_class_expression.has_name() && m_lhs_name.has_value()) {
|
||||||
class_object = TRY(m_class_expression.class_definition_evaluation(vm, name, name.is_null() ? ""sv : name));
|
class_name = interpreter.current_executable().get_identifier(m_lhs_name.value());
|
||||||
|
} else {
|
||||||
|
binding_name = name;
|
||||||
|
class_name = name.is_null() ? ""sv : name;
|
||||||
|
}
|
||||||
|
|
||||||
class_object->set_source_text(m_class_expression.source_text());
|
interpreter.accumulator() = TRY(m_class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, binding_name, class_name));
|
||||||
|
|
||||||
interpreter.accumulator() = class_object;
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue