Index: third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java |
diff --git a/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java b/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java |
index 9e2ef3f62fded3616a48061a3e28eca8ac45c9ef..436fabae734380ebfca2e21feb0400023cc9afd2 100644 |
--- a/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java |
+++ b/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java |
@@ -12,9 +12,12 @@ import com.google.javascript.rhino.Node; |
import com.google.javascript.rhino.Token; |
import java.util.ArrayList; |
+import java.util.Arrays; |
import java.util.HashMap; |
+import java.util.HashSet; |
import java.util.List; |
import java.util.Map; |
+import java.util.Set; |
/** |
* Compiler pass for Chrome-specific needs. It handles the following Chrome JS features: |
@@ -28,10 +31,11 @@ import java.util.Map; |
public class ChromePass extends AbstractPostOrderCallback implements CompilerPass { |
final AbstractCompiler compiler; |
- private static final String CR_DEFINE = "cr.define"; |
+ private Set<String> createdObjects; |
+ private static final String CR_DEFINE = "cr.define"; |
+ private static final String CR_EXPORT_PATH = "cr.exportPath"; |
private static final String OBJECT_DEFINE_PROPERTY = "Object.defineProperty"; |
- |
private static final String CR_DEFINE_PROPERTY = "cr.defineProperty"; |
private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be called like this:" |
@@ -41,6 +45,10 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", |
"cr.define() should have exactly 2 arguments. " + CR_DEFINE_COMMON_EXPLANATION); |
+ static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS = |
+ DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS", |
+ "cr.exportPath() should have exactly 1 argument: namespace name."); |
+ |
static final DiagnosticType CR_DEFINE_INVALID_FIRST_ARGUMENT = |
DiagnosticType.error("JSC_CR_DEFINE_INVALID_FIRST_ARGUMENT", |
"Invalid first argument for cr.define(). " + CR_DEFINE_COMMON_EXPLANATION); |
@@ -61,6 +69,8 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
public ChromePass(AbstractCompiler compiler) { |
this.compiler = compiler; |
+ // The global variable "cr" is declared in ui/webui/resources/js/cr.js. |
+ this.createdObjects = new HashSet<>(Arrays.asList("cr")); |
} |
@Override |
@@ -75,6 +85,9 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
if (callee.matchesQualifiedName(CR_DEFINE)) { |
visitNamespaceDefinition(node, parent); |
compiler.reportCodeChange(); |
+ } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { |
+ visitExportPath(node, parent); |
+ compiler.reportCodeChange(); |
} else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || |
callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { |
visitPropertyDefinition(node, parent); |
@@ -127,6 +140,24 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
target.setJSDocInfo(builder.build(target)); |
} |
+ private void visitExportPath(Node crExportPathNode, Node parent) { |
+ if (crExportPathNode.getChildCount() != 2) { |
+ compiler.report(JSError.make(crExportPathNode, |
+ CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); |
+ return; |
+ } |
+ |
+ createAndInsertObjectsForQualifiedName(parent, |
+ crExportPathNode.getChildAtIndex(1).getString()); |
+ } |
+ |
+ private void createAndInsertObjectsForQualifiedName(Node scriptChild, String namespace) { |
+ List<Node> objectsForQualifiedName = createObjectsForQualifiedName(namespace); |
+ for (Node n : objectsForQualifiedName) { |
+ scriptChild.getParent().addChildBefore(n, scriptChild); |
+ } |
+ } |
+ |
private void visitNamespaceDefinition(Node crDefineCallNode, Node parent) { |
if (crDefineCallNode.getChildCount() != 3) { |
compiler.report(JSError.make(crDefineCallNode, CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS)); |
@@ -144,10 +175,7 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
// identifiers. |
String namespace = namespaceArg.getString(); |
- List<Node> objectsForQualifiedName = createObjectsForQualifiedName(namespace); |
- for (Node n : objectsForQualifiedName) { |
- parent.getParent().addChildBefore(n, parent); |
- } |
+ createAndInsertObjectsForQualifiedName(parent, namespace); |
if (!function.isFunction()) { |
compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_SECOND_ARGUMENT)); |
@@ -198,20 +226,26 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
List<Node> objects = new ArrayList<>(); |
String[] parts = namespace.split("\\."); |
- objects.add(createJsNode("var " + parts[0] + " = " + parts[0] + " || {};")); |
+ createObjectIfNew(objects, parts[0], true); |
if (parts.length >= 2) { |
StringBuilder currPrefix = new StringBuilder().append(parts[0]); |
for (int i = 1; i < parts.length; ++i) { |
currPrefix.append(".").append(parts[i]); |
- String code = currPrefix + " = " + currPrefix + " || {};"; |
- objects.add(createJsNode(code)); |
+ createObjectIfNew(objects, currPrefix.toString(), false); |
} |
} |
return objects; |
} |
+ private void createObjectIfNew(List<Node> objects, String name, boolean needVar) { |
+ if (!createdObjects.contains(name)) { |
+ objects.add(createJsNode((needVar ? "var " : "") + name + " = " + name + " || {};")); |
+ createdObjects.add(name); |
+ } |
+ } |
+ |
private Node createJsNode(String code) { |
// The parent node after parseSyntheticCode() is SCRIPT node, we need to get rid of it. |
return compiler.parseSyntheticCode(code).removeFirstChild(); |