LibJS: Stop using execute_ast_node() for class property evaluation

Instead, generate bytecode to execute their AST nodes and save the
resulting operands inside the NewClass instruction.

Moving property expression evaluation to happen before NewClass
execution also moves along creation of new private environment and
its population with private members (private members should be visible
during property evaluation).

Before:
- NewClass

After:
- CreatePrivateEnvironment
- AddPrivateName
- ...
- AddPrivateName
- NewClass
- LeavePrivateEnvironment
This commit is contained in:
Aliaksandr Kalenik 2024-05-11 22:54:41 +00:00 committed by Andreas Kling
commit 6fb1d9e516
Notes: sideshowbarker 2024-07-17 03:10:07 +09:00
9 changed files with 153 additions and 36 deletions

View file

@ -2757,8 +2757,35 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ClassExpression::genera
if (m_super_class)
super_class = TRY(m_super_class->generate_bytecode(generator)).value();
generator.emit<Op::CreatePrivateEnvironment>();
for (auto const& element : m_elements) {
auto opt_private_name = element->private_bound_identifier();
if (opt_private_name.has_value()) {
generator.emit<Op::AddPrivateName>(generator.intern_identifier(*opt_private_name));
}
}
Vector<Optional<ScopedOperand>> elements;
for (auto const& element : m_elements) {
Optional<ScopedOperand> key;
if (is<ClassMethod>(*element)) {
auto const& class_method = static_cast<ClassMethod const&>(*element);
if (!is<PrivateIdentifier>(class_method.key()))
key = TRY(class_method.key().generate_bytecode(generator));
} else if (is<ClassField>(*element)) {
auto const& class_field = static_cast<ClassField const&>(*element);
if (!is<PrivateIdentifier>(class_field.key()))
key = TRY(class_field.key().generate_bytecode(generator));
}
elements.append({ key });
}
auto dst = choose_dst(generator, preferred_dst);
generator.emit<Bytecode::Op::NewClass>(dst, super_class.has_value() ? super_class->operand() : Optional<Operand> {}, *this, lhs_name);
generator.emit_with_extra_slots<Op::NewClass, Optional<Operand>>(elements.size(), dst, super_class.has_value() ? super_class->operand() : Optional<Operand> {}, *this, lhs_name, elements);
generator.emit<Op::LeavePrivateEnvironment>();
return dst;
}

View file

@ -666,7 +666,7 @@ inline ThrowCompletionOr<void> create_variable(VM& vm, DeprecatedFlyString const
return verify_cast<GlobalEnvironment>(vm.variable_environment())->create_global_var_binding(name, false);
}
inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value super_class, ClassExpression const& class_expression, Optional<IdentifierTableIndex> const& lhs_name)
inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value super_class, ClassExpression const& class_expression, Optional<IdentifierTableIndex> const& lhs_name, ReadonlySpan<Value> element_keys)
{
auto& interpreter = vm.bytecode_interpreter();
auto name = class_expression.name();
@ -684,7 +684,7 @@ inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value supe
class_name = name.is_null() ? ""sv : name;
}
return TRY(class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, binding_name, class_name));
return TRY(class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, element_keys, binding_name, class_name));
}
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation

View file

@ -15,6 +15,7 @@
#define ENUMERATE_BYTECODE_OPS(O) \
O(Add) \
O(AddPrivateName) \
O(ArrayAppend) \
O(AsyncIteratorClose) \
O(Await) \
@ -31,6 +32,7 @@
O(CopyObjectExcludingProperties) \
O(CreateLexicalEnvironment) \
O(CreateVariableEnvironment) \
O(CreatePrivateEnvironment) \
O(CreateVariable) \
O(CreateRestParams) \
O(CreateArguments) \
@ -88,6 +90,7 @@
O(JumpUndefined) \
O(LeaveFinally) \
O(LeaveLexicalEnvironment) \
O(LeavePrivateEnvironment) \
O(LeaveUnwindContext) \
O(LeftShift) \
O(LessThan) \

View file

