-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
add custom closure compiler pass #2593
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<classpath> | ||
<classpathentry including="**/*.java" kind="src" path="src"/> | ||
<classpathentry including="**/*.java" kind="src" output="build/test-classes" path="test"/> | ||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> | ||
<classpathentry kind="lib" path="lib/compiler.jar" sourcepath="/Users/erwinm/dev/java/closure-compiler"/> | ||
<classpathentry kind="lib" path="./../../third_party/closure-compiler/compiler.jar"/> | ||
<classpathentry kind="lib" path="./../../third_party/closure-compiler/compiler-and-tests.jar"/> | ||
<classpathentry kind="output" path="build/classes"/> | ||
</classpath> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<module type="JAVA_MODULE" version="4"> | ||
<component name="NewModuleRootManager" inherit-compiler-output="true"> | ||
<exclude-output /> | ||
<content url="file://$MODULE_DIR$"> | ||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" /> | ||
</content> | ||
<orderEntry type="inheritedJdk" /> | ||
<orderEntry type="sourceFolder" forTests="false" /> | ||
</component> | ||
</module> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package org.ampproject; | ||
|
||
import java.util.Set; | ||
|
||
import com.google.javascript.jscomp.AbstractCompiler; | ||
import com.google.javascript.jscomp.HotSwapCompilerPass; | ||
import com.google.javascript.jscomp.NodeTraversal; | ||
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; | ||
import com.google.javascript.rhino.Node; | ||
|
||
/** | ||
* Does a `stripTypeSuffix` which currently can't be done through | ||
* the normal `strip` mechanisms provided by closure compiler. | ||
* Some of the known mechanisms we tried before writing our own compiler pass | ||
* are setStripTypes, setStripTypePrefixes, setStripNameSuffixes, setStripNamePrefixes. | ||
* The normal mechanisms found in closure compiler can't strip the expressions we want because | ||
* they are either prefix based and/or operate on the es6 translated code which would mean they | ||
* operate on a qualifier string name that looks like | ||
* "module$__$__$__$extensions$amp_test$0_1$log.dev.fine". | ||
* | ||
* Other custom pass examples found inside closure compiler src: | ||
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PolymerPass.java | ||
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/AngularPass.java | ||
*/ | ||
class AmpPass extends AbstractPostOrderCallback implements HotSwapCompilerPass { | ||
|
||
final AbstractCompiler compiler; | ||
private final Set<String> stripTypeSuffixes; | ||
|
||
public AmpPass(AbstractCompiler compiler, Set<String> stripTypeSuffixes) { | ||
this.compiler = compiler; | ||
this.stripTypeSuffixes = stripTypeSuffixes; | ||
} | ||
|
||
@Override public void process(Node externs, Node root) { | ||
hotSwapScript(root, null); | ||
} | ||
|
||
@Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { | ||
NodeTraversal.traverseEs6(compiler, scriptRoot, this); | ||
} | ||
|
||
@Override public void visit(NodeTraversal t, Node n, Node parent) { | ||
if (isDevAssertCall(n)) { | ||
maybeEliminateCallExceptFirstParam(n, parent); | ||
} else if (n.isExprResult()) { | ||
maybeEliminateExpressionBySuffixName(n, parent); | ||
} | ||
} | ||
|
||
private boolean isDevAssertCall(Node n) { | ||
if (n.isCall()) { | ||
Node expression = n.getFirstChild(); | ||
if (expression == null) { | ||
return false; | ||
} | ||
|
||
String name = expression.getQualifiedName(); | ||
if (name == null) { | ||
return false; | ||
} | ||
|
||
if (name.endsWith("dev.assert")) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* Checks if expression is a GETPROP() (method invocation) and the property | ||
* name ends with one of the items in stripTypeSuffixes. | ||
*/ | ||
private void maybeEliminateExpressionBySuffixName(Node n, Node parent) { | ||
// n = EXPRESSION_RESULT > CALL > GETPROP | ||
Node call = n.getFirstChild(); | ||
if (call == null) { | ||
return; | ||
} | ||
Node expression = call.getFirstChild(); | ||
if (expression == null) { | ||
return; | ||
} | ||
|
||
if (qualifiedNameEndsWithStripType(expression)) { | ||
if (parent.isExprResult()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You currently just kill the full statement, right? I wonder if, for asserts, you should just alway copy the first argument here. It would be DCEed if it is side effect free. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. right. should we just go ahead and change that for all typeSuffixes we look for or specifically just for assert? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just for assert. We should just completely kill logging. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
Node grandparent = parent.getParent(); | ||
grandparent.removeChild(parent); | ||
} else { | ||
parent.removeChild(n); | ||
} | ||
compiler.reportCodeChange(); | ||
} | ||
} | ||
|
||
private void maybeEliminateCallExceptFirstParam(Node n, Node p) { | ||
Node functionName = n.getFirstChild(); | ||
if (functionName == null) { | ||
return; | ||
} | ||
Node firstArg = functionName.getNext(); | ||
if (firstArg == null) { | ||
return; | ||
} | ||
firstArg.detachFromParent(); | ||
p.replaceChild(n, firstArg); | ||
compiler.reportCodeChange(); | ||
} | ||
|
||
/** | ||
* Checks the nodes qualified name if it ends with one of the items in | ||
* stripTypeSuffixes | ||
*/ | ||
boolean qualifiedNameEndsWithStripType(Node n) { | ||
String name = n.getQualifiedName(); | ||
return qualifiedNameEndsWithStripType(name); | ||
} | ||
|
||
/** | ||
* Checks if the string ends with one of the items in stripTypeSuffixes | ||
*/ | ||
boolean qualifiedNameEndsWithStripType(String name) { | ||
if (name != null) { | ||
for (String suffix : stripTypeSuffixes) { | ||
if (name.endsWith(suffix)) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great comment.