Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip
  • Loading branch information
kermanx committed Dec 17, 2025
commit d438ebdd083cba43ab6e59900dca53f5b8cc5670
4 changes: 2 additions & 2 deletions crates/jsshaker/src/analyzer/rw_tracking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<'a> ReadWriteTarget<'a> {
}

#[derive(Debug, Clone, Copy)]
pub enum TrackReadCachable<'a> {
pub enum TrackReadCacheable<'a> {
Immutable,
Mutable(EntityOrTDZ<'a>),
}
Expand All @@ -43,7 +43,7 @@ impl<'a> Analyzer<'a> {
&mut self,
scope: CfScopeId,
target: ReadWriteTarget<'a>,
cacheable: Option<TrackReadCachable<'a>>,
cacheable: Option<TrackReadCacheable<'a>>,
) {
let target_depth = self.find_first_different_cf_scope(scope);
let mut registered = false;
Expand Down
29 changes: 10 additions & 19 deletions crates/jsshaker/src/nodes/expr/arrow_function_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ use oxc::ast::{
use crate::{
analyzer::Analyzer,
ast::{AstKind2, DeclarationKind},
dep::Dep,
entity::Entity,
scope::VariableScopeId,
transformer::Transformer,
utils::CalleeNode,
value::{ArgumentsValue, FunctionValue, cache::FnCacheTrackingData},
value::{cache::FnCacheTrackingData, call::FunctionCallInfo},
};

impl<'a> Analyzer<'a> {
Expand All @@ -24,38 +22,31 @@ impl<'a> Analyzer<'a> {

pub fn call_arrow_function_expression(
&mut self,
func: &'a FunctionValue<'a>,
call_dep: Dep<'a>,
node: &'a ArrowFunctionExpression<'a>,
variable_scopes: &'a [VariableScopeId],
args: ArgumentsValue<'a>,
consume: bool,
info: FunctionCallInfo<'a>,
) -> (Entity<'a>, FnCacheTrackingData<'a>) {
let runner = move |analyzer: &mut Analyzer<'a>| {
analyzer.push_call_scope(
func,
call_dep,
variable_scopes.to_vec(),
node.r#async,
false,
consume,
);
analyzer.push_call_scope(info, node.r#async, false);

analyzer.exec_formal_parameters(&node.params, args, DeclarationKind::ArrowFunctionParameter);
analyzer.exec_formal_parameters(
&node.params,
info.args,
DeclarationKind::ArrowFunctionParameter,
);
if node.expression {
analyzer.exec_function_expression_body(&node.body);
} else {
analyzer.exec_function_body(&node.body);
}

if consume {
if info.consume {
analyzer.consume_return_values();
}

analyzer.pop_call_scope()
};

if !consume && node.r#async {
if !info.consume && node.r#async {
// Too complex to analyze the control flow, thus run exhaustively
self.exec_async_or_generator_fn(move |analyzer| {
runner(analyzer).0.consume(analyzer);
Expand Down
26 changes: 10 additions & 16 deletions crates/jsshaker/src/nodes/misc/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ use oxc::{
use crate::{
analyzer::Analyzer,
ast::{AstKind2, DeclarationKind},
dep::Dep,
entity::Entity,
scope::VariableScopeId,
transformer::Transformer,
utils::CalleeNode,
value::{ArgumentsValue, FunctionValue, ObjectPrototype, ValueTrait, cache::FnCacheTrackingData},
value::{ObjectPrototype, ValueTrait, cache::FnCacheTrackingData, call::FunctionCallInfo},
};

#[derive(Default)]
Expand Down Expand Up @@ -128,22 +126,18 @@ impl<'a> Analyzer<'a> {

pub fn call_class_constructor(
&mut self,
func: &'a FunctionValue<'a>,
call_dep: Dep<'a>,
node: &'a Class<'a>,
variable_scopes: &'a [VariableScopeId],
this: Entity<'a>,
args: ArgumentsValue<'a>,
consume: bool,
info: FunctionCallInfo<'a>,
) -> (Entity<'a>, FnCacheTrackingData<'a>) {
let factory = self.factory;
let data = self.load_data::<Data>(AstKind2::Class(node));

self.push_call_scope(func, call_dep, variable_scopes.to_vec(), false, false, consume);
self.push_call_scope(info, false, false);
let super_class = data.super_class.unwrap_or(self.factory.undefined);
let variable_scope = self.variable_scope_mut();
variable_scope.this = Some(this);
variable_scope.arguments = Some((args, factory.vec(/* later filled by formal parameters */)));
variable_scope.this = Some(info.this);
variable_scope.arguments =
Some((info.args, factory.vec(/* later filled by formal parameters */)));
variable_scope.super_class = Some(super_class);

if let Some(id) = &node.id {
Expand All @@ -157,7 +151,7 @@ impl<'a> Analyzer<'a> {
&& !node.r#static
{
let value = self.exec_property_definition(node);
this.set_property(self, self.factory.no_dep, key.unwrap(), value);
info.this.set_property(self, self.factory.no_dep, key.unwrap(), value);
}
}

Expand All @@ -166,15 +160,15 @@ impl<'a> Analyzer<'a> {
let function = constructor.value.as_ref();
let dep = self.factory.dep(AstKind2::Function(function));
self.cf_scope_mut().push_dep(dep);
self.exec_formal_parameters(&function.params, args, DeclarationKind::FunctionParameter);
self.exec_formal_parameters(&function.params, info.args, DeclarationKind::FunctionParameter);
self.exec_function_body(function.body.as_ref().unwrap());
if consume {
if info.consume {
self.consume_return_values();
}
self.pop_call_scope()
} else if let Some(super_class) = &data.super_class {
self.pop_call_scope();
let ret_val = super_class.call(self, self.factory.no_dep, this, args);
let ret_val = super_class.call(self, self.factory.no_dep, info.this, info.args);
(ret_val, FnCacheTrackingData::worst_case())
} else {
let (_, cache_tracking) = self.pop_call_scope();
Expand Down
33 changes: 10 additions & 23 deletions crates/jsshaker/src/nodes/misc/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ use oxc::{
use crate::{
analyzer::Analyzer,
ast::{AstKind2, DeclarationKind},
dep::Dep,
entity::Entity,
scope::VariableScopeId,
transformer::Transformer,
utils::CalleeNode,
value::{ArgumentsValue, FunctionValue, cache::FnCacheTrackingData},
value::{cache::FnCacheTrackingData, call::FunctionCallInfo},
};

impl<'a> Analyzer<'a> {
Expand All @@ -40,28 +38,17 @@ impl<'a> Analyzer<'a> {

pub fn call_function(
&mut self,
func: &'a FunctionValue<'a>,
call_dep: Dep<'a>,
node: &'a Function<'a>,
variable_scopes: &'a [VariableScopeId],
this: Entity<'a>,
args: ArgumentsValue<'a>,
consume: bool,
info: FunctionCallInfo<'a>,
) -> (Entity<'a>, FnCacheTrackingData<'a>) {
let runner = move |analyzer: &mut Analyzer<'a>| {
analyzer.push_call_scope(
func,
call_dep,
variable_scopes.to_vec(),
node.r#async,
node.generator,
consume,
);
analyzer.push_call_scope(info, node.r#async, node.generator);

let factory = analyzer.factory;
let variable_scope = analyzer.variable_scope_mut();
variable_scope.this = Some(this);
variable_scope.arguments = Some((args, factory.vec(/* later filled by formal parameters */)));
variable_scope.this = Some(info.this);
variable_scope.arguments =
Some((info.args, factory.vec(/* later filled by formal parameters */)));

let declare_in_body = node.r#type == FunctionType::FunctionExpression && node.id.is_some();
if declare_in_body {
Expand All @@ -71,21 +58,21 @@ impl<'a> Analyzer<'a> {
AstKind2::BindingIdentifier(id),
false,
DeclarationKind::NamedFunctionInBody,
Some(analyzer.factory.computed(func.into(), AstKind2::BindingIdentifier(id))),
Some(analyzer.factory.computed(info.func.into(), AstKind2::BindingIdentifier(id))),
);
}

analyzer.exec_formal_parameters(&node.params, args, DeclarationKind::FunctionParameter);
analyzer.exec_formal_parameters(&node.params, info.args, DeclarationKind::FunctionParameter);
analyzer.exec_function_body(node.body.as_ref().unwrap());

if consume {
if info.consume {
analyzer.consume_return_values();
}

analyzer.pop_call_scope()
};

if !consume && (node.r#async || node.generator) {
if !info.consume && (node.r#async || node.generator) {
// Too complex to analyze the control flow, thus run exhaustively
self.exec_async_or_generator_fn(move |analyzer| {
runner(analyzer).0.consume(analyzer);
Expand Down
20 changes: 9 additions & 11 deletions crates/jsshaker/src/scope/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
entity::Entity,
module::ModuleId,
utils::{CalleeInfo, CalleeNode},
value::{FunctionValue, ObjectId, cache::FnCacheTrackingData},
value::{ObjectId, cache::FnCacheTrackingData, call::FunctionCallInfo},
};

pub struct Scoping<'a> {
Expand Down Expand Up @@ -106,32 +106,30 @@ impl<'a> Analyzer<'a> {

pub fn push_call_scope(
&mut self,
func: &'a FunctionValue<'a>,
call_dep: Dep<'a>,
variable_scope_stack: Vec<VariableScopeId>,
info: FunctionCallInfo<'a>,
is_async: bool,
is_generator: bool,
consume: bool,
) {
let dep_id = DepAtom::from_counter();
if consume {
if info.consume {
self.refer_dep(dep_id);
}

self.module_stack.push(func.callee.module_id);
let old_variable_scope_stack = self.replace_variable_scope_stack(variable_scope_stack);
self.module_stack.push(info.func.callee.module_id);
let old_variable_scope_stack =
self.replace_variable_scope_stack(info.func.variable_scope_stack.to_vec());
let body_variable_scope = self.push_variable_scope();
let cf_scope_depth = self.push_cf_scope_with_deps(
CfScopeKind::Function(
self.allocator.alloc(FnCacheTrackingData::new_in(self.allocator, func)),
self.allocator.alloc(FnCacheTrackingData::new_in(self.allocator, info)),
),
self.factory.vec1(self.dep((call_dep, dep_id))),
self.factory.vec1(self.dep((info.call_dep, dep_id))),
false,
);

self.scoping.call.push(CallScope::new_in(
dep_id,
func.callee,
info.func.callee,
old_variable_scope_stack,
cf_scope_depth,
body_variable_scope,
Expand Down
6 changes: 3 additions & 3 deletions crates/jsshaker/src/scope/variable_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use oxc::{
use super::cf_scope::CfScopeId;
use crate::{
analyzer::Analyzer,
analyzer::rw_tracking::{ReadWriteTarget, TrackReadCachable},
analyzer::rw_tracking::{ReadWriteTarget, TrackReadCacheable},
ast::DeclarationKind,
define_box_bump_idx,
dep::{Dep, LazyDep},
Expand Down Expand Up @@ -206,9 +206,9 @@ impl<'a> Analyzer<'a> {
cf_scope,
ReadWriteTarget::Variable(scope, symbol),
Some(if may_change {
TrackReadCachable::Mutable(value)
TrackReadCacheable::Mutable(value)
} else {
TrackReadCachable::Immutable
TrackReadCacheable::Immutable
}),
);
value
Expand Down
26 changes: 12 additions & 14 deletions crates/jsshaker/src/value/function/bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ use oxc::span::Span;

use crate::{
Analyzer,
dep::Dep,
entity::Entity,
scope::VariableScopeId,
value::{ArgumentsValue, FunctionValue, cache::FnCacheTrackingData},
value::{ArgumentsValue, cache::FnCacheTrackingData, call::FunctionCallInfo},
};

#[derive(Debug)]
Expand All @@ -17,17 +15,12 @@ pub struct BoundFunction<'a> {
}

impl<'a> Analyzer<'a> {
pub fn call_bound_function(
pub fn call_bound_function<const IS_CTOR: bool>(
&mut self,
func: &'a FunctionValue<'a>,
call_dep: Dep<'a>,
bound_fn: &'a BoundFunction<'a>,
variable_scopes: &'a [VariableScopeId],
ctor_this: Option<Entity<'a>>,
args: ArgumentsValue<'a>,
consume: bool,
info: FunctionCallInfo<'a>,
) -> (Entity<'a>, FnCacheTrackingData<'a>) {
self.push_call_scope(func, call_dep, variable_scopes.to_vec(), false, false, consume);
self.push_call_scope(info, false, false);

// self.exec_formal_parameters(&node.params, args, DeclarationKind::ArrowFunctionParameter);
// if node.expression {
Expand All @@ -36,11 +29,16 @@ impl<'a> Analyzer<'a> {
// self.exec_function_body(&node.body);
// }

let args = ArgumentsValue::from_concatenate(self, bound_fn.bound_args, args);
let ret = bound_fn.target.call(self, call_dep, ctor_this.unwrap_or(bound_fn.bound_this), args);
let args = ArgumentsValue::from_concatenate(self, bound_fn.bound_args, info.args);
let ret = bound_fn.target.call(
self,
info.call_dep,
if IS_CTOR { info.this } else { bound_fn.bound_this },
args,
);
self.return_value(ret, self.factory.no_dep);

if consume {
if info.consume {
self.consume_return_values();
}

Expand Down
Loading
Loading