@ -547,6 +547,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
}
HANDLE_INSTRUCTION(Add);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(AddPrivateName);
HANDLE_INSTRUCTION(ArrayAppend);
HANDLE_INSTRUCTION(AsyncIteratorClose);
HANDLE_INSTRUCTION(BitwiseAnd);
@ -561,6 +562,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
HANDLE_INSTRUCTION(CopyObjectExcludingProperties);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateLexicalEnvironment);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateVariableEnvironment);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreatePrivateEnvironment);
HANDLE_INSTRUCTION(CreateVariable);
HANDLE_INSTRUCTION(CreateRestParams);
HANDLE_INSTRUCTION(CreateArguments);
@ -601,6 +603,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
HANDLE_INSTRUCTION(IteratorToArray);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeaveFinally);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeaveLexicalEnvironment);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeavePrivateEnvironment);
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeaveUnwindContext);
HANDLE_INSTRUCTION(LeftShift);
HANDLE_INSTRUCTION(LessThan);
@ -1140,6 +1143,12 @@ void NewPrimitiveArray::execute_impl(Bytecode::Interpreter& interpreter) const
interpreter.set(dst(), array);
}
void AddPrivateName::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto const& name = interpreter.current_executable().get_identifier(m_name);
interpreter.vm().running_execution_context().private_environment->add_private_name(name);
}
ThrowCompletionOr<void> ArrayAppend::execute_impl(Bytecode::Interpreter& interpreter) const
{
return append(interpreter.vm(), interpreter.get(dst()), interpreter.get(src()), m_is_spread);
@ -1266,6 +1275,13 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter)
running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
}
void CreatePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& running_execution_context = interpreter.vm().running_execution_context();
auto outer_private_environment = running_execution_context.private_environment;
running_execution_context.private_environment = new_private_environment(interpreter.vm(), outer_private_environment);
}
ThrowCompletionOr<void> CreateVariableEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& running_execution_context = interpreter.vm().running_execution_context();
@ -1754,6 +1770,12 @@ void LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) c
running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last();
}
void LeavePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& running_execution_context = interpreter.vm().running_execution_context();
running_execution_context.private_environment = running_execution_context.private_environment->outer_environment();
}
void LeaveUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const
{
interpreter.leave_unwind_context();
@ -1923,7 +1945,14 @@ ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interprete
Value super_class;
if (m_super_class.has_value())
super_class = interpreter.get(m_super_class.value());
interpreter.set(dst(), TRY(new_class(interpreter.vm(), super_class, m_class_expression, m_lhs_name)));
Vector<Value> element_keys;
for (size_t i = 0; i < m_element_keys_count; ++i) {
Value element_key;
if (m_element_keys[i].has_value())
element_key = interpreter.get(m_element_keys[i].value());
element_keys.append(element_key);
}
interpreter.set(dst(), TRY(new_class(interpreter.vm(), super_class, m_class_expression, m_lhs_name, element_keys)));
return {};
}
@ -1969,6 +1998,11 @@ ByteString NewPrimitiveArray::to_byte_string_impl(Bytecode::Executable const& ex
format_value_list("elements"sv, elements()));
}
ByteString AddPrivateName::to_byte_string_impl(Bytecode::Executable const& executable) const
{
return ByteString::formatted("AddPrivateName {}"sv, executable.identifier_table->get(m_name));
}
ByteString ArrayAppend::to_byte_string_impl(Bytecode::Executable const& executable) const
{
return ByteString::formatted("Append {}, {}{}",
@ -2052,6 +2086,11 @@ ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable co
return "CreateLexicalEnvironment"sv;
}
ByteString CreatePrivateEnvironment::to_byte_string_impl(Bytecode::Executable const&) const
{
return "CreatePrivateEnvironment"sv;
}
ByteString CreateVariableEnvironment::to_byte_string_impl(Bytecode::Executable const&) const
{
return "CreateVariableEnvironment"sv;
@ -2421,6 +2460,11 @@ ByteString LeaveLexicalEnvironment::to_byte_string_impl(Bytecode::Executable con
return "LeaveLexicalEnvironment"sv;
}
ByteString LeavePrivateEnvironment::to_byte_string_impl(Bytecode::Executable const&) const
{
return "LeavePrivateEnvironment"sv;
}
ByteString LeaveUnwindContext::to_byte_string_impl(Bytecode::Executable const&) const
{
return "LeaveUnwindContext";

View file

@ -350,6 +350,21 @@ private:
Value m_elements[];
};
class AddPrivateName final : public Instruction {
public:
explicit AddPrivateName(IdentifierTableIndex name)
: Instruction(Type::AddPrivateName)
, m_name(name)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
private:
IdentifierTableIndex m_name;
};
class ArrayAppend final : public Instruction {
public:
ArrayAppend(Operand dst, Operand src, bool is_spread)
@ -471,6 +486,17 @@ private:
u32 m_capacity { 0 };
};
class CreatePrivateEnvironment final : public Instruction {
public:
explicit CreatePrivateEnvironment()
: Instruction(Type::CreatePrivateEnvironment)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class EnterObjectEnvironment final : public Instruction {
public:
explicit EnterObjectEnvironment(Operand object)
@ -1471,13 +1497,25 @@ private:
class NewClass final : public Instruction {
public:
explicit NewClass(Operand dst, Optional<Operand> super_class, ClassExpression const& class_expression, Optional<IdentifierTableIndex> lhs_name)
static constexpr bool IsVariableLength = true;
explicit NewClass(Operand dst, Optional<Operand> super_class, ClassExpression const& class_expression, Optional<IdentifierTableIndex> lhs_name, ReadonlySpan<Optional<ScopedOperand>> elements_keys)
: Instruction(Type::NewClass)
, m_dst(dst)
, m_super_class(super_class)
, m_class_expression(class_expression)
, m_lhs_name(lhs_name)
, m_element_keys_count(elements_keys.size())
{
for (size_t i = 0; i < m_element_keys_count; i++) {
if (elements_keys[i].has_value())
m_element_keys[i] = elements_keys[i]->operand();
}
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Optional<Operand>) * m_element_keys_count);
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
@ -1493,6 +1531,8 @@ private:
Optional<Operand> m_super_class;
ClassExpression const& m_class_expression;
Optional<IdentifierTableIndex> m_lhs_name;
size_t m_element_keys_count { 0 };
Optional<Operand> m_element_keys[];
};
class NewFunction final : public Instruction {
@ -1760,6 +1800,17 @@ public:
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class LeavePrivateEnvironment final : public Instruction {
public:
LeavePrivateEnvironment()
: Instruction(Type::LeavePrivateEnvironment)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class LeaveUnwindContext final : public Instruction {
public:
LeaveUnwindContext()