Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(191)

Side by Side Diff: third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java

Issue 453783006: Handle cr.exportPath() in compiler pass, declare every object only once (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@C_define_property
Patch Set: rebase Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package com.google.javascript.jscomp; 5 package com.google.javascript.jscomp;
6 6
7 import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; 7 import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
8 import com.google.javascript.rhino.IR; 8 import com.google.javascript.rhino.IR;
9 import com.google.javascript.rhino.JSDocInfoBuilder; 9 import com.google.javascript.rhino.JSDocInfoBuilder;
10 import com.google.javascript.rhino.JSTypeExpression; 10 import com.google.javascript.rhino.JSTypeExpression;
11 import com.google.javascript.rhino.Node; 11 import com.google.javascript.rhino.Node;
12 import com.google.javascript.rhino.Token; 12 import com.google.javascript.rhino.Token;
13 13
14 import java.util.ArrayList; 14 import java.util.ArrayList;
15 import java.util.Arrays;
15 import java.util.HashMap; 16 import java.util.HashMap;
17 import java.util.HashSet;
16 import java.util.List; 18 import java.util.List;
17 import java.util.Map; 19 import java.util.Map;
20 import java.util.Set;
18 21
19 /** 22 /**
20 * Compiler pass for Chrome-specific needs. It handles the following Chrome JS f eatures: 23 * Compiler pass for Chrome-specific needs. It handles the following Chrome JS f eatures:
21 * <ul> 24 * <ul>
22 * <li>namespace declaration using {@code cr.define()}, 25 * <li>namespace declaration using {@code cr.define()},
23 * <li>unquoted property declaration using {@code {cr|Object}.defineProperty()}. 26 * <li>unquoted property declaration using {@code {cr|Object}.defineProperty()}.
24 * </ul> 27 * </ul>
25 * 28 *
26 * <p>For the details, see tests inside ChromePassTest.java. 29 * <p>For the details, see tests inside ChromePassTest.java.
27 */ 30 */
28 public class ChromePass extends AbstractPostOrderCallback implements CompilerPas s { 31 public class ChromePass extends AbstractPostOrderCallback implements CompilerPas s {
29 final AbstractCompiler compiler; 32 final AbstractCompiler compiler;
30 33
34 private Set<String> createdObjects;
35
31 private static final String CR_DEFINE = "cr.define"; 36 private static final String CR_DEFINE = "cr.define";
32 37 private static final String CR_EXPORT_PATH = "cr.exportPath";
33 private static final String OBJECT_DEFINE_PROPERTY = "Object.defineProperty" ; 38 private static final String OBJECT_DEFINE_PROPERTY = "Object.defineProperty" ;
34
35 private static final String CR_DEFINE_PROPERTY = "cr.defineProperty"; 39 private static final String CR_DEFINE_PROPERTY = "cr.defineProperty";
36 40
37 private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be cal led like this:" 41 private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be cal led like this:"
38 + " cr.define('name.space', function() '{ ... return {Export: Intern al}; }');"; 42 + " cr.define('name.space', function() '{ ... return {Export: Intern al}; }');";
39 43
40 static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS = 44 static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS =
41 DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", 45 DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS",
42 "cr.define() should have exactly 2 arguments. " + CR_DEFINE_ COMMON_EXPLANATION); 46 "cr.define() should have exactly 2 arguments. " + CR_DEFINE_ COMMON_EXPLANATION);
43 47
48 static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS =
49 DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS",
50 "cr.exportPath() should have exactly 1 argument: namespace n ame.");
51
44 static final DiagnosticType CR_DEFINE_INVALID_FIRST_ARGUMENT = 52 static final DiagnosticType CR_DEFINE_INVALID_FIRST_ARGUMENT =
45 DiagnosticType.error("JSC_CR_DEFINE_INVALID_FIRST_ARGUMENT", 53 DiagnosticType.error("JSC_CR_DEFINE_INVALID_FIRST_ARGUMENT",
46 "Invalid first argument for cr.define(). " + CR_DEFINE_COMMO N_EXPLANATION); 54 "Invalid first argument for cr.define(). " + CR_DEFINE_COMMO N_EXPLANATION);
47 55
48 static final DiagnosticType CR_DEFINE_INVALID_SECOND_ARGUMENT = 56 static final DiagnosticType CR_DEFINE_INVALID_SECOND_ARGUMENT =
49 DiagnosticType.error("JSC_CR_DEFINE_INVALID_SECOND_ARGUMENT", 57 DiagnosticType.error("JSC_CR_DEFINE_INVALID_SECOND_ARGUMENT",
50 "Invalid second argument for cr.define(). " + CR_DEFINE_COMM ON_EXPLANATION); 58 "Invalid second argument for cr.define(). " + CR_DEFINE_COMM ON_EXPLANATION);
51 59
52 static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION = 60 static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION =
53 DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMEN T", 61 DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMEN T",
54 "Function passed as second argument of cr.define() should re turn the" 62 "Function passed as second argument of cr.define() should re turn the"
55 + " dictionary in its last statement. " + CR_DEFINE_COMMON_E XPLANATION); 63 + " dictionary in its last statement. " + CR_DEFINE_COMMON_E XPLANATION);
56 64
57 static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND = 65 static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND =
58 DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND", 66 DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND",
59 "Invalid cr.PropertyKind passed to cr.defineProperty(): expe cted ATTR," 67 "Invalid cr.PropertyKind passed to cr.defineProperty(): expe cted ATTR,"
60 + " BOOL_ATTR or JS, found \"{0}\"."); 68 + " BOOL_ATTR or JS, found \"{0}\".");
61 69
62 public ChromePass(AbstractCompiler compiler) { 70 public ChromePass(AbstractCompiler compiler) {
63 this.compiler = compiler; 71 this.compiler = compiler;
72 // The global variable "cr" is declared in ui/webui/resources/js/cr.js.
73 this.createdObjects = new HashSet<>(Arrays.asList("cr"));
64 } 74 }
65 75
66 @Override 76 @Override
67 public void process(Node externs, Node root) { 77 public void process(Node externs, Node root) {
68 NodeTraversal.traverse(compiler, root, this); 78 NodeTraversal.traverse(compiler, root, this);
69 } 79 }
70 80
71 @Override 81 @Override
72 public void visit(NodeTraversal t, Node node, Node parent) { 82 public void visit(NodeTraversal t, Node node, Node parent) {
73 if (node.isCall()) { 83 if (node.isCall()) {
74 Node callee = node.getFirstChild(); 84 Node callee = node.getFirstChild();
75 if (callee.matchesQualifiedName(CR_DEFINE)) { 85 if (callee.matchesQualifiedName(CR_DEFINE)) {
76 visitNamespaceDefinition(node, parent); 86 visitNamespaceDefinition(node, parent);
77 compiler.reportCodeChange(); 87 compiler.reportCodeChange();
88 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) {
89 visitExportPath(node, parent);
90 compiler.reportCodeChange();
78 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || 91 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) ||
79 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { 92 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) {
80 visitPropertyDefinition(node, parent); 93 visitPropertyDefinition(node, parent);
81 compiler.reportCodeChange(); 94 compiler.reportCodeChange();
82 } 95 }
83 } 96 }
84 } 97 }
85 98
86 private void visitPropertyDefinition(Node call, Node parent) { 99 private void visitPropertyDefinition(Node call, Node parent) {
87 Node callee = call.getFirstChild(); 100 Node callee = call.getFirstChild();
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 propertyKind.getQualifiedName())); 133 propertyKind.getQualifiedName()));
121 return null; 134 return null;
122 } 135 }
123 136
124 private void setJsDocWithType(Node target, Node type) { 137 private void setJsDocWithType(Node target, Node type) {
125 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); 138 JSDocInfoBuilder builder = new JSDocInfoBuilder(false);
126 builder.recordType(new JSTypeExpression(type, "")); 139 builder.recordType(new JSTypeExpression(type, ""));
127 target.setJSDocInfo(builder.build(target)); 140 target.setJSDocInfo(builder.build(target));
128 } 141 }
129 142
143 private void visitExportPath(Node crExportPathNode, Node parent) {
144 if (crExportPathNode.getChildCount() != 2) {
145 compiler.report(JSError.make(crExportPathNode,
146 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS));
147 return;
148 }
149
150 createAndInsertObjectsForQualifiedName(parent,
151 crExportPathNode.getChildAtIndex(1).getString());
152 }
153
154 private void createAndInsertObjectsForQualifiedName(Node scriptChild, String namespace) {
155 List<Node> objectsForQualifiedName = createObjectsForQualifiedName(names pace);
156 for (Node n : objectsForQualifiedName) {
157 scriptChild.getParent().addChildBefore(n, scriptChild);
158 }
159 }
160
130 private void visitNamespaceDefinition(Node crDefineCallNode, Node parent) { 161 private void visitNamespaceDefinition(Node crDefineCallNode, Node parent) {
131 if (crDefineCallNode.getChildCount() != 3) { 162 if (crDefineCallNode.getChildCount() != 3) {
132 compiler.report(JSError.make(crDefineCallNode, CR_DEFINE_WRONG_NUMBE R_OF_ARGUMENTS)); 163 compiler.report(JSError.make(crDefineCallNode, CR_DEFINE_WRONG_NUMBE R_OF_ARGUMENTS));
133 } 164 }
134 165
135 Node namespaceArg = crDefineCallNode.getChildAtIndex(1); 166 Node namespaceArg = crDefineCallNode.getChildAtIndex(1);
136 Node function = crDefineCallNode.getChildAtIndex(2); 167 Node function = crDefineCallNode.getChildAtIndex(2);
137 168
138 if (!namespaceArg.isString()) { 169 if (!namespaceArg.isString()) {
139 compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_FIRST_A RGUMENT)); 170 compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_FIRST_A RGUMENT));
140 return; 171 return;
141 } 172 }
142 173
143 // TODO(vitalyp): Check namespace name for validity here. It should be a valid chain of 174 // TODO(vitalyp): Check namespace name for validity here. It should be a valid chain of
144 // identifiers. 175 // identifiers.
145 String namespace = namespaceArg.getString(); 176 String namespace = namespaceArg.getString();
146 177
147 List<Node> objectsForQualifiedName = createObjectsForQualifiedName(names pace); 178 createAndInsertObjectsForQualifiedName(parent, namespace);
148 for (Node n : objectsForQualifiedName) {
149 parent.getParent().addChildBefore(n, parent);
150 }
151 179
152 if (!function.isFunction()) { 180 if (!function.isFunction()) {
153 compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_SECOND_ ARGUMENT)); 181 compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_SECOND_ ARGUMENT));
154 return; 182 return;
155 } 183 }
156 184
157 Node returnNode, objectLit; 185 Node returnNode, objectLit;
158 Node functionBlock = function.getLastChild(); 186 Node functionBlock = function.getLastChild();
159 if ((returnNode = functionBlock.getLastChild()) == null || 187 if ((returnNode = functionBlock.getLastChild()) == null ||
160 !returnNode.isReturn() || 188 !returnNode.isReturn() ||
(...skipping 30 matching lines...) Expand all
191 * 219 *
192 * <p><pre> 220 * <p><pre>
193 * var a = a || {}; 221 * var a = a || {};
194 * a.b = a.b || {}; 222 * a.b = a.b || {};
195 * a.b.c = a.b.c || {};</pre> 223 * a.b.c = a.b.c || {};</pre>
196 */ 224 */
197 private List<Node> createObjectsForQualifiedName(String namespace) { 225 private List<Node> createObjectsForQualifiedName(String namespace) {
198 List<Node> objects = new ArrayList<>(); 226 List<Node> objects = new ArrayList<>();
199 String[] parts = namespace.split("\\."); 227 String[] parts = namespace.split("\\.");
200 228
201 objects.add(createJsNode("var " + parts[0] + " = " + parts[0] + " || {}; ")); 229 createObjectIfNew(objects, parts[0], true);
202 230
203 if (parts.length >= 2) { 231 if (parts.length >= 2) {
204 StringBuilder currPrefix = new StringBuilder().append(parts[0]); 232 StringBuilder currPrefix = new StringBuilder().append(parts[0]);
205 for (int i = 1; i < parts.length; ++i) { 233 for (int i = 1; i < parts.length; ++i) {
206 currPrefix.append(".").append(parts[i]); 234 currPrefix.append(".").append(parts[i]);
207 String code = currPrefix + " = " + currPrefix + " || {};"; 235 createObjectIfNew(objects, currPrefix.toString(), false);
208 objects.add(createJsNode(code));
209 } 236 }
210 } 237 }
211 238
212 return objects; 239 return objects;
213 } 240 }
214 241
242 private void createObjectIfNew(List<Node> objects, String name, boolean need Var) {
243 if (!createdObjects.contains(name)) {
244 objects.add(createJsNode((needVar ? "var " : "") + name + " = " + na me + " || {};"));
245 createdObjects.add(name);
246 }
247 }
248
215 private Node createJsNode(String code) { 249 private Node createJsNode(String code) {
216 // The parent node after parseSyntheticCode() is SCRIPT node, we need to get rid of it. 250 // The parent node after parseSyntheticCode() is SCRIPT node, we need to get rid of it.
217 return compiler.parseSyntheticCode(code).removeFirstChild(); 251 return compiler.parseSyntheticCode(code).removeFirstChild();
218 } 252 }
219 253
220 private class RenameInternalsToExternalsCallback extends AbstractPostOrderCa llback { 254 private class RenameInternalsToExternalsCallback extends AbstractPostOrderCa llback {
221 private final String namespaceName; 255 private final String namespaceName;
222 private final Map<String, String> exports; 256 private final Map<String, String> exports;
223 private final Node namespaceBlock; 257 private final Node namespaceBlock;
224 258
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 } 332 }
299 333
300 private Node buildQualifiedName(Node internalName) { 334 private Node buildQualifiedName(Node internalName) {
301 String externalName = this.exports.get(internalName.getString()); 335 String externalName = this.exports.get(internalName.getString());
302 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), 336 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(),
303 this.namespaceName + "." + externalName).srcrefTree(internal Name); 337 this.namespaceName + "." + externalName).srcrefTree(internal Name);
304 } 338 }
305 } 339 }
306 340
307 } 341 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698