summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorHendiadyoin1 <leon.a@serenityos.org>2022-08-30 18:03:02 +0200
committerLinus Groh <mail@linusgroh.de>2022-08-31 15:22:36 +0100
commit21ae882cfd9a6fb1a95c092916bea346445ea4aa (patch)
treee915b3af41f3bf3ff7355f5eebf6a268f75d673f /Userland
parent25be67299ea915da2f9978c95f5df135a0785f4b (diff)
downloadserenity-21ae882cfd9a6fb1a95c092916bea346445ea4aa.zip
LibJS: Implement SuperCall for the Bytecode-VM
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibJS/AST.h1
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp28
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Instruction.h1
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.cpp69
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.h33
5 files changed, 130 insertions, 2 deletions
diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h
index 87f7e9ae5f..13cf6f55d9 100644
--- a/Userland/Libraries/LibJS/AST.h
+++ b/Userland/Libraries/LibJS/AST.h
@@ -1516,6 +1516,7 @@ public:
virtual Completion execute(Interpreter&) const override;
virtual void dump(int indent) const override;
+ virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
private:
Vector<CallExpression::Argument> const m_arguments;
diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
index 5a4e8dd37f..7c3bec1fe7 100644
--- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
+++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
@@ -521,6 +521,34 @@ Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Ge
return {};
}
+Bytecode::CodeGenerationErrorOr<void> SuperCall::generate_bytecode(Bytecode::Generator& generator) const
+{
+ Vector<Bytecode::Register> argument_registers;
+
+ if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) {
+ // NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which
+ // shouldn't call @@iterator of %Array.prototype%.
+ VERIFY(m_arguments.size() == 1);
+ VERIFY(m_arguments[0].is_spread);
+ auto const& argument = m_arguments[0];
+ // This generates a single argument, which will be implicitly passed in accumulator
+ MUST(argument.value->generate_bytecode(generator));
+ } else {
+ argument_registers.ensure_capacity(m_arguments.size());
+
+ for (auto const& arg : m_arguments) {
+ TRY(arg.value->generate_bytecode(generator));
+ auto arg_reg = generator.allocate_register();
+ generator.emit<Bytecode::Op::Store>(arg_reg);
+ argument_registers.unchecked_append(arg_reg);
+ }
+ }
+
+ generator.emit_with_extra_register_slots<Bytecode::Op::SuperCall>(argument_registers.size(), m_is_synthetic == IsPartOfSyntheticConstructor::Yes, argument_registers);
+
+ return {};
+}
+
static Bytecode::CodeGenerationErrorOr<void> generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Register const& value_reg);
Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const
diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h
index d5accd5586..befd238fc1 100644
--- a/Userland/Libraries/LibJS/Bytecode/Instruction.h
+++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h
@@ -80,6 +80,7 @@
O(StrictlyEquals) \
O(StrictlyInequals) \
O(Sub) \
+ O(SuperCall) \
O(Throw) \
O(Typeof) \
O(TypeofVariable) \
diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp
index b931410d70..a9f41f994b 100644
--- a/Userland/Libraries/LibJS/Bytecode/Op.cpp
+++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp
@@ -15,6 +15,7 @@
#include <LibJS/Runtime/DeclarativeEnvironment.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/Environment.h>
+#include <LibJS/Runtime/FunctionEnvironment.h>
#include <LibJS/Runtime/GlobalEnvironment.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Iterator.h>
@@ -525,6 +526,60 @@ ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) c
return {};
}
+// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
+ThrowCompletionOr<void> SuperCall::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+ auto& vm = interpreter.vm();
+ // 1. Let newTarget be GetNewTarget().
+ auto new_target = vm.get_new_target();
+
+ // 2. Assert: Type(newTarget) is Object.
+ VERIFY(new_target.is_object());
+
+ // 3. Let func be GetSuperConstructor().
+ auto* func = get_super_constructor(vm);
+
+ // 4. Let argList be ? ArgumentListEvaluation of Arguments.
+ MarkedVector<Value> arg_list { vm.heap() };
+ if (m_is_synthetic) {
+ auto const& value = interpreter.accumulator();
+ VERIFY(value.is_object() && is<Array>(value.as_object()));
+ auto const& array_value = static_cast<Array const&>(value.as_object());
+ auto length = MUST(length_of_array_like(vm, array_value));
+ for (size_t i = 0; i < length; ++i)
+ arg_list.append(array_value.get_without_side_effects(PropertyKey { i }));
+ } else {
+ for (size_t i = 0; i < m_argument_count; ++i)
+ arg_list.append(interpreter.reg(m_arguments[i]));
+ }
+
+ // 5. If IsConstructor(func) is false, throw a TypeError exception.
+ if (!Value(func).is_constructor())
+ return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, "Super constructor");
+
+ // 6. Let result be ? Construct(func, argList, newTarget).
+ auto* result = TRY(construct(vm, static_cast<FunctionObject&>(*func), move(arg_list), &new_target.as_function()));
+
+ // 7. Let thisER be GetThisEnvironment().
+ auto& this_environment = verify_cast<FunctionEnvironment>(get_this_environment(vm));
+
+ // 8. Perform ? thisER.BindThisValue(result).
+ TRY(this_environment.bind_this_value(vm, result));
+
+ // 9. Let F be thisER.[[FunctionObject]].
+ auto& f = this_environment.function_object();
+
+ // 10. Assert: F is an ECMAScript function object.
+ // NOTE: This is implied by the strong C++ type.
+
+ // 11. Perform ? InitializeInstanceElements(result, F).
+ TRY(vm.initialize_instance_elements(*result, f));
+
+ // 12. Return result.
+ interpreter.accumulator() = result;
+ return {};
+}
+
ThrowCompletionOr<void> NewFunction::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
@@ -1000,6 +1055,20 @@ String Call::to_string_impl(Bytecode::Executable const&) const
return builder.to_string();
}
+String SuperCall::to_string_impl(Bytecode::Executable const&) const
+{
+ StringBuilder builder;
+ builder.append("SuperCall"sv);
+ if (m_is_synthetic) {
+ builder.append(" arguments:[...acc]"sv);
+ } else if (m_argument_count != 0) {
+ builder.append(" arguments:["sv);
+ builder.join(", "sv, Span<Register const>(m_arguments, m_argument_count));
+ builder.append(']');
+ }
+ return builder.to_string();
+}
+
String NewFunction::to_string_impl(Bytecode::Executable const&) const
{
return "NewFunction";
diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h
index 2217136147..3be84e0465 100644
--- a/Userland/Libraries/LibJS/Bytecode/Op.h
+++ b/Userland/Libraries/LibJS/Bytecode/Op.h
@@ -613,6 +613,33 @@ private:
Register m_arguments[];
};
+// NOTE: This instruction is variable-width depending on the number of arguments!
+class SuperCall : public Instruction {
+public:
+ explicit SuperCall(bool is_synthetic, Vector<Register> const& arguments)
+ : Instruction(Type::SuperCall)
+ , m_is_synthetic(is_synthetic)
+ , m_argument_count(arguments.size())
+ {
+ for (size_t i = 0; i < m_argument_count; ++i)
+ m_arguments[i] = arguments[i];
+ }
+
+ ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+ String to_string_impl(Bytecode::Executable const&) const;
+ void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
+
+ size_t length_impl() const
+ {
+ return sizeof(*this) + sizeof(Register) * m_argument_count;
+ }
+
+private:
+ bool m_is_synthetic;
+ size_t m_argument_count { 0 };
+ Register m_arguments[];
+};
+
class NewClass final : public Instruction {
public:
explicit NewClass(ClassExpression const& class_expression)
@@ -966,9 +993,11 @@ ALWAYS_INLINE size_t Instruction::length() const
{
if (type() == Type::Call)
return static_cast<Op::Call const&>(*this).length_impl();
- else if (type() == Type::NewArray)
+ if (type() == Type::SuperCall)
+ return static_cast<Op::SuperCall const&>(*this).length_impl();
+ if (type() == Type::NewArray)
return static_cast<Op::NewArray const&>(*this).length_impl();
- else if (type() == Type::CopyObjectExcludingProperties)
+ if (type() == Type::CopyObjectExcludingProperties)
return static_cast<Op::CopyObjectExcludingProperties const&>(*this).length_impl();
#define __BYTECODE_OP(op) \