1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 21:57:43 +00:00

LibJS: Make class definition evaluation work in bytecode mode

Instead of assuming that there's an active AST interpreter, this code
now takes VM& everywhere and invokes the appropriate interpreter.

92 new passes on test262. :^)
This commit is contained in:
Andreas Kling 2023-06-25 17:33:17 +02:00
parent 66936a0d61
commit 8450948458
4 changed files with 38 additions and 39 deletions

View file

@ -1660,18 +1660,16 @@ Completion ClassElement::execute(Interpreter&) const
VERIFY_NOT_REACHED();
}
static ThrowCompletionOr<ClassElementName> class_key_to_property_name(Interpreter& interpreter, Expression const& key)
static ThrowCompletionOr<ClassElementName> class_key_to_property_name(VM& vm, Expression const& key)
{
auto& vm = interpreter.vm();
if (is<PrivateIdentifier>(key)) {
auto& private_identifier = static_cast<PrivateIdentifier const&>(key);
auto private_environment = interpreter.vm().running_execution_context().private_environment;
auto private_environment = vm.running_execution_context().private_environment;
VERIFY(private_environment);
return ClassElementName { private_environment->resolve_private_identifier(private_identifier.string()) };
}
auto prop_key = TRY(key.execute(interpreter)).release_value();
auto prop_key = TRY(vm.execute_ast_node(key));
if (prop_key.is_object())
prop_key = TRY(prop_key.to_primitive(vm, Value::PreferredType::String));
@ -1681,11 +1679,11 @@ static ThrowCompletionOr<ClassElementName> class_key_to_property_name(Interprete
}
// 15.4.5 Runtime Semantics: MethodDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
ThrowCompletionOr<ClassElement::ClassValue> ClassMethod::class_element_evaluation(Interpreter& interpreter, Object& target) const
ThrowCompletionOr<ClassElement::ClassValue> ClassMethod::class_element_evaluation(VM& vm, Object& target) const
{
auto property_key_or_private_name = TRY(class_key_to_property_name(interpreter, *m_key));
auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key));
auto method_value = TRY(m_function->execute(interpreter)).release_value();
auto method_value = TRY(vm.execute_ast_node(*m_function));
auto function_handle = make_handle(&method_value.as_function());
@ -1739,10 +1737,10 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassMethod::class_element_evaluatio
return ClassValue { PrivateElement { private_name, PrivateElement::Kind::Method, make_handle(method_value) } };
case Kind::Getter:
set_function_name("get");
return ClassValue { PrivateElement { private_name, PrivateElement::Kind::Accessor, make_handle(Value(Accessor::create(interpreter.vm(), &method_function, nullptr))) } };
return ClassValue { PrivateElement { private_name, PrivateElement::Kind::Accessor, make_handle(Value(Accessor::create(vm, &method_function, nullptr))) } };
case Kind::Setter:
set_function_name("set");
return ClassValue { PrivateElement { private_name, PrivateElement::Kind::Accessor, make_handle(Value(Accessor::create(interpreter.vm(), nullptr, &method_function))) } };
return ClassValue { PrivateElement { private_name, PrivateElement::Kind::Accessor, make_handle(Value(Accessor::create(vm, nullptr, &method_function))) } };
default:
VERIFY_NOT_REACHED();
}
@ -1775,12 +1773,11 @@ void ClassFieldInitializerStatement::dump(int) const
}
// 15.7.10 Runtime Semantics: ClassFieldDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classfielddefinitionevaluation
ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation(Interpreter& interpreter, Object& target) const
ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation(VM& vm, Object& target) const
{
auto& vm = interpreter.vm();
auto& realm = *vm.current_realm();
auto property_key_or_private_name = TRY(class_key_to_property_name(interpreter, *m_key));
auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key));
Handle<ECMAScriptFunctionObject> initializer {};
if (m_initializer) {
auto copy_initializer = m_initializer;
@ -1794,7 +1791,7 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation
// FIXME: A potential optimization is not creating the functions here since these are never directly accessible.
auto function_code = create_ast_node<ClassFieldInitializerStatement>(m_initializer->source_range(), copy_initializer.release_nonnull(), name);
initializer = make_handle(*ECMAScriptFunctionObject::create(realm, DeprecatedString::empty(), DeprecatedString::empty(), *function_code, {}, 0, interpreter.lexical_environment(), interpreter.vm().running_execution_context().private_environment, FunctionKind::Normal, true, false, m_contains_direct_call_to_eval, false, property_key_or_private_name));
initializer = make_handle(*ECMAScriptFunctionObject::create(realm, DeprecatedString::empty(), DeprecatedString::empty(), *function_code, {}, 0, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, false, m_contains_direct_call_to_eval, false, property_key_or_private_name));
initializer->make_method(target);
}
@ -1824,16 +1821,15 @@ Optional<DeprecatedFlyString> ClassMethod::private_bound_identifier() const
}
// 15.7.11 Runtime Semantics: ClassStaticBlockDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classstaticblockdefinitionevaluation
ThrowCompletionOr<ClassElement::ClassValue> StaticInitializer::class_element_evaluation(Interpreter& interpreter, Object& home_object) const
ThrowCompletionOr<ClassElement::ClassValue> StaticInitializer::class_element_evaluation(VM& vm, Object& home_object) const
{
auto& vm = interpreter.vm();
auto& realm = *vm.current_realm();
// 1. Let lex be the running execution context's LexicalEnvironment.
auto lexical_environment = interpreter.vm().running_execution_context().lexical_environment;
auto lexical_environment = vm.running_execution_context().lexical_environment;
// 2. Let privateEnv be the running execution context's PrivateEnvironment.
auto private_environment = interpreter.vm().running_execution_context().private_environment;
auto private_environment = vm.running_execution_context().private_environment;
// 3. Let sourceText be the empty sequence of Unicode code points.
// 4. Let formalParameters be an instance of the production FormalParameters : [empty] .
@ -1856,7 +1852,7 @@ Completion ClassExpression::execute(Interpreter& interpreter) const
// 1. Let className be StringValue of BindingIdentifier.
// 2. Let value be ? ClassDefinitionEvaluation of ClassTail with arguments className and className.
auto* value = TRY(class_definition_evaluation(interpreter, m_name, m_name.is_null() ? "" : m_name));
auto* value = TRY(class_definition_evaluation(interpreter.vm(), m_name, m_name.is_null() ? "" : m_name));
// 3. Set value.[[SourceText]] to the source text matched by ClassExpression.
value->set_source_text(m_source_text);
@ -1873,7 +1869,7 @@ static ThrowCompletionOr<Value> binding_class_declaration_evaluation(Interpreter
// ClassDeclaration : class ClassTail
if (!class_expression.has_name()) {
// 1. Let value be ? ClassDefinitionEvaluation of ClassTail with arguments undefined and "default".
auto value = TRY(class_expression.class_definition_evaluation(interpreter, {}, "default"));
auto value = TRY(class_expression.class_definition_evaluation(vm, {}, "default"));
// 2. Set value.[[SourceText]] to the source text matched by ClassDeclaration.
value->set_source_text(class_expression.source_text());
@ -1889,7 +1885,7 @@ static ThrowCompletionOr<Value> binding_class_declaration_evaluation(Interpreter
VERIFY(!class_name.is_empty());
// 2. Let value be ? ClassDefinitionEvaluation of ClassTail with arguments className and className.
auto value = TRY(class_expression.class_definition_evaluation(interpreter, class_name, class_name));
auto value = TRY(class_expression.class_definition_evaluation(vm, class_name, class_name));
// 3. Set value.[[SourceText]] to the source text matched by ClassDeclaration.
value->set_source_text(class_expression.source_text());
@ -1918,9 +1914,8 @@ Completion ClassDeclaration::execute(Interpreter& interpreter) const
}
// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_evaluation(Interpreter& interpreter, 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& vm = interpreter.vm();
auto& realm = *vm.current_realm();
auto* environment = vm.lexical_environment();
@ -1954,12 +1949,17 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
Value super_class;
auto reference = TRY(m_super_class->to_reference(interpreter));
if (reference.is_valid_reference()) {
super_class = TRY(reference.get_value(vm));
if (vm.bytecode_interpreter_if_exists()) {
super_class = TRY(vm.execute_ast_node(*m_super_class));
} else {
super_class = TRY(m_super_class->execute(interpreter)).release_value();
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()) {
@ -1990,7 +1990,7 @@ 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%
auto class_constructor_value = TRY(m_constructor->execute(interpreter)).release_value();
auto class_constructor_value = TRY(vm.execute_ast_node(*m_constructor));
update_function_name(class_constructor_value, class_name);
@ -2015,7 +2015,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
for (auto const& element : m_elements) {
// Note: All ClassElementEvaluation start with evaluating the name (or we fake it).
auto element_value = TRY(element->class_element_evaluation(interpreter, element->is_static() ? *class_constructor : *prototype));
auto element_value = TRY(element->class_element_evaluation(vm, element->is_static() ? *class_constructor : *prototype));
if (element_value.has<PrivateElement>()) {
auto& container = element->is_static() ? static_private_methods : instance_private_methods;

View file

@ -1301,7 +1301,7 @@ public:
// We use the Completion also as a ClassStaticBlockDefinition Record.
using ClassValue = Variant<ClassFieldDefinition, Completion, PrivateElement>;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(Interpreter&, Object& home_object) const = 0;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(VM&, Object& home_object) const = 0;
virtual Optional<DeprecatedFlyString> private_bound_identifier() const { return {}; };
@ -1330,7 +1330,7 @@ public:
virtual ElementKind class_element_kind() const override { return ElementKind::Method; }
virtual void dump(int indent) const override;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(Interpreter&, Object& home_object) const override;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(VM&, Object& home_object) const override;
virtual Optional<DeprecatedFlyString> private_bound_identifier() const override;
private:
@ -1357,7 +1357,7 @@ public:
virtual ElementKind class_element_kind() const override { return ElementKind::Field; }
virtual void dump(int indent) const override;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(Interpreter&, Object& home_object) const override;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(VM&, Object& home_object) const override;
virtual Optional<DeprecatedFlyString> private_bound_identifier() const override;
private:
@ -1376,7 +1376,7 @@ public:
}
virtual ElementKind class_element_kind() const override { return ElementKind::StaticInitializer; }
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(Interpreter&, Object& home_object) const override;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(VM&, Object& home_object) const override;
virtual void dump(int indent) const override;
@ -1421,7 +1421,7 @@ public:
bool has_name() const { return !m_name.is_empty(); }
ThrowCompletionOr<ECMAScriptFunctionObject*> class_definition_evaluation(Interpreter&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
ThrowCompletionOr<ECMAScriptFunctionObject*> class_definition_evaluation(VM&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
private:
virtual bool is_class_expression() const override { return true; }

View file

@ -1137,16 +1137,15 @@ ThrowCompletionOr<void> IteratorResultValue::execute_impl(Bytecode::Interpreter&
ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto name = m_class_expression.name();
auto scope = interpreter.ast_interpreter_scope(interpreter.realm());
auto& ast_interpreter = scope.interpreter();
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()));
class_object = TRY(m_class_expression.class_definition_evaluation(vm, {}, m_lhs_name.value()));
else
class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, name, name.is_null() ? ""sv : name));
class_object = TRY(m_class_expression.class_definition_evaluation(vm, name, name.is_null() ? ""sv : name));
class_object->set_source_text(m_class_expression.source_text());

View file

@ -291,7 +291,7 @@ ThrowCompletionOr<Value> VM::named_evaluation_if_anonymous_function(ASTNode cons
} else if (is<ClassExpression>(expression)) {
auto& class_expression = static_cast<ClassExpression const&>(expression);
if (!class_expression.has_name()) {
return TRY(class_expression.class_definition_evaluation(interpreter(), {}, name));
return TRY(class_expression.class_definition_evaluation(*this, {}, name));
}
}