Skip to content

Commit 0fdec33

Browse files
committed
feat: shake unused args in recursed functions
1 parent 0c5c83a commit 0fdec33

File tree

6 files changed

+82
-47
lines changed

6 files changed

+82
-47
lines changed

crates/jsshaker/src/dep/atom.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ impl Debug for DepAtom {
1818
}
1919
}
2020

21+
impl DepAtom {
22+
pub fn placeholder() -> Self {
23+
AstKind2::ENVIRONMENT.into()
24+
}
25+
}
26+
2127
impl<'a> CustomDepTrait<'a> for DepAtom {
2228
fn consume(&self, analyzer: &mut Analyzer<'a>) {
2329
analyzer.include_atom(*self);

crates/jsshaker/src/value/function/cache.rs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ pub struct FnCacheTrackDeps<'a> {
115115
}
116116

117117
impl<'a> FnCacheTrackDeps<'a> {
118-
pub fn wrap(
118+
pub fn wrap<const UNKNOWN: bool>(
119119
analyzer: &Analyzer<'a>,
120120
call_id: DepAtom,
121121
this: &mut Entity<'a>,
@@ -129,18 +129,52 @@ impl<'a> FnCacheTrackDeps<'a> {
129129
for arg in args.elements {
130130
let arg_dep = EntityTrackerDep::default();
131131
arg_deps.push(arg_dep);
132-
new_args.push(factory.computed(*arg, arg_dep));
132+
new_args.push(if UNKNOWN {
133+
factory.computed_unknown((*arg, arg_dep))
134+
} else {
135+
factory.computed(*arg, arg_dep)
136+
});
133137
}
134138
args.elements = new_args.into_bump_slice();
135139
let rest_dep = if let Some(rest) = &mut args.rest {
136140
let rest_dep = EntityTrackerDep::default();
137-
*rest = factory.computed(*rest, rest_dep);
141+
*rest = if UNKNOWN {
142+
factory.computed_unknown((*rest, rest_dep))
143+
} else {
144+
factory.computed(*rest, rest_dep)
145+
};
146+
Some(rest_dep)
147+
} else if UNKNOWN {
148+
let rest_dep = EntityTrackerDep::default();
149+
args.rest = Some(factory.computed_unknown(rest_dep));
138150
Some(rest_dep)
139151
} else {
140152
None
141153
};
142154
Self { call_id, this: this_dep, args: arg_deps.into_bump_slice(), rest: rest_dep }
143155
}
156+
157+
pub fn assoc(
158+
self,
159+
analyzer: &mut Analyzer<'a>,
160+
call_dep: Dep<'a>,
161+
this: Entity<'a>,
162+
args: &ArgumentsValue<'a>,
163+
) {
164+
analyzer.add_assoc_dep(self.call_id, call_dep);
165+
analyzer.add_assoc_entity_dep(self.this, this);
166+
for (dep, arg) in self.args.iter().zip(args.elements.iter()) {
167+
analyzer.add_assoc_entity_dep(*dep, *arg);
168+
}
169+
if let Some(dep) = self.rest {
170+
if let Some(rest) = args.rest {
171+
analyzer.add_assoc_entity_dep(dep, rest);
172+
}
173+
for arg in args.elements.iter().skip(self.args.len()) {
174+
analyzer.add_assoc_entity_dep(dep, *arg);
175+
}
176+
}
177+
}
144178
}
145179

146180
#[derive(Debug)]
@@ -284,17 +318,7 @@ impl<'a> FnCache<'a> {
284318
let ret = analyzer.factory.computed(cached.ret, call_dep);
285319
let has_global_effects = cached.has_global_effects;
286320
drop(table);
287-
288-
analyzer.add_assoc_dep(track_deps.call_id, call_dep);
289-
290-
analyzer.add_assoc_entity_dep(track_deps.this, this);
291-
for (dep, arg) in track_deps.args.iter().zip(args.elements.iter()) {
292-
analyzer.add_assoc_entity_dep(*dep, *arg);
293-
}
294-
for (dep, rest) in track_deps.rest.iter().zip(args.rest.iter()) {
295-
analyzer.add_assoc_entity_dep(*dep, *rest);
296-
}
297-
321+
track_deps.assoc(analyzer, call_dep, this, &args);
298322
if has_global_effects {
299323
analyzer.global_effect();
300324
}

crates/jsshaker/src/value/function/call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a> FunctionValue<'a> {
4747
{
4848
return cached_ret;
4949
} else {
50-
Some(FnCacheTrackDeps::wrap(analyzer, call_id, &mut this, &mut args))
50+
Some(FnCacheTrackDeps::wrap::<false>(analyzer, call_id, &mut this, &mut args))
5151
}
5252
} else {
5353
None

crates/jsshaker/src/value/function/mod.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ use super::{
1616
use crate::{
1717
analyzer::Analyzer,
1818
builtin_string,
19-
dep::{Dep, LazyDep},
19+
dep::{Dep, DepAtom},
2020
entity::Entity,
2121
scope::VariableScopeId,
2222
utils::{CalleeInfo, CalleeNode},
23-
value::cache::FnCache,
23+
value::cache::{FnCache, FnCacheTrackDeps},
2424
};
2525
pub use arguments::*;
2626
pub use builtin::*;
@@ -33,15 +33,14 @@ pub struct FunctionValue<'a> {
3333
pub finite_recursion: bool,
3434
pub statics: &'a ObjectValue<'a>,
3535

36-
// Workaround: The lazy dep of `this` value
37-
body_consumed: Cell<Option<LazyDep<'a, Entity<'a>>>>,
36+
body_consumed: Cell<Option<&'a FnCacheTrackDeps<'a>>>,
3837

3938
cache: FnCache<'a>,
4039
}
4140

4241
impl<'a> ValueTrait<'a> for FunctionValue<'a> {
4342
fn consume(&'a self, analyzer: &mut Analyzer<'a>) {
44-
self.consume_body(analyzer, analyzer.factory.unknown);
43+
self.consume_body(analyzer, analyzer.factory.unknown, analyzer.factory.unknown_arguments);
4544
self.statics.consume(analyzer);
4645
}
4746

@@ -88,14 +87,16 @@ impl<'a> ValueTrait<'a> for FunctionValue<'a> {
8887
this: Entity<'a>,
8988
args: ArgumentsValue<'a>,
9089
) -> Entity<'a> {
91-
if let Some(this_dep) = self.body_consumed.get() {
92-
this_dep.push(analyzer, this);
93-
return consumed_object::call(self, analyzer, dep, analyzer.factory.unknown, args);
90+
if let Some(track_deps) = self.body_consumed.get() {
91+
analyzer.global_effect();
92+
track_deps.assoc(analyzer, dep, this, &args);
93+
return analyzer.factory.unknown;
9494
}
9595

9696
if self.check_recursion(analyzer) {
97-
self.consume_body(analyzer, this);
98-
return consumed_object::call(self, analyzer, dep, analyzer.factory.unknown, args);
97+
analyzer.global_effect();
98+
analyzer.consume(dep);
99+
return self.consume_body(analyzer, this, args);
99100
}
100101

101102
self.call_impl::<false>(analyzer, dep, this, args, false)
@@ -107,13 +108,16 @@ impl<'a> ValueTrait<'a> for FunctionValue<'a> {
107108
dep: Dep<'a>,
108109
args: ArgumentsValue<'a>,
109110
) -> Entity<'a> {
110-
if self.body_consumed.get().is_some() {
111-
return consumed_object::construct(self, analyzer, dep, args);
111+
if let Some(track_deps) = self.body_consumed.get() {
112+
analyzer.global_effect();
113+
track_deps.assoc(analyzer, dep, analyzer.factory.unknown, &args);
114+
return analyzer.factory.unknown;
112115
}
113116

114117
if self.check_recursion(analyzer) {
115-
self.consume_body(analyzer, analyzer.factory.unknown);
116-
return consumed_object::construct(self, analyzer, dep, args);
118+
analyzer.global_effect();
119+
analyzer.consume(dep);
120+
return self.consume_body(analyzer, analyzer.factory.unknown, args);
117121
}
118122

119123
self.construct_impl(analyzer, dep, args, false)
@@ -215,14 +219,19 @@ impl<'a> FunctionValue<'a> {
215219
false
216220
}
217221

218-
pub fn consume_body(&'a self, analyzer: &mut Analyzer<'a>, this: Entity<'a>) {
222+
pub fn consume_body(
223+
&'a self,
224+
analyzer: &mut Analyzer<'a>,
225+
mut this: Entity<'a>,
226+
mut args: ArgumentsValue<'a>,
227+
) -> Entity<'a> {
219228
if self.body_consumed.get().is_some() {
220-
return;
229+
return analyzer.factory.unknown;
221230
}
222231

223-
let this_dep = analyzer.factory.lazy_dep(analyzer.factory.vec1(this));
224-
let this = analyzer.factory.computed_unknown(this_dep);
225-
self.body_consumed.set(Some(this_dep));
232+
let track_deps =
233+
FnCacheTrackDeps::wrap::<true>(analyzer, DepAtom::placeholder(), &mut this, &mut args);
234+
self.body_consumed.set(Some(analyzer.allocator.alloc(track_deps)));
226235

227236
analyzer.consume(self.callee.into_node());
228237

@@ -232,14 +241,8 @@ impl<'a> FunctionValue<'a> {
232241
let name = "";
233242

234243
analyzer.exec_consumed_fn(name, move |analyzer| {
235-
self.call_impl::<false>(
236-
analyzer,
237-
analyzer.factory.no_dep,
238-
this,
239-
analyzer.factory.unknown_arguments,
240-
true,
241-
)
242-
});
244+
self.call_impl::<false>(analyzer, analyzer.factory.no_dep, this, args, true)
245+
})
243246
}
244247
}
245248

@@ -267,7 +270,7 @@ impl<'a> Analyzer<'a> {
267270
}
268271

269272
if created_in_self {
270-
function.consume_body(self, self.factory.unknown);
273+
function.consume_body(self, self.factory.unknown, self.factory.unknown_arguments);
271274
}
272275

273276
(function, prototype)

crates/jsshaker/tests/fixtures/recursion.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ export function simple() {
1717
}
1818

1919
export function complex1() {
20-
function main(a) {
21-
return () => g(a)
20+
function main(a, b) {
21+
console.log(a)
22+
return () => g(a, b)
2223
}
2324

24-
function g(a) {
25-
return () => main(a + 1)
25+
function g(a, b) {
26+
return () => main(a + 1, b * 2)
2627
}
2728

2829
t = main(1)

crates/jsshaker/tests/snapshots/test@recursion.js.snap

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)