OLD | NEW |
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; |
(...skipping 19 matching lines...) Expand all Loading... |
30 */ | 30 */ |
31 public class ChromePass extends AbstractPostOrderCallback implements CompilerPas
s { | 31 public class ChromePass extends AbstractPostOrderCallback implements CompilerPas
s { |
32 final AbstractCompiler compiler; | 32 final AbstractCompiler compiler; |
33 | 33 |
34 private Set<String> createdObjects; | 34 private Set<String> createdObjects; |
35 | 35 |
36 private static final String CR_DEFINE = "cr.define"; | 36 private static final String CR_DEFINE = "cr.define"; |
37 private static final String CR_EXPORT_PATH = "cr.exportPath"; | 37 private static final String CR_EXPORT_PATH = "cr.exportPath"; |
38 private static final String OBJECT_DEFINE_PROPERTY = "Object.defineProperty"
; | 38 private static final String OBJECT_DEFINE_PROPERTY = "Object.defineProperty"
; |
39 private static final String CR_DEFINE_PROPERTY = "cr.defineProperty"; | 39 private static final String CR_DEFINE_PROPERTY = "cr.defineProperty"; |
40 private static final String CR_MAKE_PUBLIC = "cr.makePublic"; | |
41 | 40 |
42 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:" |
43 + " cr.define('name.space', function() '{ ... return {Export: Intern
al}; }');"; | 42 + " cr.define('name.space', function() '{ ... return {Export: Intern
al}; }');"; |
44 | 43 |
45 static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS = | 44 static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS = |
46 DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", | 45 DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", |
47 "cr.define() should have exactly 2 arguments. " + CR_DEFINE_
COMMON_EXPLANATION); | 46 "cr.define() should have exactly 2 arguments. " + CR_DEFINE_
COMMON_EXPLANATION); |
48 | 47 |
49 static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS = | 48 static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS = |
50 DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS", | 49 DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS", |
(...skipping 10 matching lines...) Expand all Loading... |
61 static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION = | 60 static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION = |
62 DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMEN
T", | 61 DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMEN
T", |
63 "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" |
64 + " dictionary in its last statement. " + CR_DEFINE_COMMON_E
XPLANATION); | 63 + " dictionary in its last statement. " + CR_DEFINE_COMMON_E
XPLANATION); |
65 | 64 |
66 static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND = | 65 static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND = |
67 DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND", | 66 DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND", |
68 "Invalid cr.PropertyKind passed to cr.defineProperty(): expe
cted ATTR," | 67 "Invalid cr.PropertyKind passed to cr.defineProperty(): expe
cted ATTR," |
69 + " BOOL_ATTR or JS, found \"{0}\"."); | 68 + " BOOL_ATTR or JS, found \"{0}\"."); |
70 | 69 |
71 static final DiagnosticType CR_MAKE_PUBLIC_HAS_NO_JSDOC = | |
72 DiagnosticType.error("JSC_CR_MAKE_PUBLIC_HAS_NO_JSDOC", | |
73 "Private method exported by cr.makePublic() has no JSDoc."); | |
74 | |
75 static final DiagnosticType CR_MAKE_PUBLIC_MISSED_DECLARATION = | |
76 DiagnosticType.error("JSC_CR_MAKE_PUBLIC_MISSED_DECLARATION", | |
77 "Method \"{1}_\" exported by cr.makePublic() on \"{0}\" has
no declaration."); | |
78 | |
79 static final DiagnosticType CR_MAKE_PUBLIC_INVALID_SECOND_ARGUMENT = | |
80 DiagnosticType.error("JSC_CR_MAKE_PUBLIC_INVALID_SECOND_ARGUMENT", | |
81 "Invalid second argument passed to cr.makePublic(): should b
e array of " + | |
82 "strings."); | |
83 | |
84 public ChromePass(AbstractCompiler compiler) { | 70 public ChromePass(AbstractCompiler compiler) { |
85 this.compiler = compiler; | 71 this.compiler = compiler; |
86 // The global variable "cr" is declared in ui/webui/resources/js/cr.js. | 72 // The global variable "cr" is declared in ui/webui/resources/js/cr.js. |
87 this.createdObjects = new HashSet<>(Arrays.asList("cr")); | 73 this.createdObjects = new HashSet<>(Arrays.asList("cr")); |
88 } | 74 } |
89 | 75 |
90 @Override | 76 @Override |
91 public void process(Node externs, Node root) { | 77 public void process(Node externs, Node root) { |
92 NodeTraversal.traverse(compiler, root, this); | 78 NodeTraversal.traverse(compiler, root, this); |
93 } | 79 } |
94 | 80 |
95 @Override | 81 @Override |
96 public void visit(NodeTraversal t, Node node, Node parent) { | 82 public void visit(NodeTraversal t, Node node, Node parent) { |
97 if (node.isCall()) { | 83 if (node.isCall()) { |
98 Node callee = node.getFirstChild(); | 84 Node callee = node.getFirstChild(); |
99 if (callee.matchesQualifiedName(CR_DEFINE)) { | 85 if (callee.matchesQualifiedName(CR_DEFINE)) { |
100 visitNamespaceDefinition(node, parent); | 86 visitNamespaceDefinition(node, parent); |
101 compiler.reportCodeChange(); | 87 compiler.reportCodeChange(); |
102 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { | 88 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { |
103 visitExportPath(node, parent); | 89 visitExportPath(node, parent); |
104 compiler.reportCodeChange(); | 90 compiler.reportCodeChange(); |
105 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || | 91 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || |
106 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { | 92 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { |
107 visitPropertyDefinition(node, parent); | 93 visitPropertyDefinition(node, parent); |
108 compiler.reportCodeChange(); | 94 compiler.reportCodeChange(); |
109 } else if (callee.matchesQualifiedName(CR_MAKE_PUBLIC)) { | |
110 if (visitMakePublic(node, parent)) { | |
111 compiler.reportCodeChange(); | |
112 } | |
113 } | 95 } |
114 } | 96 } |
115 } | 97 } |
116 | 98 |
117 private void visitPropertyDefinition(Node call, Node parent) { | 99 private void visitPropertyDefinition(Node call, Node parent) { |
118 Node callee = call.getFirstChild(); | 100 Node callee = call.getFirstChild(); |
119 String target = call.getChildAtIndex(1).getQualifiedName(); | 101 String target = call.getChildAtIndex(1).getQualifiedName(); |
120 if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith(
".prototype")) { | 102 if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith(
".prototype")) { |
121 target += ".prototype"; | 103 target += ".prototype"; |
122 } | 104 } |
(...skipping 28 matching lines...) Expand all Loading... |
151 propertyKind.getQualifiedName())); | 133 propertyKind.getQualifiedName())); |
152 return null; | 134 return null; |
153 } | 135 } |
154 | 136 |
155 private void setJsDocWithType(Node target, Node type) { | 137 private void setJsDocWithType(Node target, Node type) { |
156 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); | 138 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); |
157 builder.recordType(new JSTypeExpression(type, "")); | 139 builder.recordType(new JSTypeExpression(type, "")); |
158 target.setJSDocInfo(builder.build(target)); | 140 target.setJSDocInfo(builder.build(target)); |
159 } | 141 } |
160 | 142 |
161 private boolean visitMakePublic(Node call, Node exprResult) { | |
162 boolean changesMade = false; | |
163 Node scope = exprResult.getParent(); | |
164 String className = call.getChildAtIndex(1).getQualifiedName(); | |
165 String prototype = className + ".prototype"; | |
166 Node methods = call.getChildAtIndex(2); | |
167 | |
168 if (methods == null || !methods.isArrayLit()) { | |
169 compiler.report(JSError.make(exprResult, CR_MAKE_PUBLIC_INVALID_SECO
ND_ARGUMENT)); | |
170 return changesMade; | |
171 } | |
172 | |
173 Set<String> methodNames = new HashSet<>(); | |
174 for (Node methodName: methods.children()) { | |
175 if (!methodName.isString()) { | |
176 compiler.report(JSError.make(methodName, CR_MAKE_PUBLIC_INVALID_
SECOND_ARGUMENT)); | |
177 return changesMade; | |
178 } | |
179 methodNames.add(methodName.getString()); | |
180 } | |
181 | |
182 for (Node child: scope.children()) { | |
183 if (isAssignmentToPrototype(child, prototype)) { | |
184 Node objectLit = child.getFirstChild().getChildAtIndex(1); | |
185 for (Node stringKey : objectLit.children()) { | |
186 String field = stringKey.getString(); | |
187 changesMade |= maybeAddPublicDeclaration(field, methodNames,
className, | |
188 stringKey, scope, e
xprResult); | |
189 } | |
190 } else if (isAssignmentToPrototypeMethod(child, prototype)) { | |
191 Node assignNode = child.getFirstChild(); | |
192 String qualifiedName = assignNode.getFirstChild().getQualifiedNa
me(); | |
193 String field = qualifiedName.substring(qualifiedName.lastIndexOf
('.') + 1); | |
194 changesMade |= maybeAddPublicDeclaration(field, methodNames, cla
ssName, | |
195 assignNode, scope, expr
Result); | |
196 } else if (isDummyPrototypeMethodDeclaration(child, prototype)) { | |
197 String qualifiedName = child.getFirstChild().getQualifiedName(); | |
198 String field = qualifiedName.substring(qualifiedName.lastIndexOf
('.') + 1); | |
199 changesMade |= maybeAddPublicDeclaration(field, methodNames, cla
ssName, | |
200 child.getFirstChild(),
scope, exprResult); | |
201 } | |
202 } | |
203 | |
204 for (String missedDeclaration : methodNames) { | |
205 compiler.report(JSError.make(exprResult, CR_MAKE_PUBLIC_MISSED_DECLA
RATION, className, | |
206 missedDeclaration)); | |
207 } | |
208 | |
209 return changesMade; | |
210 } | |
211 | |
212 private boolean isAssignmentToPrototype(Node node, String prototype) { | |
213 Node assignNode; | |
214 return node.isExprResult() && (assignNode = node.getFirstChild()).isAssi
gn() && | |
215 assignNode.getFirstChild().getQualifiedName().equals(prototype); | |
216 } | |
217 | |
218 private boolean isAssignmentToPrototypeMethod(Node node, String prototype) { | |
219 Node assignNode; | |
220 return node.isExprResult() && (assignNode = node.getFirstChild()).isAssi
gn() && | |
221 assignNode.getFirstChild().getQualifiedName().startsWith(prototy
pe + "."); | |
222 } | |
223 | |
224 private boolean isDummyPrototypeMethodDeclaration(Node node, String prototyp
e) { | |
225 Node getPropNode; | |
226 return node.isExprResult() && (getPropNode = node.getFirstChild()).isGet
Prop() && | |
227 getPropNode.getQualifiedName().startsWith(prototype + "."); | |
228 } | |
229 | |
230 private boolean maybeAddPublicDeclaration(String field, Set<String> publicAP
IStrings, | |
231 String className, Node jsDocSourceNode, Node scope, Node exprResult)
{ | |
232 boolean changesMade = false; | |
233 if (field.endsWith("_")) { | |
234 String publicName = field.substring(0, field.length() - 1); | |
235 if (publicAPIStrings.contains(publicName)) { | |
236 Node methodDeclaration = NodeUtil.newQualifiedNameNode( | |
237 compiler.getCodingConvention(), className + "." + public
Name); | |
238 if (jsDocSourceNode.getJSDocInfo() != null) { | |
239 methodDeclaration.setJSDocInfo(jsDocSourceNode.getJSDocInfo(
)); | |
240 scope.addChildBefore( | |
241 IR.exprResult(methodDeclaration).srcrefTree(exprResu
lt), | |
242 exprResult); | |
243 changesMade = true; | |
244 } else { | |
245 compiler.report(JSError.make(jsDocSourceNode, CR_MAKE_PUBLIC
_HAS_NO_JSDOC)); | |
246 } | |
247 publicAPIStrings.remove(publicName); | |
248 } | |
249 } | |
250 return changesMade; | |
251 } | |
252 | |
253 private void visitExportPath(Node crExportPathNode, Node parent) { | 143 private void visitExportPath(Node crExportPathNode, Node parent) { |
254 if (crExportPathNode.getChildCount() != 2) { | 144 if (crExportPathNode.getChildCount() != 2) { |
255 compiler.report(JSError.make(crExportPathNode, | 145 compiler.report(JSError.make(crExportPathNode, |
256 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); | 146 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); |
257 return; | 147 return; |
258 } | 148 } |
259 | 149 |
260 createAndInsertObjectsForQualifiedName(parent, | 150 createAndInsertObjectsForQualifiedName(parent, |
261 crExportPathNode.getChildAtIndex(1).getString()); | 151 crExportPathNode.getChildAtIndex(1).getString()); |
262 } | 152 } |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 } | 335 } |
446 } | 336 } |
447 } | 337 } |
448 | 338 |
449 private Node buildQualifiedName(Node internalName) { | 339 private Node buildQualifiedName(Node internalName) { |
450 String externalName = this.exports.get(internalName.getString()); | 340 String externalName = this.exports.get(internalName.getString()); |
451 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), | 341 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), |
452 this.namespaceName + "." + externalName).srcrefTree(internal
Name); | 342 this.namespaceName + "." + externalName).srcrefTree(internal
Name); |
453 } | 343 } |
454 } | 344 } |
| 345 |
455 } | 346 } |
OLD | NEW |