mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 16:55:08 +00:00
LibJS: Implement SuperCall for the Bytecode-VM
This commit is contained in:
parent
25be67299e
commit
21ae882cfd
5 changed files with 130 additions and 2 deletions
|
@ -1516,6 +1516,7 @@ public:
|
||||||
|
|
||||||
virtual Completion execute(Interpreter&) const override;
|
virtual Completion execute(Interpreter&) const override;
|
||||||
virtual void dump(int indent) const override;
|
virtual void dump(int indent) const override;
|
||||||
|
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<CallExpression::Argument> const m_arguments;
|
Vector<CallExpression::Argument> const m_arguments;
|
||||||
|
|
|
@ -521,6 +521,34 @@ Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Ge
|
||||||
return {};
|
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);
|
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
|
Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
O(StrictlyEquals) \
|
O(StrictlyEquals) \
|
||||||
O(StrictlyInequals) \
|
O(StrictlyInequals) \
|
||||||
O(Sub) \
|
O(Sub) \
|
||||||
|
O(SuperCall) \
|
||||||
O(Throw) \
|
O(Throw) \
|
||||||
O(Typeof) \
|
O(Typeof) \
|
||||||
O(TypeofVariable) \
|
O(TypeofVariable) \
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
#include <LibJS/Runtime/Environment.h>
|
#include <LibJS/Runtime/Environment.h>
|
||||||
|
#include <LibJS/Runtime/FunctionEnvironment.h>
|
||||||
#include <LibJS/Runtime/GlobalEnvironment.h>
|
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/Iterator.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
|
@ -525,6 +526,60 @@ ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) c
|
||||||
return {};
|
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
|
ThrowCompletionOr<void> NewFunction::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto& vm = interpreter.vm();
|
auto& vm = interpreter.vm();
|
||||||
|
@ -1000,6 +1055,20 @@ String Call::to_string_impl(Bytecode::Executable const&) const
|
||||||
return builder.to_string();
|
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
|
String NewFunction::to_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return "NewFunction";
|
return "NewFunction";
|
||||||
|
|
|
@ -613,6 +613,33 @@ private:
|
||||||
Register m_arguments[];
|
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 {
|
class NewClass final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit NewClass(ClassExpression const& class_expression)
|
explicit NewClass(ClassExpression const& class_expression)
|
||||||
|
@ -966,9 +993,11 @@ ALWAYS_INLINE size_t Instruction::length() const
|
||||||
{
|
{
|
||||||
if (type() == Type::Call)
|
if (type() == Type::Call)
|
||||||
return static_cast<Op::Call const&>(*this).length_impl();
|
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();
|
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();
|
return static_cast<Op::CopyObjectExcludingProperties const&>(*this).length_impl();
|
||||||
|
|
||||||
#define __BYTECODE_OP(op) \
|
#define __BYTECODE_OP(op) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue