Skip to content

Commit 21ae882

Browse files
Hendiadyoin1linusg
authored andcommitted
LibJS: Implement SuperCall for the Bytecode-VM
1 parent 25be672 commit 21ae882

File tree

5 files changed

+130
-2
lines changed

5 files changed

+130
-2
lines changed

Userland/Libraries/LibJS/AST.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ class SuperCall final : public Expression {
15161516

15171517
virtual Completion execute(Interpreter&) const override;
15181518
virtual void dump(int indent) const override;
1519+
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
15191520

15201521
private:
15211522
Vector<CallExpression::Argument> const m_arguments;

Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,34 @@ Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Ge
521521
return {};
522522
}
523523

524+
Bytecode::CodeGenerationErrorOr<void> SuperCall::generate_bytecode(Bytecode::Generator& generator) const
525+
{
526+
Vector<Bytecode::Register> argument_registers;
527+
528+
if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) {
529+
// NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which
530+
// shouldn't call @@iterator of %Array.prototype%.
531+
VERIFY(m_arguments.size() == 1);
532+
VERIFY(m_arguments[0].is_spread);
533+
auto const& argument = m_arguments[0];
534+
// This generates a single argument, which will be implicitly passed in accumulator
535+
MUST(argument.value->generate_bytecode(generator));
536+
} else {
537+
argument_registers.ensure_capacity(m_arguments.size());
538+
539+
for (auto const& arg : m_arguments) {
540+
TRY(arg.value->generate_bytecode(generator));
541+
auto arg_reg = generator.allocate_register();
542+
generator.emit<Bytecode::Op::Store>(arg_reg);
543+
argument_registers.unchecked_append(arg_reg);
544+
}
545+
}
546+
547+
generator.emit_with_extra_register_slots<Bytecode::Op::SuperCall>(argument_registers.size(), m_is_synthetic == IsPartOfSyntheticConstructor::Yes, argument_registers);
548+
549+
return {};
550+
}
551+
524552
static Bytecode::CodeGenerationErrorOr<void> generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Register const& value_reg);
525553

526554
Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const

Userland/Libraries/LibJS/Bytecode/Instruction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
O(StrictlyEquals) \
8181
O(StrictlyInequals) \
8282
O(Sub) \
83+
O(SuperCall) \
8384
O(Throw) \
8485
O(Typeof) \
8586
O(TypeofVariable) \

Userland/Libraries/LibJS/Bytecode/Op.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <LibJS/Runtime/DeclarativeEnvironment.h>
1616
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
1717
#include <LibJS/Runtime/Environment.h>
18+
#include <LibJS/Runtime/FunctionEnvironment.h>
1819
#include <LibJS/Runtime/GlobalEnvironment.h>
1920
#include <LibJS/Runtime/GlobalObject.h>
2021
#include <LibJS/Runtime/Iterator.h>
@@ -525,6 +526,60 @@ ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) c
525526
return {};
526527
}
527528

529+
// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
530+
ThrowCompletionOr<void> SuperCall::execute_impl(Bytecode::Interpreter& interpreter) const
531+
{
532+
auto& vm = interpreter.vm();
533+
// 1. Let newTarget be GetNewTarget().
534+
auto new_target = vm.get_new_target();
535+
536+
// 2. Assert: Type(newTarget) is Object.
537+
VERIFY(new_target.is_object());
538+
539+
// 3. Let func be GetSuperConstructor().
540+
auto* func = get_super_constructor(vm);
541+
542+
// 4. Let argList be ? ArgumentListEvaluation of Arguments.
543+
MarkedVector<Value> arg_list { vm.heap() };
544+
if (m_is_synthetic) {
545+
auto const& value = interpreter.accumulator();
546+
VERIFY(value.is_object() && is<Array>(value.as_object()));
547+
auto const& array_value = static_cast<Array const&>(value.as_object());
548+
auto length = MUST(length_of_array_like(vm, array_value));
549+
for (size_t i = 0; i < length; ++i)
550+
arg_list.append(array_value.get_without_side_effects(PropertyKey { i }));
551+
} else {
552+
for (size_t i = 0; i < m_argument_count; ++i)
553+
arg_list.append(interpreter.reg(m_arguments[i]));
554+
}
555+
556+
// 5. If IsConstructor(func) is false, throw a TypeError exception.
557+
if (!Value(func).is_constructor())
558+
return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, "Super constructor");
559+
560+
// 6. Let result be ? Construct(func, argList, newTarget).
561+
auto* result = TRY(construct(vm, static_cast<FunctionObject&>(*func), move(arg_list), &new_target.as_function()));
562+
563+
// 7. Let thisER be GetThisEnvironment().
564+
auto& this_environment = verify_cast<FunctionEnvironment>(get_this_environment(vm));
565+
566+
// 8. Perform ? thisER.BindThisValue(result).
567+
TRY(this_environment.bind_this_value(vm, result));
568+
569+
// 9. Let F be thisER.[[FunctionObject]].
570+
auto& f = this_environment.function_object();
571+
572+
// 10. Assert: F is an ECMAScript function object.
573+
// NOTE: This is implied by the strong C++ type.
574+
575+
// 11. Perform ? InitializeInstanceElements(result, F).
576+
TRY(vm.initialize_instance_elements(*result, f));
577+
578+
// 12. Return result.
579+
interpreter.accumulator() = result;
580+
return {};
581+
}
582+
528583
ThrowCompletionOr<void> NewFunction::execute_impl(Bytecode::Interpreter& interpreter) const
529584
{
530585
auto& vm = interpreter.vm();
@@ -1000,6 +1055,20 @@ String Call::to_string_impl(Bytecode::Executable const&) const
10001055
return builder.to_string();
10011056
}
10021057

1058+
String SuperCall::to_string_impl(Bytecode::Executable const&) const
1059+
{
1060+
StringBuilder builder;
1061+
builder.append("SuperCall"sv);
1062+
if (m_is_synthetic) {
1063+
builder.append(" arguments:[...acc]"sv);
1064+
} else if (m_argument_count != 0) {
1065+
builder.append(" arguments:["sv);
1066+
builder.join(", "sv, Span<Register const>(m_arguments, m_argument_count));
1067+
builder.append(']');
1068+
}
1069+
return builder.to_string();
1070+
}
1071+
10031072
String NewFunction::to_string_impl(Bytecode::Executable const&) const
10041073
{
10051074
return "NewFunction";

Userland/Libraries/LibJS/Bytecode/Op.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,33 @@ class Call final : public Instruction {
613613
Register m_arguments[];
614614
};
615615

616+
// NOTE: This instruction is variable-width depending on the number of arguments!
617+
class SuperCall : public Instruction {
618+
public:
619+
explicit SuperCall(bool is_synthetic, Vector<Register> const& arguments)
620+
: Instruction(Type::SuperCall)
621+
, m_is_synthetic(is_synthetic)
622+
, m_argument_count(arguments.size())
623+
{
624+
for (size_t i = 0; i < m_argument_count; ++i)
625+
m_arguments[i] = arguments[i];
626+
}
627+
628+
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
629+
String to_string_impl(Bytecode::Executable const&) const;
630+
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
631+
632+
size_t length_impl() const
633+
{
634+
return sizeof(*this) + sizeof(Register) * m_argument_count;
635+
}
636+
637+
private:
638+
bool m_is_synthetic;
639+
size_t m_argument_count { 0 };
640+
Register m_arguments[];
641+
};
642+
616643
class NewClass final : public Instruction {
617644
public:
618645
explicit NewClass(ClassExpression const& class_expression)
@@ -966,9 +993,11 @@ ALWAYS_INLINE size_t Instruction::length() const
966993
{
967994
if (type() == Type::Call)
968995
return static_cast<Op::Call const&>(*this).length_impl();
969-
else if (type() == Type::NewArray)
996+
if (type() == Type::SuperCall)
997+
return static_cast<Op::SuperCall const&>(*this).length_impl();
998+
if (type() == Type::NewArray)
970999
return static_cast<Op::NewArray const&>(*this).length_impl();
971-
else if (type() == Type::CopyObjectExcludingProperties)
1000+
if (type() == Type::CopyObjectExcludingProperties)
9721001
return static_cast<Op::CopyObjectExcludingProperties const&>(*this).length_impl();
9731002

9741003
#define __BYTECODE_OP(op) \

0 commit comments

Comments
 (0)