mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 09:27:35 +00:00
LibJS: Switch to array-calls in the bytecode vm
This will make it easier to implement spreading arguments.
This commit is contained in:
parent
ab763a56f6
commit
4235b2020f
3 changed files with 67 additions and 65 deletions
|
@ -523,8 +523,6 @@ Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Ge
|
||||||
|
|
||||||
Bytecode::CodeGenerationErrorOr<void> SuperCall::generate_bytecode(Bytecode::Generator& generator) const
|
Bytecode::CodeGenerationErrorOr<void> SuperCall::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
{
|
{
|
||||||
Vector<Bytecode::Register> argument_registers;
|
|
||||||
|
|
||||||
if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) {
|
if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) {
|
||||||
// NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which
|
// NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which
|
||||||
// shouldn't call @@iterator of %Array.prototype%.
|
// shouldn't call @@iterator of %Array.prototype%.
|
||||||
|
@ -534,17 +532,24 @@ Bytecode::CodeGenerationErrorOr<void> SuperCall::generate_bytecode(Bytecode::Gen
|
||||||
// This generates a single argument, which will be implicitly passed in accumulator
|
// This generates a single argument, which will be implicitly passed in accumulator
|
||||||
MUST(argument.value->generate_bytecode(generator));
|
MUST(argument.value->generate_bytecode(generator));
|
||||||
} else {
|
} else {
|
||||||
|
Vector<Bytecode::Register> argument_registers;
|
||||||
argument_registers.ensure_capacity(m_arguments.size());
|
argument_registers.ensure_capacity(m_arguments.size());
|
||||||
|
|
||||||
for (auto const& arg : m_arguments) {
|
for (size_t i = 0; i < m_arguments.size(); ++i) {
|
||||||
TRY(arg.value->generate_bytecode(generator));
|
|
||||||
auto arg_reg = generator.allocate_register();
|
auto arg_reg = generator.allocate_register();
|
||||||
generator.emit<Bytecode::Op::Store>(arg_reg);
|
|
||||||
argument_registers.unchecked_append(arg_reg);
|
argument_registers.unchecked_append(arg_reg);
|
||||||
}
|
}
|
||||||
|
for (size_t i = 0; i < m_arguments.size(); ++i) {
|
||||||
|
TRY(m_arguments[i].value->generate_bytecode(generator));
|
||||||
|
generator.emit<Bytecode::Op::Store>(argument_registers[i]);
|
||||||
|
}
|
||||||
|
if (!argument_registers.is_empty())
|
||||||
|
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2, AK::Array { argument_registers.first(), argument_registers.last() });
|
||||||
|
else
|
||||||
|
generator.emit<Bytecode::Op::NewArray>();
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.emit_with_extra_register_slots<Bytecode::Op::SuperCall>(argument_registers.size(), m_is_synthetic == IsPartOfSyntheticConstructor::Yes, argument_registers);
|
generator.emit<Bytecode::Op::SuperCall>(m_is_synthetic == IsPartOfSyntheticConstructor::Yes);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1489,13 +1494,16 @@ Bytecode::CodeGenerationErrorOr<void> CallExpression::generate_bytecode(Bytecode
|
||||||
generator.emit<Bytecode::Op::Store>(callee_reg);
|
generator.emit<Bytecode::Op::Store>(callee_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: We only need to record the first and last register, due to packing everything in an array
|
||||||
Vector<Bytecode::Register> argument_registers;
|
Vector<Bytecode::Register> argument_registers;
|
||||||
for (auto& arg : m_arguments) {
|
for (size_t i = 0; i < m_arguments.size(); ++i) {
|
||||||
TRY(arg.value->generate_bytecode(generator));
|
|
||||||
auto arg_reg = generator.allocate_register();
|
auto arg_reg = generator.allocate_register();
|
||||||
generator.emit<Bytecode::Op::Store>(arg_reg);
|
|
||||||
argument_registers.append(arg_reg);
|
argument_registers.append(arg_reg);
|
||||||
}
|
}
|
||||||
|
for (size_t i = 0; i < m_arguments.size(); ++i) {
|
||||||
|
TRY(m_arguments[i].value->generate_bytecode(generator));
|
||||||
|
generator.emit<Bytecode::Op::Store>(argument_registers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
Bytecode::Op::Call::CallType call_type;
|
Bytecode::Op::Call::CallType call_type;
|
||||||
if (is<NewExpression>(*this)) {
|
if (is<NewExpression>(*this)) {
|
||||||
|
@ -1504,7 +1512,11 @@ Bytecode::CodeGenerationErrorOr<void> CallExpression::generate_bytecode(Bytecode
|
||||||
call_type = Bytecode::Op::Call::CallType::Call;
|
call_type = Bytecode::Op::Call::CallType::Call;
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.emit_with_extra_register_slots<Bytecode::Op::Call>(argument_registers.size(), call_type, callee_reg, this_reg, argument_registers);
|
if (!argument_registers.is_empty())
|
||||||
|
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2, AK::Array { argument_registers.first(), argument_registers.last() });
|
||||||
|
else
|
||||||
|
generator.emit<Bytecode::Op::NewArray>();
|
||||||
|
generator.emit<Bytecode::Op::Call>(call_type, callee_reg, this_reg);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1676,6 +1688,8 @@ Bytecode::CodeGenerationErrorOr<void> TaggedTemplateLiteral::generate_bytecode(B
|
||||||
auto tag_reg = generator.allocate_register();
|
auto tag_reg = generator.allocate_register();
|
||||||
generator.emit<Bytecode::Op::Store>(tag_reg);
|
generator.emit<Bytecode::Op::Store>(tag_reg);
|
||||||
|
|
||||||
|
// FIXME: We only need to record the first and last register,
|
||||||
|
// due to packing everything in an array, same goes for argument_regs
|
||||||
Vector<Bytecode::Register> string_regs;
|
Vector<Bytecode::Register> string_regs;
|
||||||
auto& expressions = m_template_literal->expressions();
|
auto& expressions = m_template_literal->expressions();
|
||||||
for (size_t i = 0; i < expressions.size(); ++i) {
|
for (size_t i = 0; i < expressions.size(); ++i) {
|
||||||
|
@ -1704,14 +1718,13 @@ Bytecode::CodeGenerationErrorOr<void> TaggedTemplateLiteral::generate_bytecode(B
|
||||||
|
|
||||||
Vector<Bytecode::Register> argument_regs;
|
Vector<Bytecode::Register> argument_regs;
|
||||||
argument_regs.append(strings_reg);
|
argument_regs.append(strings_reg);
|
||||||
for (size_t i = 0; i < expressions.size(); ++i) {
|
for (size_t i = 1; i < expressions.size(); i += 2)
|
||||||
if (i % 2 == 0)
|
argument_regs.append(generator.allocate_register());
|
||||||
continue;
|
|
||||||
|
|
||||||
|
for (size_t i = 1; i < expressions.size(); i += 2) {
|
||||||
|
auto string_reg = argument_regs[1 + i / 2];
|
||||||
TRY(expressions[i].generate_bytecode(generator));
|
TRY(expressions[i].generate_bytecode(generator));
|
||||||
auto string_reg = generator.allocate_register();
|
|
||||||
generator.emit<Bytecode::Op::Store>(string_reg);
|
generator.emit<Bytecode::Op::Store>(string_reg);
|
||||||
argument_regs.append(string_reg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Bytecode::Register> raw_string_regs;
|
Vector<Bytecode::Register> raw_string_regs;
|
||||||
|
@ -1741,7 +1754,12 @@ Bytecode::CodeGenerationErrorOr<void> TaggedTemplateLiteral::generate_bytecode(B
|
||||||
auto this_reg = generator.allocate_register();
|
auto this_reg = generator.allocate_register();
|
||||||
generator.emit<Bytecode::Op::Store>(this_reg);
|
generator.emit<Bytecode::Op::Store>(this_reg);
|
||||||
|
|
||||||
generator.emit_with_extra_register_slots<Bytecode::Op::Call>(argument_regs.size(), Bytecode::Op::Call::CallType::Call, tag_reg, this_reg, move(argument_regs));
|
if (!argument_regs.is_empty())
|
||||||
|
generator.emit_with_extra_register_slots<Bytecode::Op::NewArray>(2, AK::Array { argument_regs.first(), argument_regs.last() });
|
||||||
|
else
|
||||||
|
generator.emit<Bytecode::Op::NewArray>();
|
||||||
|
|
||||||
|
generator.emit<Bytecode::Op::Call>(Bytecode::Op::Call::CallType::Call, tag_reg, this_reg);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -496,6 +496,33 @@ ThrowCompletionOr<void> JumpUndefined::execute_impl(Bytecode::Interpreter& inter
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 13.3.8.1 https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
|
||||||
|
static MarkedVector<Value> argument_list_evaluation(Bytecode::Interpreter& interpreter)
|
||||||
|
{
|
||||||
|
// Note: Any spreading and actual evaluation is handled in preceding opcodes
|
||||||
|
// Note: The spec uses the concept of a list, while we create a temporary array
|
||||||
|
// in the preceding opcodes, so we have to convert in a manner that is not
|
||||||
|
// visible to the user
|
||||||
|
auto& vm = interpreter.vm();
|
||||||
|
|
||||||
|
MarkedVector<Value> argument_values { vm.heap() };
|
||||||
|
auto arguments = interpreter.accumulator();
|
||||||
|
|
||||||
|
auto& argument_array = arguments.as_array();
|
||||||
|
auto array_length = argument_array.indexed_properties().array_like_size();
|
||||||
|
|
||||||
|
argument_values.ensure_capacity(array_length);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < array_length; ++i) {
|
||||||
|
if (auto maybe_value = argument_array.indexed_properties().get(i); maybe_value.has_value())
|
||||||
|
argument_values.append(maybe_value.release_value().value);
|
||||||
|
else
|
||||||
|
argument_values.append(js_undefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
return argument_values;
|
||||||
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto& vm = interpreter.vm();
|
auto& vm = interpreter.vm();
|
||||||
|
@ -512,9 +539,7 @@ ThrowCompletionOr<void> Call::execute_impl(Bytecode::Interpreter& interpreter) c
|
||||||
|
|
||||||
auto this_value = interpreter.reg(m_this_value);
|
auto this_value = interpreter.reg(m_this_value);
|
||||||
|
|
||||||
MarkedVector<Value> argument_values { vm.heap() };
|
auto argument_values = argument_list_evaluation(interpreter);
|
||||||
for (size_t i = 0; i < m_argument_count; ++i)
|
|
||||||
argument_values.append(interpreter.reg(m_arguments[i]));
|
|
||||||
|
|
||||||
Value return_value;
|
Value return_value;
|
||||||
if (m_type == CallType::Call)
|
if (m_type == CallType::Call)
|
||||||
|
@ -549,8 +574,7 @@ ThrowCompletionOr<void> SuperCall::execute_impl(Bytecode::Interpreter& interpret
|
||||||
for (size_t i = 0; i < length; ++i)
|
for (size_t i = 0; i < length; ++i)
|
||||||
arg_list.append(array_value.get_without_side_effects(PropertyKey { i }));
|
arg_list.append(array_value.get_without_side_effects(PropertyKey { i }));
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = 0; i < m_argument_count; ++i)
|
arg_list = argument_list_evaluation(interpreter);
|
||||||
arg_list.append(interpreter.reg(m_arguments[i]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. If IsConstructor(func) is false, throw a TypeError exception.
|
// 5. If IsConstructor(func) is false, throw a TypeError exception.
|
||||||
|
@ -1045,28 +1069,12 @@ String JumpUndefined::to_string_impl(Bytecode::Executable const&) const
|
||||||
|
|
||||||
String Call::to_string_impl(Bytecode::Executable const&) const
|
String Call::to_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
return String::formatted("Call callee:{}, this:{}, arguments:[...acc]", m_callee, m_this_value);
|
||||||
builder.appendff("Call callee:{}, this:{}", m_callee, m_this_value);
|
|
||||||
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 SuperCall::to_string_impl(Bytecode::Executable const&) const
|
String SuperCall::to_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
return "SuperCall arguments:[...acc]"sv;
|
||||||
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
|
||||||
|
|
|
@ -585,59 +585,39 @@ public:
|
||||||
Construct,
|
Construct,
|
||||||
};
|
};
|
||||||
|
|
||||||
Call(CallType type, Register callee, Register this_value, Vector<Register> const& arguments)
|
Call(CallType type, Register callee, Register this_value)
|
||||||
: Instruction(Type::Call)
|
: Instruction(Type::Call)
|
||||||
, m_callee(callee)
|
, m_callee(callee)
|
||||||
, m_this_value(this_value)
|
, m_this_value(this_value)
|
||||||
, m_type(type)
|
, m_type(type)
|
||||||
, 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;
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
String to_string_impl(Bytecode::Executable const&) const;
|
String to_string_impl(Bytecode::Executable const&) const;
|
||||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
|
|
||||||
size_t length_impl() const
|
|
||||||
{
|
|
||||||
return sizeof(*this) + sizeof(Register) * m_argument_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Register m_callee;
|
Register m_callee;
|
||||||
Register m_this_value;
|
Register m_this_value;
|
||||||
CallType m_type;
|
CallType m_type;
|
||||||
size_t m_argument_count { 0 };
|
|
||||||
Register m_arguments[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: This instruction is variable-width depending on the number of arguments!
|
// NOTE: This instruction is variable-width depending on the number of arguments!
|
||||||
class SuperCall : public Instruction {
|
class SuperCall : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit SuperCall(bool is_synthetic, Vector<Register> const& arguments)
|
explicit SuperCall(bool is_synthetic)
|
||||||
: Instruction(Type::SuperCall)
|
: Instruction(Type::SuperCall)
|
||||||
, m_is_synthetic(is_synthetic)
|
, 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;
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
String to_string_impl(Bytecode::Executable const&) const;
|
String to_string_impl(Bytecode::Executable const&) const;
|
||||||
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
|
|
||||||
size_t length_impl() const
|
|
||||||
{
|
|
||||||
return sizeof(*this) + sizeof(Register) * m_argument_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_is_synthetic;
|
bool m_is_synthetic;
|
||||||
size_t m_argument_count { 0 };
|
|
||||||
Register m_arguments[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewClass final : public Instruction {
|
class NewClass final : public Instruction {
|
||||||
|
@ -991,10 +971,6 @@ ALWAYS_INLINE void Instruction::replace_references(BasicBlock const& from, Basic
|
||||||
|
|
||||||
ALWAYS_INLINE size_t Instruction::length() const
|
ALWAYS_INLINE size_t Instruction::length() const
|
||||||
{
|
{
|
||||||
if (type() == Type::Call)
|
|
||||||
return static_cast<Op::Call const&>(*this).length_impl();
|
|
||||||
if (type() == Type::SuperCall)
|
|
||||||
return static_cast<Op::SuperCall const&>(*this).length_impl();
|
|
||||||
if (type() == Type::NewArray)
|
if (type() == Type::NewArray)
|
||||||
return static_cast<Op::NewArray const&>(*this).length_impl();
|
return static_cast<Op::NewArray const&>(*this).length_impl();
|
||||||
if (type() == Type::CopyObjectExcludingProperties)
|
if (type() == Type::CopyObjectExcludingProperties)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue