fix(es/minifier): Do not drop properties used via this (#7785)

**Related issue:**

 - Closes #7783.
This commit is contained in:
Donny/강동윤 2023-08-10 11:48:35 +09:00 committed by GitHub
parent e90b555790
commit 552d9aa344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 1 deletions

View File

@ -0,0 +1,71 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
},
"target": "es2015",
"loose": false,
"minify": {
"compress": {
"arguments": false,
"arrows": true,
"booleans": true,
"booleans_as_integers": false,
"collapse_vars": true,
"comparisons": true,
"computed_props": true,
"conditionals": true,
"dead_code": true,
"directives": true,
"drop_console": false,
"drop_debugger": true,
"evaluate": true,
"expression": false,
"hoist_funs": false,
"hoist_props": true,
"hoist_vars": false,
"if_return": true,
"join_vars": true,
"keep_classnames": false,
"keep_fargs": true,
"keep_fnames": false,
"keep_infinity": false,
"loops": true,
"negate_iife": true,
"properties": true,
"reduce_funcs": false,
"reduce_vars": false,
"side_effects": true,
"switches": true,
"typeofs": true,
"unsafe": false,
"unsafe_arrows": false,
"unsafe_comps": false,
"unsafe_Function": false,
"unsafe_math": false,
"unsafe_symbols": false,
"unsafe_methods": false,
"unsafe_proto": false,
"unsafe_regexp": false,
"unsafe_undefined": false,
"unused": true,
"const_to_let": true,
"pristine_globals": true
},
"mangle": {
"toplevel": false,
"keep_classnames": false,
"keep_fnames": false,
"keep_private_props": false,
"ie8": false,
"safari10": false
}
}
},
"module": {
"type": "es6"
},
"minify": true,
"isModule": true
}

View File

@ -0,0 +1,14 @@
export default function Home() {
return (
<div>{foo.a}</div>
)
}
const foo = {
get a() {
return `a ${this.b}`;
},
get b() {
return `b`;
}
}

View File

@ -0,0 +1 @@
export default function e(){return React.createElement("div",null,foo.a)}let foo={get a(){return`a ${this.b}`},get b(){return"b"}};

View File

@ -28,6 +28,7 @@ function implicitThis(n) {
return this.m + n + 12;
}
var impl = {
a: 12,
explicitVoid2: function() {
return _this.a;
},

View File

@ -1,8 +1,10 @@
use rustc_hash::FxHashSet;
use swc_atoms::{js_word, JsWord};
use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_usage_analyzer::util::is_global_var_with_pure_property_access;
use swc_ecma_utils::{contains_ident_ref, ExprExt};
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
use super::Optimizer;
#[cfg(feature = "debug")]
@ -785,11 +787,17 @@ impl Optimizer<'_> {
return None;
}
let properties_used_via_this = {
let mut v = ThisPropertyVisitor::default();
obj.visit_with(&mut v);
v.properties
};
let should_preserve_property = |sym: &JsWord| {
if let "toString" = &**sym {
return true;
}
!usage.accessed_props.contains_key(sym)
!usage.accessed_props.contains_key(sym) && !properties_used_via_this.contains(sym)
};
let should_preserve = |key: &PropName| match key {
PropName::Ident(k) => should_preserve_property(&k.sym),
@ -824,3 +832,34 @@ impl Optimizer<'_> {
None
}
}
#[derive(Default)]
struct ThisPropertyVisitor {
properties: FxHashSet<JsWord>,
should_abort: bool,
}
impl Visit for ThisPropertyVisitor {
noop_visit_type!();
fn visit_member_expr(&mut self, e: &MemberExpr) {
if self.should_abort {
return;
}
e.visit_children_with(self);
if let Expr::This(..) = &*e.obj {
match &e.prop {
MemberProp::Ident(p) => {
self.properties.insert(p.sym.clone());
}
MemberProp::Computed(_) => {
self.should_abort = true;
}
_ => {}
}
}
}
}

View File

@ -0,0 +1,11 @@
export default function Home() {
return React.createElement("div", null, foo.a);
}
const foo = {
get a() {
return `a ${this.b}`;
},
get b() {
return `b`;
}
};

View File

@ -0,0 +1,11 @@
export default function Home() {
return React.createElement("div", null, foo.a);
}
const foo = {
get a () {
return `a ${this.b}`;
},
get b () {
return "b";
}
};