Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve dead code removal for object's empty methods #9756

Open
yf-yang opened this issue Nov 23, 2024 · 2 comments
Open

Improve dead code removal for object's empty methods #9756

yf-yang opened this issue Nov 23, 2024 · 2 comments
Assignees

Comments

@yf-yang
Copy link

yf-yang commented Nov 23, 2024

Describe the feature

const logger = { 
  info: () => {}, 
  log: x => console.log(x), 
  warn: x => console.log(x),
};
logger.info('abc');
// logger.log('def');
// logger.info('aaa');

playground link

When only one logger.log exists, in 1 pass, the code would be compiled to a chaining method call and blocks further dead code detection. When uncomment the second / the last two lines, 1 pass compiled codes are different and lead to different multi pass compiled code.

Terser could work more stable in this scenario. You can copy the code to https://try.terser.org/ and configure compress.passes to 1/2/3 to see how it is different from SWC.

Not a bug, but I hope it could be improved, so that I can remove lots of strings logged by logger in out app with SWC's minimizer.

Thank you!

Babel plugin or link to the feature description

No response

Additional context

No response

@CPunisher CPunisher self-assigned this Nov 25, 2024
@CPunisher
Copy link
Contributor

CPunisher commented Nov 26, 2024

Investigation:
Currently this optimization is prevented by the following code, where self.ctx.is_callee is true.

if self.ctx.is_update_arg || self.ctx.is_callee || self.ctx.is_exact_lhs_of_assign {

We can find the corresponding check in terser at: https://github.com/terser/terser/blob/3167d34557d9bbf6394b468605169e7f9648594a/lib/compress/index.js#L3716C66-L3716C79
where the restriction is released with !has_annotation(parent, _NOINLINE) (which is usually true).

We can make it by add a new is_noline context. However, to keep the this pointer in the inlined function correct, we also need to add a safety check: https://github.com/terser/terser/blob/3167d34557d9bbf6394b468605169e7f9648594a/lib/compress/index.js#L3475

The only problem is that when the value of the property is Ident(AST_SymbolRef), we have not collected the fixed expr of all idents but those to be inlined. So for now, by the above, we can only optimize ({ f: (x) => console.log(x) }).f(1) to console.log(1), but we can't optimize:

const f = (x) => console.log(x);
console.log(f); // another reference that prevents minifier inlining f
({ f: f }).f(1);

@yf-yang
Copy link
Author

yf-yang commented Nov 30, 2024

Thank you for your investigation. I don't quite understand "the only problem" you are talking about, could

const g = (x) => console.log(x);
({ f: g }).f(1);

and

const f = (x) => console.log(x);
({ f: f }).f(1);

be compressed?

IMHO it is perfectly fine that it could not be compressed if the function is referenced somewhere else. That's just a corner case.

And, without considering those corner cases, I hope it could deal with simpler cases like:

const logger = { 
  info: () => {}, 
  log: () => {}, 
  warn: () => {}, 
};
logger.info('abc');
logger.info('abc');
logger.log('def');
logger.warn('fgh');

and

const logger = { 
  info: () => {}, 
  log: () => {}, 
  warn: () => {}, 
};
logger.info('abc');

Those cases are not working for the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants