Skip to content

Commit e817278

Browse files
committed
[core][rewriter] dpsc: properly handle assignment destructuring
1 parent 6960da8 commit e817278

File tree

3 files changed

+117
-26
lines changed

3 files changed

+117
-26
lines changed

rewriter/js/src/changes.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub enum JsChangeType<'alloc: 'data, 'data> {
4040
RewriteProperty {
4141
ident: Atom<'data>,
4242
},
43+
RebindProperty {
44+
ident: Atom<'data>,
45+
},
4346

4447
/// insert `${cfg.setrealmfn}({}).`
4548
SetRealmFn,
@@ -106,7 +109,6 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> {
106109
(cfg, flags): &Self::ToLowLevelData,
107110
offset: i32,
108111
) -> TransformLL<'data> {
109-
dbg!(&&self);
110112
use JsChangeType as Ty;
111113
use TransformLL as LL;
112114
match self.ty {
@@ -123,6 +125,9 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> {
123125
Ty::WrapPropertyLeft => LL::insert(transforms![&cfg.wrappropertyfn, "(("]),
124126
Ty::WrapPropertyRight => LL::insert(transforms!["))"]),
125127
Ty::RewriteProperty { ident } => LL::replace(transforms![&cfg.wrappropertybase, ident]),
128+
Ty::RebindProperty { ident } => {
129+
LL::replace(transforms![&cfg.wrappropertybase, ident, ":", ident])
130+
}
126131

127132
Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]),
128133
Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]),

