Skip to content

Commit

Permalink
LibJS: Make class definition evaluation work in bytecode mode
Browse files Browse the repository at this point in the history
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. :^)
  • Loading branch information
awesomekling committed Jun 25, 2023
1 parent 66936a0 commit b47bdad
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 39 deletions.
58 changes: 29 additions & 29 deletions Userland/Libraries/LibJS/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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());

Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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] .
Expand All @@ -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);
Expand All @@ -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());
Expand All @@ -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());
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down
10 changes: 5 additions & 5 deletions Userland/Libraries/LibJS/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -1301,7 +1301,7 @@ class ClassElement : public ASTNode {

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

Expand Down Expand Up @@ -1330,7 +1330,7 @@ class ClassMethod final : public ClassElement {
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:
Expand All @@ -1357,7 +1357,7 @@ class ClassField final : public ClassElement {
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:
Expand All @@ -1376,7 +1376,7 @@ class StaticInitializer final : public ClassElement {
}

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;

Expand Down Expand Up @@ -1421,7 +1421,7 @@ class ClassExpression final : public Expression {

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; }
Expand Down
7 changes: 3 additions & 4 deletions Userland/Libraries/LibJS/Bytecode/Op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
2 changes: 1 addition & 1 deletion Userland/Libraries/LibJS/Runtime/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand Down

0 comments on commit b47bdad

Please sign in to comment.