refactor: 拆分 counter expression 计数分发
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_assignment_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::AssignmentExpression(assign) = expr else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
self.count_static_member_logical_assignment(member, logical_op, &assign.right, ctx);
|
||||
return;
|
||||
}
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_ic_set_with_ext(); // IC_SET_PROP or COMPOUND_MEMBER_* + 3 ext words
|
||||
} else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
self.count_computed_member_logical_assignment(member, logical_op, &assign.right, ctx);
|
||||
return;
|
||||
}
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // SET_PROP_DYNAMIC
|
||||
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.count_load_const(); // private id
|
||||
ctx.count_instr(); // SET_PRIVATE
|
||||
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(_) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
self.count_identifier_logical_assignment(logical_op, &assign.right, ctx);
|
||||
} else {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
ctx.projected_pc += 1; // COMPOUND_* on var
|
||||
} else {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
}
|
||||
}
|
||||
} else if let oxide_parser::AssignmentTarget::ArrayAssignmentTarget(ap) = &assign.left {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
self.count_array_assignment(ap, ctx);
|
||||
} else if let oxide_parser::AssignmentTarget::ObjectAssignmentTarget(op) = &assign.left {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
self.count_object_assignment(op, ctx);
|
||||
} else {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn count_static_member_logical_assignment(
|
||||
&self, member: &oxide_parser::StaticMemberExpression<'_>, logical_op: LogicalOperator, right: &Expression,
|
||||
ctx: &mut CompileCtx,
|
||||
) {
|
||||
let id = ctx.next_label_id();
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_load_var(); // current value copy
|
||||
ctx.count_ic_instr_with_ext(); // IC_GET_PROP + 3 ext
|
||||
self.count_logical_assign_test(logical_op, id, ctx);
|
||||
self.count_expression(right, ctx);
|
||||
ctx.count_ic_set_with_ext(); // IC_SET_PROP + 3 ext
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
|
||||
fn count_computed_member_logical_assignment(
|
||||
&self, member: &oxide_parser::ComputedMemberExpression<'_>, logical_op: LogicalOperator, right: &Expression,
|
||||
ctx: &mut CompileCtx,
|
||||
) {
|
||||
let id = ctx.next_label_id();
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // GET_PROP_DYNAMIC
|
||||
self.count_logical_assign_test(logical_op, id, ctx);
|
||||
self.count_expression(right, ctx);
|
||||
ctx.projected_pc += 1; // SET_PROP_DYNAMIC
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
|
||||
fn count_identifier_logical_assignment(
|
||||
&self, logical_op: LogicalOperator, right: &Expression, ctx: &mut CompileCtx,
|
||||
) {
|
||||
let id = ctx.next_label_id();
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- current var
|
||||
self.count_logical_assign_test(logical_op, id, ctx);
|
||||
self.count_expression(right, ctx);
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_call_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::CallExpression(call) = expr else {
|
||||
return;
|
||||
};
|
||||
if matches!(&call.callee, Expression::Super(_)) {
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
ctx.alloc_reg();
|
||||
ctx.count_call_instr_with_arg_ext(); // SUPER_CALL + arg_count ext word
|
||||
return;
|
||||
}
|
||||
|
||||
match &call.callee {
|
||||
Expression::PrivateFieldExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_private_access();
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
if matches!(&member.object, Expression::Super(_)) {
|
||||
ctx.count_load_var(); // this register
|
||||
ctx.count_load_const(); // key
|
||||
ctx.alloc_reg(); // callee
|
||||
ctx.count_instr(); // SUPER_GET_PROP
|
||||
} else {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_load_var(); // callee object copy
|
||||
ctx.count_ic_instr_with_ext(); // IC_GET_PROP + 3 ext words
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.count_expression(&call.callee, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
ctx.count_call_instr_with_arg_ext(); // CALL/CALL_NATIVE + arg_count ext word
|
||||
ctx.count_load_var(); // result <- regs[0]
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_new_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::NewExpression(ne) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&ne.callee, ctx);
|
||||
for arg in &ne.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
ctx.alloc_reg(); // result register
|
||||
ctx.count_instr_with_ext(1); // NEW_EXPRESSION + arg_count ext word
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_chain_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ChainExpression(chain) = expr else {
|
||||
return;
|
||||
};
|
||||
let id = ctx.next_label_id();
|
||||
self.count_chain_element(&chain.expression, true, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- chain value
|
||||
ctx.projected_pc += 1; // JMP over short-circuit writer
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
ctx.projected_pc += 1; // LOAD_CONST undefined
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_conditional_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ConditionalExpression(cond) = expr else {
|
||||
return;
|
||||
};
|
||||
let id = ctx.next_label_id();
|
||||
let else_label = Label::TernaryElse(id);
|
||||
let end_label = Label::TernaryEnd(id);
|
||||
|
||||
self.count_expression(&cond.test, ctx);
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE
|
||||
self.count_expression(&cond.consequent, ctx);
|
||||
ctx.alloc_reg(); // result register
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- consequent
|
||||
ctx.projected_pc += 1; // JMP to end
|
||||
ctx.label_map.insert(else_label, ctx.projected_pc);
|
||||
self.count_expression(&cond.alternate, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- alternate
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_sequence_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::SequenceExpression(seq) = expr else {
|
||||
return;
|
||||
};
|
||||
for e in &seq.expressions {
|
||||
self.count_expression(e, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_logical_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::LogicalExpression(log) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&log.left, ctx);
|
||||
|
||||
let is_simple = is_side_effect_free(&log.left) && is_side_effect_free(&log.right);
|
||||
|
||||
if is_simple {
|
||||
self.count_expression(&log.right, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // AND/OR
|
||||
} else {
|
||||
let id = ctx.next_label_id();
|
||||
ctx.alloc_reg(); // dup register
|
||||
ctx.projected_pc += 1; // LOAD_VAR (DUP)
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE, JMP_IF_TRUE, or JMP_IF_NULLISH
|
||||
if matches!(log.operator, LogicalOperator::Coalesce) {
|
||||
ctx.projected_pc += 1; // JMP over RHS on non-nullish
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
}
|
||||
self.count_expression(&log.right, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR (overwrite)
|
||||
let skip_label = match log.operator {
|
||||
LogicalOperator::And => Label::TernaryEnd(id),
|
||||
LogicalOperator::Or => Label::TernaryElse(id),
|
||||
LogicalOperator::Coalesce => Label::TernaryEnd(id),
|
||||
};
|
||||
ctx.label_map.insert(skip_label, ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_arrow_function_expression(&self, ctx: &mut CompileCtx) {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_function_expression(&self, ctx: &mut CompileCtx) {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_class_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ClassExpression(class) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_class(class, ctx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_this_expression(&self, ctx: &mut CompileCtx) {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR from reg 254
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_identifier_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::Identifier(ident) = expr else {
|
||||
return;
|
||||
};
|
||||
if CompileCtx::is_known_builtin(ident.name.as_str()) {
|
||||
let _ = ctx.lookup_or_builtin(ident.name.as_str());
|
||||
}
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_regexp_literal(&self, ctx: &mut CompileCtx) {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_default_expression(&self, ctx: &mut CompileCtx) {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST or LOAD_VAR
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_static_member_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::StaticMemberExpression(member) = expr else {
|
||||
return;
|
||||
};
|
||||
if matches!(&member.object, Expression::Super(_)) {
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_load_var(); // this
|
||||
ctx.alloc_reg(); // result
|
||||
ctx.count_instr(); // SUPER_GET_PROP
|
||||
return;
|
||||
}
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_ic_instr_with_ext(); // IC_GET_PROP + 3 ext words
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_computed_member_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ComputedMemberExpression(member) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // GET_PROP_DYNAMIC
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_private_field_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::PrivateFieldExpression(member) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_private_access();
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_parenthesized_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ParenthesizedExpression(p) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&p.expression, ctx);
|
||||
}
|
||||
}
|
||||
@@ -1,434 +1,45 @@
|
||||
use super::*;
|
||||
|
||||
mod assignment;
|
||||
mod call;
|
||||
mod chain;
|
||||
mod control;
|
||||
mod function;
|
||||
mod literal;
|
||||
mod member;
|
||||
mod object;
|
||||
mod operator;
|
||||
mod template;
|
||||
|
||||
impl Compiler {
|
||||
pub(crate) fn count_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
match expr {
|
||||
Expression::BinaryExpression(bin) => {
|
||||
let checkpoint = ctx.reg_checkpoint();
|
||||
self.count_expression(&bin.left, ctx);
|
||||
self.count_expression(&bin.right, ctx);
|
||||
ctx.projected_pc += 1; // ADD/SUB/MUL/DIV/etc.
|
||||
if is_side_effect_free(&bin.left) && is_side_effect_free(&bin.right) {
|
||||
ctx.restore_reg_checkpoint(checkpoint.saturating_add(1));
|
||||
}
|
||||
}
|
||||
Expression::PrivateInExpression(pin) => {
|
||||
self.count_expression(&pin.right, ctx);
|
||||
ctx.count_private_access();
|
||||
}
|
||||
Expression::UnaryExpression(un) => {
|
||||
match un.operator {
|
||||
UnaryOperator::Delete => {
|
||||
match &un.argument {
|
||||
Expression::Identifier(_) => {
|
||||
// SyntaxError at compile time, no bytecode cost
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_delete_static();
|
||||
}
|
||||
Expression::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.projected_pc += 1; // DELETE_PROP_DYNAMIC
|
||||
}
|
||||
Expression::ChainExpression(chain) => {
|
||||
match &chain.expression {
|
||||
ChainElement::StaticMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
if member.optional {
|
||||
self.count_optional_guard(ctx);
|
||||
}
|
||||
ctx.count_delete_static();
|
||||
}
|
||||
ChainElement::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
if member.optional {
|
||||
self.count_optional_guard(ctx);
|
||||
}
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.projected_pc += 1; // DELETE_PROP_DYNAMIC
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ctx.projected_pc += 1; // JMP over nullish true writer
|
||||
ctx.projected_pc += 1; // LOAD_CONST true
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.count_expression(&un.argument, ctx);
|
||||
ctx.projected_pc += 1; // NEG/TYPEOF/VOID/NOT
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::CallExpression(call) => {
|
||||
if matches!(&call.callee, Expression::Super(_)) {
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
ctx.alloc_reg();
|
||||
ctx.count_call_instr_with_arg_ext(); // SUPER_CALL + arg_count ext word
|
||||
return;
|
||||
}
|
||||
match &call.callee {
|
||||
Expression::PrivateFieldExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_private_access();
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
if matches!(&member.object, Expression::Super(_)) {
|
||||
ctx.count_load_var(); // this register
|
||||
ctx.count_load_const(); // key
|
||||
ctx.alloc_reg(); // callee
|
||||
ctx.count_instr(); // SUPER_GET_PROP
|
||||
} else {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_load_var(); // callee object copy
|
||||
ctx.count_ic_instr_with_ext(); // IC_GET_PROP + 3 ext words
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.count_expression(&call.callee, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
}
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
ctx.count_call_instr_with_arg_ext(); // CALL/CALL_NATIVE + arg_count ext word
|
||||
ctx.count_load_var(); // result <- regs[0]
|
||||
}
|
||||
Expression::AssignmentExpression(assign) => {
|
||||
if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
let id = ctx.next_label_id();
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_load_var(); // current value copy
|
||||
ctx.count_ic_instr_with_ext(); // IC_GET_PROP + 3 ext
|
||||
self.count_logical_assign_test(logical_op, id, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.count_ic_set_with_ext(); // IC_SET_PROP + 3 ext
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
return;
|
||||
}
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_ic_set_with_ext(); // IC_SET_PROP or COMPOUND_MEMBER_* + 3 ext words
|
||||
} else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
let id = ctx.next_label_id();
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // GET_PROP_DYNAMIC
|
||||
self.count_logical_assign_test(logical_op, id, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.projected_pc += 1; // SET_PROP_DYNAMIC
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
return;
|
||||
}
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // SET_PROP_DYNAMIC
|
||||
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.count_load_const(); // private id
|
||||
ctx.count_instr(); // SET_PRIVATE
|
||||
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(_) = &assign.left {
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
let id = ctx.next_label_id();
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- current var
|
||||
self.count_logical_assign_test(logical_op, id, ctx);
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- rhs
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
} else {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
ctx.projected_pc += 1; // COMPOUND_* on var
|
||||
} else {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
}
|
||||
}
|
||||
} else if let oxide_parser::AssignmentTarget::ArrayAssignmentTarget(ap) = &assign.left {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
self.count_array_assignment(ap, ctx);
|
||||
} else if let oxide_parser::AssignmentTarget::ObjectAssignmentTarget(op) = &assign.left {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
self.count_object_assignment(op, ctx);
|
||||
} else {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
}
|
||||
Expression::ConditionalExpression(cond) => {
|
||||
let id = ctx.next_label_id();
|
||||
let else_label = Label::TernaryElse(id);
|
||||
let end_label = Label::TernaryEnd(id);
|
||||
|
||||
self.count_expression(&cond.test, ctx);
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE
|
||||
self.count_expression(&cond.consequent, ctx);
|
||||
ctx.alloc_reg(); // result register
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- consequent
|
||||
ctx.projected_pc += 1; // JMP to end
|
||||
ctx.label_map.insert(else_label, ctx.projected_pc);
|
||||
self.count_expression(&cond.alternate, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- alternate
|
||||
ctx.label_map.insert(end_label, ctx.projected_pc);
|
||||
}
|
||||
Expression::SequenceExpression(seq) => {
|
||||
for e in &seq.expressions {
|
||||
self.count_expression(e, ctx);
|
||||
}
|
||||
}
|
||||
Expression::LogicalExpression(log) => {
|
||||
self.count_expression(&log.left, ctx);
|
||||
|
||||
let is_simple = is_side_effect_free(&log.left) && is_side_effect_free(&log.right);
|
||||
|
||||
if is_simple {
|
||||
self.count_expression(&log.right, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // AND/OR
|
||||
} else {
|
||||
use oxide_parser::LogicalOperator;
|
||||
let id = ctx.next_label_id();
|
||||
ctx.alloc_reg(); // dup register
|
||||
ctx.projected_pc += 1; // LOAD_VAR (DUP)
|
||||
ctx.projected_pc += 1; // JMP_IF_FALSE, JMP_IF_TRUE, or JMP_IF_NULLISH
|
||||
if matches!(log.operator, LogicalOperator::Coalesce) {
|
||||
ctx.projected_pc += 1; // JMP over RHS on non-nullish
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
}
|
||||
self.count_expression(&log.right, ctx);
|
||||
ctx.projected_pc += 1; // LOAD_VAR (overwrite)
|
||||
let skip_label = match log.operator {
|
||||
LogicalOperator::And => Label::TernaryEnd(id),
|
||||
LogicalOperator::Or => Label::TernaryElse(id),
|
||||
LogicalOperator::Coalesce => Label::TernaryEnd(id),
|
||||
};
|
||||
ctx.label_map.insert(skip_label, ctx.projected_pc);
|
||||
}
|
||||
}
|
||||
Expression::ChainExpression(chain) => {
|
||||
let id = ctx.next_label_id();
|
||||
self.count_chain_element(&chain.expression, true, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR result <- chain value
|
||||
ctx.projected_pc += 1; // JMP over short-circuit writer
|
||||
ctx.label_map.insert(Label::TernaryElse(id), ctx.projected_pc);
|
||||
ctx.projected_pc += 1; // LOAD_CONST undefined
|
||||
ctx.label_map.insert(Label::TernaryEnd(id), ctx.projected_pc);
|
||||
}
|
||||
Expression::ObjectExpression(obj) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // NEW_OBJECT
|
||||
let prop_checkpoint = ctx.reg_checkpoint();
|
||||
for prop in &obj.properties {
|
||||
if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
|
||||
if matches!(p.kind, oxide_parser::PropertyKind::Get | oxide_parser::PropertyKind::Set) {
|
||||
self.count_expression(&p.value, ctx);
|
||||
ctx.count_load_const(); // undefined getter/setter placeholder
|
||||
ctx.count_define_accessor(); // DEFINE_ACCESSOR + ext
|
||||
} else {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST key
|
||||
self.count_expression(&p.value, ctx);
|
||||
ctx.projected_pc += 1; // SET_PROP
|
||||
}
|
||||
ctx.restore_reg_checkpoint(prop_checkpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::TemplateLiteral(tl) => {
|
||||
// TemplateLiteral: N quasis (strings) + M expressions interleaved.
|
||||
// Allocate 1 result reg + count expressions.
|
||||
for expr in &tl.expressions {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
let segment_count = tl.quasis.len() + tl.expressions.len();
|
||||
ctx.count_template_str(segment_count);
|
||||
}
|
||||
Expression::TaggedTemplateExpression(tt) => {
|
||||
// Tagged template: tag expr + quasis as LOAD_CONST + expression args + CALL
|
||||
// Consecutive arg registers at end (via LOAD_VAR copies)
|
||||
self.count_expression(&tt.tag, ctx);
|
||||
let quasi_count = tt.quasi.quasis.len();
|
||||
// Each quasi: LOAD_CONST string + LOAD_CONST index + SET_ELEM
|
||||
// for both cooked and raw arrays
|
||||
for _ in 0..quasi_count {
|
||||
ctx.count_load_const(); // string
|
||||
ctx.count_load_const(); // index
|
||||
ctx.count_instr(); // SET_ELEM (cooked)
|
||||
}
|
||||
// Cooked array: alloc + NEW_ARRAY
|
||||
ctx.alloc_reg(); // cooked_temp
|
||||
ctx.count_instr(); // NEW_ARRAY
|
||||
|
||||
for _ in 0..quasi_count {
|
||||
ctx.count_load_const(); // string
|
||||
ctx.count_load_const(); // index
|
||||
ctx.count_instr(); // SET_ELEM (raw)
|
||||
}
|
||||
// Raw array: alloc + NEW_ARRAY
|
||||
ctx.alloc_reg(); // raw_temp
|
||||
ctx.count_instr(); // NEW_ARRAY
|
||||
|
||||
// Count expression arguments
|
||||
for expr in &tt.quasi.expressions {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
|
||||
// Consecutive arg slots: cooked_slot, raw_slot, N expr_slots
|
||||
ctx.alloc_reg(); // cooked_slot
|
||||
ctx.alloc_reg(); // raw_slot
|
||||
for _ in &tt.quasi.expressions {
|
||||
ctx.alloc_reg(); // expr_slot
|
||||
ctx.count_instr(); // LOAD_VAR
|
||||
}
|
||||
// LOAD_VAR for cooked and raw
|
||||
ctx.count_words(2);
|
||||
|
||||
// undefined this arg
|
||||
ctx.count_load_const();
|
||||
|
||||
// CALL + ext word
|
||||
ctx.count_call_instr_with_arg_ext();
|
||||
|
||||
// Result reg + LOAD_VAR
|
||||
ctx.count_load_var();
|
||||
}
|
||||
Expression::ArrowFunctionExpression(_arrow) => {
|
||||
// Arrow functions: alloc 1 register for the function value.
|
||||
// Body is compiled in the emit pass only (same pattern as FE).
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST
|
||||
}
|
||||
Expression::FunctionExpression(_fe) => {
|
||||
// No hoisting - function created at expression position.
|
||||
// Body is compiled in the emit pass only.
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST
|
||||
}
|
||||
Expression::ClassExpression(class) => {
|
||||
self.count_class(class, ctx);
|
||||
}
|
||||
Expression::NewExpression(ne) => {
|
||||
// Count callee expression
|
||||
self.count_expression(&ne.callee, ctx);
|
||||
// Count arguments
|
||||
for arg in &ne.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
ctx.alloc_reg(); // result register
|
||||
ctx.count_instr_with_ext(1); // NEW_EXPRESSION + arg_count ext word
|
||||
}
|
||||
Expression::ArrayExpression(arr) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // NEW_ARRAY
|
||||
let elem_checkpoint = ctx.reg_checkpoint();
|
||||
for elem in &arr.elements {
|
||||
if let Some(e) = elem.as_expression() {
|
||||
self.count_expression(e, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST index
|
||||
ctx.projected_pc += 1; // SET_ELEM
|
||||
ctx.restore_reg_checkpoint(elem_checkpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
if matches!(&member.object, Expression::Super(_)) {
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_load_var(); // this
|
||||
ctx.alloc_reg(); // result
|
||||
ctx.count_instr(); // SUPER_GET_PROP
|
||||
return;
|
||||
}
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.count_ic_instr_with_ext(); // IC_GET_PROP + 3 ext words
|
||||
}
|
||||
Expression::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // GET_PROP_DYNAMIC
|
||||
}
|
||||
Expression::PrivateFieldExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_private_access();
|
||||
}
|
||||
Expression::ParenthesizedExpression(p) => {
|
||||
self.count_expression(&p.expression, ctx);
|
||||
}
|
||||
Expression::ThisExpression(_) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR from reg 254
|
||||
}
|
||||
Expression::Identifier(ident) => {
|
||||
if CompileCtx::is_known_builtin(ident.name.as_str()) {
|
||||
let _ = ctx.lookup_or_builtin(ident.name.as_str());
|
||||
}
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR
|
||||
}
|
||||
Expression::UpdateExpression(update) => match &update.argument {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
SimpleAssignmentTarget::StaticMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.alloc_reg();
|
||||
ctx.count_ic_instr_with_ext(); // MEMBER_INC/MEMBER_DEC + 3 ext words
|
||||
}
|
||||
SimpleAssignmentTarget::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
_ => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
},
|
||||
Expression::RegExpLiteral(_) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
_ => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST or LOAD_VAR
|
||||
}
|
||||
Expression::BinaryExpression(_) => self.count_binary_expression(expr, ctx),
|
||||
Expression::PrivateInExpression(_) => self.count_private_in_expression(expr, ctx),
|
||||
Expression::UnaryExpression(_) => self.count_unary_expression(expr, ctx),
|
||||
Expression::CallExpression(_) => self.count_call_expression(expr, ctx),
|
||||
Expression::AssignmentExpression(_) => self.count_assignment_expression(expr, ctx),
|
||||
Expression::ConditionalExpression(_) => self.count_conditional_expression(expr, ctx),
|
||||
Expression::SequenceExpression(_) => self.count_sequence_expression(expr, ctx),
|
||||
Expression::LogicalExpression(_) => self.count_logical_expression(expr, ctx),
|
||||
Expression::ChainExpression(_) => self.count_chain_expression(expr, ctx),
|
||||
Expression::ObjectExpression(_) => self.count_object_expression(expr, ctx),
|
||||
Expression::TemplateLiteral(_) => self.count_template_literal(expr, ctx),
|
||||
Expression::TaggedTemplateExpression(_) => self.count_tagged_template_expression(expr, ctx),
|
||||
Expression::ArrowFunctionExpression(_) => self.count_arrow_function_expression(ctx),
|
||||
Expression::FunctionExpression(_) => self.count_function_expression(ctx),
|
||||
Expression::ClassExpression(_) => self.count_class_expression(expr, ctx),
|
||||
Expression::NewExpression(_) => self.count_new_expression(expr, ctx),
|
||||
Expression::ArrayExpression(_) => self.count_array_expression(expr, ctx),
|
||||
Expression::StaticMemberExpression(_) => self.count_static_member_expression(expr, ctx),
|
||||
Expression::ComputedMemberExpression(_) => self.count_computed_member_expression(expr, ctx),
|
||||
Expression::PrivateFieldExpression(_) => self.count_private_field_expression(expr, ctx),
|
||||
Expression::ParenthesizedExpression(_) => self.count_parenthesized_expression(expr, ctx),
|
||||
Expression::ThisExpression(_) => self.count_this_expression(ctx),
|
||||
Expression::Identifier(_) => self.count_identifier_expression(expr, ctx),
|
||||
Expression::UpdateExpression(_) => self.count_update_expression(expr, ctx),
|
||||
Expression::RegExpLiteral(_) => self.count_regexp_literal(ctx),
|
||||
_ => self.count_default_expression(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_object_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ObjectExpression(obj) = expr else {
|
||||
return;
|
||||
};
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // NEW_OBJECT
|
||||
let prop_checkpoint = ctx.reg_checkpoint();
|
||||
for prop in &obj.properties {
|
||||
if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
|
||||
if matches!(p.kind, oxide_parser::PropertyKind::Get | oxide_parser::PropertyKind::Set) {
|
||||
self.count_expression(&p.value, ctx);
|
||||
ctx.count_load_const(); // undefined getter/setter placeholder
|
||||
ctx.count_define_accessor(); // DEFINE_ACCESSOR + ext
|
||||
} else {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST key
|
||||
self.count_expression(&p.value, ctx);
|
||||
ctx.projected_pc += 1; // SET_PROP
|
||||
}
|
||||
ctx.restore_reg_checkpoint(prop_checkpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_array_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::ArrayExpression(arr) = expr else {
|
||||
return;
|
||||
};
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // NEW_ARRAY
|
||||
let elem_checkpoint = ctx.reg_checkpoint();
|
||||
for elem in &arr.elements {
|
||||
if let Some(e) = elem.as_expression() {
|
||||
self.count_expression(e, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_CONST index
|
||||
ctx.projected_pc += 1; // SET_ELEM
|
||||
ctx.restore_reg_checkpoint(elem_checkpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_binary_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::BinaryExpression(bin) = expr else {
|
||||
return;
|
||||
};
|
||||
let checkpoint = ctx.reg_checkpoint();
|
||||
self.count_expression(&bin.left, ctx);
|
||||
self.count_expression(&bin.right, ctx);
|
||||
ctx.projected_pc += 1; // ADD/SUB/MUL/DIV/etc.
|
||||
if is_side_effect_free(&bin.left) && is_side_effect_free(&bin.right) {
|
||||
ctx.restore_reg_checkpoint(checkpoint.saturating_add(1));
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_private_in_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::PrivateInExpression(pin) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&pin.right, ctx);
|
||||
ctx.count_private_access();
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_unary_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::UnaryExpression(un) = expr else {
|
||||
return;
|
||||
};
|
||||
match un.operator {
|
||||
UnaryOperator::Delete => self.count_delete_expression(&un.argument, ctx),
|
||||
_ => {
|
||||
self.count_expression(&un.argument, ctx);
|
||||
ctx.projected_pc += 1; // NEG/TYPEOF/VOID/NOT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn count_delete_expression(&self, argument: &Expression, ctx: &mut CompileCtx) {
|
||||
match argument {
|
||||
Expression::Identifier(_) => {
|
||||
// SyntaxError at compile time, no bytecode cost.
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_delete_static();
|
||||
}
|
||||
Expression::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.projected_pc += 1; // DELETE_PROP_DYNAMIC
|
||||
}
|
||||
Expression::ChainExpression(chain) => {
|
||||
self.count_delete_chain_expression(&chain.expression, ctx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn count_delete_chain_expression(&self, chain: &ChainElement, ctx: &mut CompileCtx) {
|
||||
match chain {
|
||||
ChainElement::StaticMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
if member.optional {
|
||||
self.count_optional_guard(ctx);
|
||||
}
|
||||
ctx.count_delete_static();
|
||||
}
|
||||
ChainElement::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
if member.optional {
|
||||
self.count_optional_guard(ctx);
|
||||
}
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.projected_pc += 1; // DELETE_PROP_DYNAMIC
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ctx.projected_pc += 1; // JMP over nullish true writer
|
||||
ctx.projected_pc += 1; // LOAD_CONST true
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_update_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::UpdateExpression(update) = expr else {
|
||||
return;
|
||||
};
|
||||
match &update.argument {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
SimpleAssignmentTarget::StaticMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
ctx.count_load_const(); // key
|
||||
ctx.alloc_reg();
|
||||
ctx.count_ic_instr_with_ext(); // MEMBER_INC/MEMBER_DEC + 3 ext words
|
||||
}
|
||||
SimpleAssignmentTarget::ComputedMemberExpression(member) => {
|
||||
self.count_expression(&member.object, ctx);
|
||||
self.count_expression(&member.expression, ctx);
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
_ => {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
use super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_template_literal(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::TemplateLiteral(tl) = expr else {
|
||||
return;
|
||||
};
|
||||
for expr in &tl.expressions {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
let segment_count = tl.quasis.len() + tl.expressions.len();
|
||||
ctx.count_template_str(segment_count);
|
||||
}
|
||||
|
||||
pub(in crate::counter) fn count_tagged_template_expression(&self, expr: &Expression, ctx: &mut CompileCtx) {
|
||||
let Expression::TaggedTemplateExpression(tt) = expr else {
|
||||
return;
|
||||
};
|
||||
self.count_expression(&tt.tag, ctx);
|
||||
let quasi_count = tt.quasi.quasis.len();
|
||||
|
||||
for _ in 0..quasi_count {
|
||||
ctx.count_load_const(); // string
|
||||
ctx.count_load_const(); // index
|
||||
ctx.count_instr(); // SET_ELEM (cooked)
|
||||
}
|
||||
ctx.alloc_reg(); // cooked_temp
|
||||
ctx.count_instr(); // NEW_ARRAY
|
||||
|
||||
for _ in 0..quasi_count {
|
||||
ctx.count_load_const(); // string
|
||||
ctx.count_load_const(); // index
|
||||
ctx.count_instr(); // SET_ELEM (raw)
|
||||
}
|
||||
ctx.alloc_reg(); // raw_temp
|
||||
ctx.count_instr(); // NEW_ARRAY
|
||||
|
||||
for expr in &tt.quasi.expressions {
|
||||
self.count_expression(expr, ctx);
|
||||
}
|
||||
|
||||
ctx.alloc_reg(); // cooked_slot
|
||||
ctx.alloc_reg(); // raw_slot
|
||||
for _ in &tt.quasi.expressions {
|
||||
ctx.alloc_reg(); // expr_slot
|
||||
ctx.count_instr(); // LOAD_VAR
|
||||
}
|
||||
ctx.count_words(2); // LOAD_VAR for cooked and raw
|
||||
ctx.count_load_const(); // undefined this arg
|
||||
ctx.count_call_instr_with_arg_ext();
|
||||
ctx.count_load_var(); // result <- regs[0]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user