rewriter/js/src/rewrite.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> {
2727
/// `cfg.metafn("cfg.base")`
2828
MetaFn,
2929

30+
/// `location` -> `$sj_location`
3031
RewriteProperty {
3132
ident: Atom<'data>,
3233
},
34+
35+
/// `location` -> `$sj_location: location`
36+
RebindProperty {
37+
ident: Atom<'data>,
38+
},
39+
40+
/// `cfg.wrapprop({})`
3341
WrapProperty,
3442

3543
// dead code only if debug is disabled
@@ -107,6 +115,7 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> {
107115
change!(span!(end), WrapFnRight { enclose }),
108116
],
109117
Self::RewriteProperty { ident } => smallvec![change!(span, RewriteProperty { ident }),],
118+
Self::RebindProperty { ident } => smallvec![change!(span, RebindProperty { ident })],
110119
Self::WrapProperty => smallvec![
111120
change!(span!(start), WrapPropertyLeft),
112121
change!(span!(end), WrapPropertyRight),

rewriter/js/src/visitor.rs

Lines changed: 102 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use std::error::Error;
33
use oxc::{
44
allocator::{Allocator, StringBuilder},
55
ast::ast::{
6-
AssignmentExpression, AssignmentTarget, BindingPattern, CallExpression,
7-
ComputedMemberExpression, DebuggerStatement, ExportAllDeclaration, ExportNamedDeclaration,
8-
Expression, FunctionBody, IdentifierReference, ImportDeclaration, ImportExpression,
9-
MemberExpression, MetaProperty, NewExpression, ObjectExpression, ObjectPattern,
10-
ObjectPropertyKind, ReturnStatement, SimpleAssignmentTarget, StringLiteral, ThisExpression,
6+
AssignmentExpression, AssignmentTarget, AssignmentTargetMaybeDefault,
7+
AssignmentTargetProperty, BindingPattern, CallExpression, ComputedMemberExpression,
8+
DebuggerStatement, ExportAllDeclaration, ExportNamedDeclaration, Expression, FunctionBody,
9+
IdentifierReference, ImportDeclaration, ImportExpression, MemberExpression, MetaProperty,
10+
NewExpression, ObjectExpression, ObjectPattern, ObjectPropertyKind, PrivateIdentifier,
11+
PropertyKey, ReturnStatement, SimpleAssignmentTarget, StringLiteral, ThisExpression,
1112
UnaryExpression, UnaryOperator, UpdateExpression,
1213
},
1314
ast_visit::{Visit, walk},
@@ -327,24 +328,8 @@ where
327328
}
328329
}
329330

330-
fn visit_assignment_expression(&mut self, it: &AssignmentExpression<'data>) {
331-
match &it.left {
332-
AssignmentTarget::AssignmentTargetIdentifier(s) => {
333-
if ["location"].contains(&s.name.to_string().as_str()) {
334-
self.jschanges.add(rewrite!(
335-
it.span,
336-
Assignment {
337-
name: s.name,
338-
rhs: it.right.span(),
339-
op: it.operator,
340-
}
341-
));
342-
343-
// avoid walking rest of tree, i would need to figure out nested rewrites
344-
// somehow
345-
return;
346-
}
347-
}
331+
fn visit_assignment_target(&mut self, it: &AssignmentTarget<'data>) {
332+
match &it {
348333
AssignmentTarget::StaticMemberExpression(s) => {
349334
if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) {
350335
self.jschanges.add(rewrite!(
@@ -363,6 +348,73 @@ where
363348
walk::walk_expression(self, &s.object);
364349
walk::walk_expression(self, &s.expression);
365350
}
351+
AssignmentTarget::ObjectAssignmentTarget(s) => {
352+
if s.rest.is_some() {
353+
// infeasible to rewrite :(
354+
eprintln!("cannot rewrite rest parameters");
355+
return;
356+
}
357+
for prop in &s.properties {
358+
match prop {
359+
AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(p) => {
360+
// { location } = self;
361+
// correct thing to do here is to change it into an AsignmentTargetPropertyProperty
362+
// { $sj_location: location } = self;
363+
if UNSAFE_GLOBALS.contains(&p.binding.name.to_string().as_str()) {
364+
self.jschanges.add(rewrite!(
365+
p.binding.span(),
366+
RebindProperty {
367+
ident: p.binding.name.clone()
368+
}
369+
));
370+
}
371+
}
372+
AssignmentTargetProperty::AssignmentTargetPropertyProperty(p) => {
373+
// { location: x } = self;
374+
// { location: x = "..."} = self;
375+
// { location: { href } } = self;
376+
// { location: { href: x } } = self;
377+
// { ["location"]: x } = self;
378+
379+
match &p.name {
380+
PropertyKey::StaticIdentifier(id) => {
381+
// { location: x } = self;
382+
if UNSAFE_GLOBALS.contains(&id.name.to_string().as_str()) {
383+
self.jschanges.add(rewrite!(
384+
p.name.span(),
385+
RewriteProperty { ident: id.name }
386+
));
387+
}
388+
}
389+
PropertyKey::PrivateIdentifier(_) => {
390+
// doesn't matter
391+
}
392+
// (expression variant)
393+
_ => {
394+
// { ["location"]: x } = self;
395+
396+
// TODO: check literals
397+
self.jschanges.add(rewrite!(p.name.span(), WrapProperty));
398+
}
399+
}
400+
401+
match &p.binding {
402+
AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(d) => {
403+
// { location: x = parent } = {};
404+
// { location = parent } = {};
405+
406+
// we still need to rewrite whatever stuff might be in the default expression
407+
walk::walk_expression(self, &d.init);
408+
}
409+
_ => {
410+
// { location: { href } } = {};
411+
walk::walk_assignment_target_maybe_default(self, &p.binding);
412+
}
413+
}
414+
}
415+
}
416+
}
417+
}
366418
AssignmentTarget::ArrayAssignmentTarget(_) => {
367419
// [location] = ["https://example.com"]
368420
// this is such a ridiculously specific edge case. just ignore it
@@ -371,9 +423,34 @@ where
371423
_ => {
372424
// only walk the left side if it isn't an identifier, we can't replace the
373425
// identifier with a function obviously
374-
walk::walk_assignment_target(self, &it.left);
426+
walk::walk_assignment_target(self, &it);
427+
}
428+
}
429+
}
430+
431+
fn visit_assignment_expression(&mut self, it: &AssignmentExpression<'data>) {
432+
// location = "https://example.com"
433+
match &it.left {
434+
AssignmentTarget::AssignmentTargetIdentifier(s) => {
435+
if ["location"].contains(&s.name.to_string().as_str()) {
436+
self.jschanges.add(rewrite!(
437+
it.span,
438+
Assignment {
439+
name: s.name,
440+
rhs: it.right.span(),
441+
op: it.operator,
442+
}
443+
));
444+
445+
// avoid walking rest of tree, i would need to figure out nested rewrites
446+
// somehow
447+
return;
448+
}
449+
}
450+
_ => {
451+
// gets handled by visit_assignment_target
375452
}
376453
}
377-
walk::walk_expression(self, &it.right);
454+
walk::walk_assignment_expression(self, it);
378455
}
379456
}

0 commit comments

Comments
 (0)