google / closure-compiler

A JavaScript checker and optimizer.
https://developers.google.com/closure/compiler/
Apache License 2.0
7.35k stars 1.14k forks source link

Static methods lost using ES6 modules #1776

Open supersteves opened 8 years ago

supersteves commented 8 years ago

In advanced obfuscation, the method "something" is removed from the compiled output.

class A {
  method() { console.log("instance method"); }
  static something() {
    console.log("something static");
    new A().method();
  }
}
export default A;
import A from "A"
A.something(); // fails at runtime, [obfuscated name] is not a function

I'm guessing this is specific to ES6 modules, since I use classes and static methods with goog.module successfully.

I tried to set up a debugger but don't know how ES6 module paths work there: https://closure-compiler-debugger.appspot.com/#input0%3Dclass%2520A%2520%257B%250A%250A%2520%2520method()%2520%257B%250A%2520%2520%2520%2520console.log(%2522instance%2520method%2522)%253B%250A%2520%2520%257D%250A%250A%2520%2520static%2520something()%2520%257B%250A%2520%2520%2520%2520console.log(%2522something%2520static%2522)%253B%250A%2520%2520%2520%2520new%2520A().method()%253B%250A%2520%2520%257D%250A%257D%250A%250Aexport%2520default%2520A%253B%250A%26input1%3Dimport%2520A%2520from%2520%2522A%2522%250A%250AA.something()%253B%26conformanceConfig%26externs%26refasterjs-template%26includeDefaultExterns%3D1%26CHECK_SYMBOLS%3D1%26CHECK_TYPES%3D1%26CLOSURE_PASS%3D1%26LANG_IN_IS_ES6%3D1%26MISSING_PROPERTIES%3D1%26PRESERVE_TYPE_ANNOTATIONS%3D1%26PRETTY_PRINT%3D1%26REMOVE_DEAD_CODE%3D1%26REMOVE_UNUSED_CLASS_PROPERTIES%3D1%26TRANSPILE%3D1

supersteves commented 8 years ago

same as https://github.com/google/closure-compiler/issues/1693?

supersteves commented 8 years ago

related to https://github.com/google/closure-compiler/issues/370?

supersteves commented 8 years ago

Yep, CompilerOptions.collapseProperties=true was the culprit. I think I want collapse properties for my large codebase, and it seems it is missing the collapse at the point of use and therefore pruning as unused. So probably a bug. Any suggestions to work around it?

supersteves commented 8 years ago

In my case both A and B are ES6 modules.

I've tried converting just A or just B to a goog.module, retaining an ES6 module pattern for the other; same problem.

I've tried A and B both as goog.module, it fixes it. So I have a workaround - avoid ES6 modules for both the declaring and importing modules if you have a static class property and collapseProperties enabled.

(In case it's relevant: in my real-world case, the static method had an an arg, and all calls it had the same expression, and in the compiled output the arg had been removed and inlined into the method. Don't see a problem with this but it might shed light.)

ChadKillingsworth commented 8 years ago

How are you using A.something? If it's referenced in the same compiled code, it should be renamed consistently. If you are using it from an external reference, you'll need to use @nocollapse.

supersteves commented 8 years ago

In the same compiled code, a single run of the compiler. It works with goog.modules, it fails with either being ES6 modules. @nocollapse is a much better workaround, and works - thanks.

supersteves commented 8 years ago

Let me know how to set up the debugger to use ES6 modules, and I'll create a test case. Do you have any example debugger URLs I can look at?

ChadKillingsworth commented 8 years ago

I don't have an example for the debugger.

supersteves commented 8 years ago

Just updated to compiler tip after running into this again (still an issue). It seems that all static functions in an ES6 class within an ES6 module, are being collapsed+renamed inconsistently where accessed outside of the module. If anyone can suggest how to create a live test case for this, I'll set one up - can't see how to recreate a multi- ES6 modules scenario in the debugger or service.