Skip to content

Commit 0463220

Browse files
committed
fix: object mangling group, super class, object has_own, function.prototype
1 parent 0c7b0c9 commit 0463220

18 files changed

Lines changed: 180 additions & 111 deletions

File tree

crates/jsshaker/src/builtins/globals/object_constructor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ impl<'a> Builtins<'a> {
261261
let deps = analyzer.dep((proto, dep));
262262
(deps, ObjectPrototype::Unknown(deps))
263263
};
264-
let mangling = analyzer.new_object_mangling_group();
264+
let mangling = analyzer.mangler.new_object_group();
265265
let object = analyzer.new_empty_object(prototype, Some(mangling));
266266
object.add_extra_dep(deps);
267267
object.into()

crates/jsshaker/src/builtins/prototypes/function.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,11 @@ pub fn create_function_prototype<'a>(factory: &'a Factory<'a>) -> BuiltinPrototy
3333
bound_this,
3434
bound_args,
3535
});
36-
analyzer.factory.computed(analyzer.new_function(CalleeNode::BoundFunction(bound_fn)).into(), (dep, args.get_last_shallow_dep(analyzer)))
36+
analyzer.factory.computed(analyzer.new_function(CalleeNode::BoundFunction(bound_fn), false).0.into(), (dep, args.get_last_shallow_dep(analyzer)))
3737
}),
3838
"length" => factory.unknown_number,
3939
"arguments" => factory.unknown,
4040
"caller" => factory.unknown,
4141
"name" => factory.unknown_string,
42-
"prototype" => factory.unknown,
4342
})
4443
}

crates/jsshaker/src/builtins/prototypes/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ impl<'a> BuiltinPrototype<'a> {
101101
analyzer.factory.computed_unknown(dep)
102102
}
103103
}
104+
105+
pub fn test_has_own(&self, key: PropertyKeyValue<'a>) -> Option<bool> {
106+
if self.get(&key).is_some() { Some(true) } else { None }
107+
}
104108
}
105109

106110
pub struct BuiltinPrototypes<'a> {

crates/jsshaker/src/mangling/constraint.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::mem;
22

3-
use rustc_hash::FxHashSet;
3+
use oxc::allocator::{self, TakeIn};
44

55
use super::{AtomState, MangleAtom};
66
use super::{Mangler, UniquenessGroupId};
@@ -116,7 +116,11 @@ impl<'a> Mangler<'a> {
116116
}
117117
}
118118
} else {
119-
let group = uniqueness_groups.push((vec![a, b], 0, FxHashSet::default()));
119+
let group = uniqueness_groups.alloc((
120+
allocator::Vec::from_array_in([a, b], self.allocator),
121+
0,
122+
allocator::HashSet::new_in(self.allocator),
123+
));
120124
ua.insert(UniquenessConstraint::Group(group));
121125
ub.insert(UniquenessConstraint::Group(group));
122126
}
@@ -143,7 +147,7 @@ impl<'a> Mangler<'a> {
143147
}
144148

145149
pub fn mark_uniqueness_group_non_mangable(&mut self, group: UniquenessGroupId) {
146-
for atom in mem::take(&mut self.uniqueness_groups[group].0) {
150+
for atom in self.uniqueness_groups[group].0.take_in(self.allocator) {
147151
self.mark_atom_non_mangable(atom);
148152
}
149153
}

crates/jsshaker/src/mangling/mangler.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
77

88
use crate::{
99
analyzer::Factory,
10+
define_box_bump_idx,
1011
dep::DepAtom,
1112
utils::box_bump::BoxBump,
1213
value::{LiteralValue, Value},
@@ -19,9 +20,10 @@ oxc_index::define_index_type! {
1920
DISABLE_MAX_INDEX_CHECK = cfg!(not(debug_assertions));
2021
}
2122

22-
oxc_index::define_index_type! {
23-
pub struct UniquenessGroupId = u32;
24-
DISABLE_MAX_INDEX_CHECK = cfg!(not(debug_assertions));
23+
type UniquenessGroup<'a> = (Vec<MangleAtom>, usize, FxHashSet<&'a str>);
24+
25+
define_box_bump_idx! {
26+
pub struct UniquenessGroupId for UniquenessGroup<'static>;
2527
}
2628

2729
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -37,8 +39,11 @@ pub enum AtomState<'a> {
3739
Builtin,
3840
}
3941

40-
type UniquenessGroups<'a> =
41-
IndexVec<UniquenessGroupId, (Vec<MangleAtom>, usize, FxHashSet<&'a str>)>;
42+
type UniquenessGroups<'a> = BoxBump<
43+
'a,
44+
UniquenessGroupId,
45+
(allocator::Vec<'a, MangleAtom>, usize, allocator::HashSet<'a, &'a str>),
46+
>;
4247

4348
pub struct Mangler<'a> {
4449
pub enabled: bool,
@@ -47,6 +52,8 @@ pub struct Mangler<'a> {
4752

4853
pub states: BoxBump<'a, MangleAtom, AtomState<'a>>,
4954
pub constant_nodes: FxHashMap<DepAtom, (Option<MangleAtom>, Value<'a>)>,
55+
pub object_groups: FxHashMap<DepAtom, UniquenessGroupId>,
56+
pub prototype_groups: FxHashMap<DepAtom, UniquenessGroupId>,
5057

5158
/// (atoms, resolved_name)[]
5259
pub identity_groups: IndexVec<IdentityGroupId, (Vec<MangleAtom>, Option<&'a str>)>,
@@ -63,8 +70,10 @@ impl<'a> Mangler<'a> {
6370
allocator,
6471
states,
6572
constant_nodes: FxHashMap::default(),
73+
object_groups: FxHashMap::default(),
74+
prototype_groups: FxHashMap::default(),
6675
identity_groups: IndexVec::new(),
67-
uniqueness_groups: IndexVec::new(),
76+
uniqueness_groups: BoxBump::new(allocator),
6877
}
6978
}
7079

@@ -83,6 +92,34 @@ impl<'a> Mangler<'a> {
8392
.1
8493
}
8594

95+
pub fn new_object_group(&mut self) -> UniquenessGroupId {
96+
self.uniqueness_groups.alloc((
97+
allocator::Vec::new_in(self.allocator),
98+
0,
99+
allocator::HashSet::new_in(self.allocator),
100+
))
101+
}
102+
103+
pub fn use_object_group(&mut self, node: impl Into<DepAtom>) -> UniquenessGroupId {
104+
*self.object_groups.entry(node.into()).or_insert_with(|| {
105+
self.uniqueness_groups.alloc((
106+
allocator::Vec::new_in(self.allocator),
107+
0,
108+
allocator::HashSet::new_in(self.allocator),
109+
))
110+
})
111+
}
112+
113+
pub fn use_prototype_group(&mut self, node: impl Into<DepAtom>) -> UniquenessGroupId {
114+
*self.prototype_groups.entry(node.into()).or_insert_with(|| {
115+
self.uniqueness_groups.alloc((
116+
allocator::Vec::new_in(self.allocator),
117+
0,
118+
allocator::HashSet::new_in(self.allocator),
119+
))
120+
})
121+
}
122+
86123
pub fn new_atom(&self, str: &'a str) -> MangleAtom {
87124
self.states.alloc(AtomState::Constrained(str, None, allocator::HashSet::new_in(self.allocator)))
88125
}

crates/jsshaker/src/nodes/expr/arrow_function_expression.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl<'a> Analyzer<'a> {
1717
&mut self,
1818
node: &'a ArrowFunctionExpression<'a>,
1919
) -> Entity<'a> {
20-
self.new_function(CalleeNode::ArrowFunctionExpression(node)).into()
20+
self.new_function(CalleeNode::ArrowFunctionExpression(node), false).0.into()
2121
}
2222

2323
pub fn call_arrow_function_expression(

crates/jsshaker/src/nodes/expr/call_expression.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ impl<'a> Transformer<'a> {
9494
return Err(callee);
9595
}
9696

97-
let need_call = need_val || self.is_included(dep_id) || callee.is_super();
97+
let need_call = need_val
98+
|| self.is_included(dep_id)
99+
|| (callee.is_super() && self.has_super_class.borrow().last().copied().unwrap_or(false));
98100

99101
if !need_call {
100102
let callee = self.transform_expression_in_chain(callee, need_optional)?;

crates/jsshaker/src/nodes/misc/class.rs

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::cell::RefCell;
2+
13
use oxc::{
2-
allocator::{self, Allocator},
4+
allocator,
35
ast::{
46
NONE,
57
ast::{
6-
Class, ClassBody, ClassElement, ClassType, MethodDefinition, MethodDefinitionKind,
7-
PropertyDefinitionType, PropertyKind, StaticBlock,
8+
Class, ClassBody, ClassElement, ClassType, MethodDefinitionKind, PropertyDefinitionType,
9+
PropertyKind, StaticBlock,
810
},
911
},
1012
span::GetSpan,
@@ -16,35 +18,30 @@ use crate::{
1618
dep::DepAtom,
1719
entity::Entity,
1820
transformer::Transformer,
19-
utils::{CalleeNode, DefaultIn},
21+
utils::{CalleeNode, ClassData},
2022
value::{ObjectPrototype, cache::FnCacheTrackingData, call::FnCallInfo},
2123
};
2224

23-
struct Data<'a> {
24-
pub value: Option<Entity<'a>>,
25-
pub constructor: Option<&'a MethodDefinition<'a>>,
26-
pub keys: allocator::Vec<'a, Option<Entity<'a>>>,
27-
pub super_class: Option<Entity<'a>>,
28-
}
29-
30-
impl<'a> DefaultIn<'a> for Data<'a> {
31-
fn default_in(allocator: &'a Allocator) -> Self {
32-
Data {
33-
value: None,
25+
impl<'a> Analyzer<'a> {
26+
pub fn exec_class(&mut self, node: &'a Class<'a>) -> Entity<'a> {
27+
let data_cell = self.allocator.alloc(RefCell::new(ClassData {
28+
initializing: true,
29+
included: false,
3430
constructor: None,
35-
keys: allocator::Vec::new_in(allocator),
31+
keys: self.factory.vec(),
3632
super_class: None,
37-
}
38-
}
39-
}
33+
}));
4034

41-
impl<'a> Analyzer<'a> {
42-
pub fn exec_class(&mut self, node: &'a Class<'a>) -> Entity<'a> {
43-
let data = self.load_data::<Data>(AstKind2::Class(node));
44-
let (class, prototype) = self.new_function_with_prototype(CalleeNode::ClassConstructor(node));
35+
let (class, prototype) = self.new_function(CalleeNode::ClassConstructor(node, data_cell), true);
36+
let prototype = prototype.unwrap();
37+
let mut data = data_cell.borrow_mut();
38+
data.initializing = false;
4539

4640
// 1. Execute super class
47-
data.super_class = node.super_class.as_ref().map(|node| self.exec_expression(node));
41+
data.super_class = node
42+
.super_class
43+
.as_ref()
44+
.map(|node| self.factory.computed(self.exec_expression(node), AstKind2::SuperExpr(node)));
4845
if let Some(super_class) = &data.super_class {
4946
// Because we can't re-define the "prototype" property, this should be side-effect free
5047
if let Some((prototype_dep, super_statics, super_prototype)) =
@@ -80,6 +77,8 @@ impl<'a> Analyzer<'a> {
8077
data.constructor = Some(method);
8178
}
8279
}
80+
drop(data);
81+
let data = data_cell.borrow();
8382

8483
// 3. Register methods
8584
for (key, element) in data.keys.iter().zip(node.body.body.iter()) {
@@ -155,14 +154,17 @@ impl<'a> Analyzer<'a> {
155154

156155
// 5. Execute class decorators (ES2025 Stage 3)
157156
let class = class.into();
158-
let final_class = if !node.decorators.is_empty() {
157+
let class = if !node.decorators.is_empty() {
159158
self.exec_decorators(&node.decorators, class)
160159
} else {
161160
class
162161
};
163162

164-
data.value = Some(final_class);
165-
final_class
163+
if data.included {
164+
self.include(class);
165+
}
166+
167+
class
166168
}
167169

168170
pub fn declare_class(&mut self, node: &'a Class<'a>, exporting: Option<DepAtom>) {
@@ -180,10 +182,18 @@ impl<'a> Analyzer<'a> {
180182
pub fn call_class_constructor(
181183
&mut self,
182184
node: &'a Class<'a>,
185+
data_cell: &RefCell<ClassData<'a>>,
183186
info: FnCallInfo<'a>,
184187
) -> (Entity<'a>, FnCacheTrackingData<'a>) {
188+
let data = data_cell.borrow();
189+
if info.include && data.initializing {
190+
drop(data);
191+
let mut data = data_cell.borrow_mut();
192+
data.included = true;
193+
return (self.factory.undefined, FnCacheTrackingData::worst_case());
194+
}
195+
185196
let factory = self.factory;
186-
let data = self.load_data::<Data>(AstKind2::Class(node));
187197

188198
self.push_call_scope(info, false, false);
189199
let super_class = data.super_class.unwrap_or(self.factory.undefined);
@@ -195,7 +205,11 @@ impl<'a> Analyzer<'a> {
195205

196206
if let Some(id) = &node.id {
197207
self.declare_binding_identifier(id, None, DeclarationKind::NamedFunctionInBody);
198-
self.init_binding_identifier(id, DeclarationKind::NamedFunctionInBody, data.value);
208+
self.init_binding_identifier(
209+
id,
210+
DeclarationKind::NamedFunctionInBody,
211+
Some(info.func.into()),
212+
);
199213
}
200214

201215
// 1. Init properties
@@ -256,8 +270,11 @@ impl<'a> Transformer<'a> {
256270
transformed_id
257271
};
258272

259-
let super_class = super_class.as_ref().and_then(|node| self.transform_expression(node, true));
273+
let super_class = super_class.as_ref().and_then(|node| {
274+
self.transform_expression(node, self.is_included(AstKind2::SuperExpr(node)))
275+
});
260276

277+
self.has_super_class.borrow_mut().push(super_class.is_some());
261278
let body = {
262279
let ClassBody { span, body } = body.as_ref();
263280

@@ -298,6 +315,7 @@ impl<'a> Transformer<'a> {
298315

299316
self.ast.class_body(*span, transformed_body)
300317
};
318+
self.has_super_class.borrow_mut().pop();
301319

302320
let decorators = self.transform_decorators(&node.decorators);
303321

@@ -335,6 +353,7 @@ impl<'a> Transformer<'a> {
335353
}
336354
}
337355

356+
self.has_super_class.borrow_mut().push(false);
338357
for element in &body.body {
339358
match element {
340359
ClassElement::StaticBlock(node) => {
@@ -354,6 +373,7 @@ impl<'a> Transformer<'a> {
354373
_ => {}
355374
}
356375
}
376+
self.has_super_class.borrow_mut().pop();
357377

358378
if statements.is_empty() {
359379
None

crates/jsshaker/src/nodes/misc/function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ impl<'a> Analyzer<'a> {
2121
if self.has_no_shake_notation(node.span) {
2222
return self.factory.computed_unknown(AstKind2::FunctionNoShake(node));
2323
}
24-
self.new_function(CalleeNode::Function(node)).into()
24+
self.new_function(CalleeNode::Function(node), true).0.into()
2525
}
2626

2727
pub fn declare_function(

crates/jsshaker/src/transformer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub struct Transformer<'a> {
4343
pub need_unused_assignment_target: Cell<bool>,
4444
pub need_non_nullish_helper: Cell<bool>,
4545
pub unused_identifier_names: RefCell<FxHashMap<u64, usize>>,
46+
pub has_super_class: RefCell<Vec<bool>>,
4647
}
4748

4849
impl<'a> Transformer<'a> {
@@ -75,6 +76,7 @@ impl<'a> Transformer<'a> {
7576
need_unused_assignment_target: Cell::new(false),
7677
need_non_nullish_helper: Cell::new(false),
7778
unused_identifier_names: Default::default(),
79+
has_super_class: Default::default(),
7880
}
7981
}
8082

0 commit comments

Comments
 (0)