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"; | |
40 | 41 |
41 private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be cal led like this:" | 42 private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be cal led like this:" |
42 + " cr.define('name.space', function() '{ ... return {Export: Intern al}; }');"; | 43 + " cr.define('name.space', function() '{ ... return {Export: Intern al}; }');"; |
43 | 44 |
44 static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS = | 45 static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS = |
45 DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", | 46 DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", |
46 "cr.define() should have exactly 2 arguments. " + CR_DEFINE_ COMMON_EXPLANATION); | 47 "cr.define() should have exactly 2 arguments. " + CR_DEFINE_ COMMON_EXPLANATION); |
47 | 48 |
48 static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS = | 49 static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS = |
49 DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS", | 50 DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS", |
(...skipping 10 matching lines...) Expand all Loading... | |
60 static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION = | 61 static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION = |
61 DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMEN T", | 62 DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMEN T", |
62 "Function passed as second argument of cr.define() should re turn the" | 63 "Function passed as second argument of cr.define() should re turn the" |
63 + " dictionary in its last statement. " + CR_DEFINE_COMMON_E XPLANATION); | 64 + " dictionary in its last statement. " + CR_DEFINE_COMMON_E XPLANATION); |
64 | 65 |
65 static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND = | 66 static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND = |
66 DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND", | 67 DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND", |
67 "Invalid cr.PropertyKind passed to cr.defineProperty(): expe cted ATTR," | 68 "Invalid cr.PropertyKind passed to cr.defineProperty(): expe cted ATTR," |
68 + " BOOL_ATTR or JS, found \"{0}\"."); | 69 + " BOOL_ATTR or JS, found \"{0}\"."); |
69 | 70 |
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("CR_MAKE_PUBLIC_MISSED_DECLARATION", | |
77 "Method \"{1}_\" exported by cr.makePublic() on \"{0}\" has no declaration."); | |
78 | |
70 public ChromePass(AbstractCompiler compiler) { | 79 public ChromePass(AbstractCompiler compiler) { |
71 this.compiler = compiler; | 80 this.compiler = compiler; |
72 // The global variable "cr" is declared in ui/webui/resources/js/cr.js. | 81 // The global variable "cr" is declared in ui/webui/resources/js/cr.js. |
73 this.createdObjects = new HashSet<>(Arrays.asList("cr")); | 82 this.createdObjects = new HashSet<>(Arrays.asList("cr")); |
74 } | 83 } |
75 | 84 |
76 @Override | 85 @Override |
77 public void process(Node externs, Node root) { | 86 public void process(Node externs, Node root) { |
78 NodeTraversal.traverse(compiler, root, this); | 87 NodeTraversal.traverse(compiler, root, this); |
79 } | 88 } |
80 | 89 |
81 @Override | 90 @Override |
82 public void visit(NodeTraversal t, Node node, Node parent) { | 91 public void visit(NodeTraversal t, Node node, Node parent) { |
83 if (node.isCall()) { | 92 if (node.isCall()) { |
84 Node callee = node.getFirstChild(); | 93 Node callee = node.getFirstChild(); |
85 if (callee.matchesQualifiedName(CR_DEFINE)) { | 94 if (callee.matchesQualifiedName(CR_DEFINE)) { |
86 visitNamespaceDefinition(node, parent); | 95 visitNamespaceDefinition(node, parent); |
87 compiler.reportCodeChange(); | 96 compiler.reportCodeChange(); |
88 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { | 97 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { |
89 visitExportPath(node, parent); | 98 visitExportPath(node, parent); |
90 compiler.reportCodeChange(); | 99 compiler.reportCodeChange(); |
91 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || | 100 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || |
92 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { | 101 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { |
93 visitPropertyDefinition(node, parent); | 102 visitPropertyDefinition(node, parent); |
94 compiler.reportCodeChange(); | 103 compiler.reportCodeChange(); |
104 } else if (callee.matchesQualifiedName(CR_MAKE_PUBLIC)) { | |
105 if (visitMakePublic(node, parent)) { | |
106 compiler.reportCodeChange(); | |
107 } | |
95 } | 108 } |
96 } | 109 } |
97 } | 110 } |
98 | 111 |
99 private void visitPropertyDefinition(Node call, Node parent) { | 112 private void visitPropertyDefinition(Node call, Node parent) { |
100 Node callee = call.getFirstChild(); | 113 Node callee = call.getFirstChild(); |
101 String target = call.getChildAtIndex(1).getQualifiedName(); | 114 String target = call.getChildAtIndex(1).getQualifiedName(); |
102 if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith( ".prototype")) { | 115 if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith( ".prototype")) { |
103 target += ".prototype"; | 116 target += ".prototype"; |
104 } | 117 } |
(...skipping 28 matching lines...) Expand all Loading... | |
133 propertyKind.getQualifiedName())); | 146 propertyKind.getQualifiedName())); |
134 return null; | 147 return null; |
135 } | 148 } |
136 | 149 |
137 private void setJsDocWithType(Node target, Node type) { | 150 private void setJsDocWithType(Node target, Node type) { |
138 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); | 151 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); |
139 builder.recordType(new JSTypeExpression(type, "")); | 152 builder.recordType(new JSTypeExpression(type, "")); |
140 target.setJSDocInfo(builder.build(target)); | 153 target.setJSDocInfo(builder.build(target)); |
141 } | 154 } |
142 | 155 |
156 private boolean visitMakePublic(Node call, Node exprResult) { | |
157 boolean changesMade = false; | |
158 Node scope = exprResult.getParent(); | |
159 String className = call.getChildAtIndex(1).getQualifiedName(); | |
160 String prototype = className + ".prototype"; | |
161 Node publicAPI = call.getChildAtIndex(2); | |
162 | |
163 assert publicAPI != null && publicAPI.isArrayLit(); | |
Tyler Breisacher (Chromium)
2014/09/09 19:34:45
This should probably report an error to the user,
Vitaly Pavlenko
2014/09/09 20:31:57
Done.
| |
164 | |
165 Set<String> publicAPIStrings = new HashSet<>(); | |
166 for (Node methodName: publicAPI.children()) { | |
167 assert methodName.isString(); | |
Tyler Breisacher (Chromium)
2014/09/09 19:34:45
Same here.
Vitaly Pavlenko
2014/09/09 20:31:57
Done.
| |
168 publicAPIStrings.add(methodName.getString()); | |
169 } | |
170 | |
171 for (Node child: scope.children()) { | |
172 if (isAssignmentToPrototype(child, prototype)) { | |
173 Node objectLit = child.getFirstChild().getChildAtIndex(1); | |
174 for (Node stringKey : objectLit.children()) { | |
175 String field = stringKey.getString(); | |
176 changesMade |= maybeAddPublicDeclaration(field, publicAPIStr ings, className, | |
177 stringKey, scope, e xprResult); | |
178 } | |
179 } else if (isAssignmentToPrototypeMethod(child, prototype)) { | |
180 Node assignNode = child.getFirstChild(); | |
181 String qualifiedName = assignNode.getFirstChild().getQualifiedNa me(); | |
182 String field = qualifiedName.substring(qualifiedName.lastIndexOf ('.') + 1); | |
183 changesMade |= maybeAddPublicDeclaration(field, publicAPIStrings , className, | |
184 assignNode, scope, expr Result); | |
185 } | |
186 } | |
187 | |
188 for (String missedDeclaration : publicAPIStrings) { | |
189 compiler.report(JSError.make(exprResult, CR_MAKE_PUBLIC_MISSED_DECLA RATION, className, | |
190 missedDeclaration)); | |
191 } | |
192 | |
193 return changesMade; | |
194 } | |
195 | |
196 private boolean isAssignmentToPrototype(Node node, String prototype) { | |
197 Node assignNode; | |
198 return node.isExprResult() && (assignNode = node.getFirstChild()).isAssi gn() && | |
199 assignNode.getFirstChild().getQualifiedName().equals(prototype); | |
200 } | |
201 | |
202 private boolean isAssignmentToPrototypeMethod(Node node, String prototype) { | |
203 Node assignNode; | |
204 return node.isExprResult() && (assignNode = node.getFirstChild()).isAssi gn() && | |
205 assignNode.getFirstChild().getQualifiedName().startsWith(prototy pe + "."); | |
206 } | |
207 | |
208 private boolean maybeAddPublicDeclaration(String field, Set<String> publicAP IStrings, | |
209 String className, Node JSDocSourceNode, Node scope, Node exprResult) { | |
210 boolean changesMade = false; | |
211 if (field.endsWith("_")) { | |
212 String publicName = field.substring(0, field.length() - 1); | |
213 if (publicAPIStrings.contains(publicName)) { | |
214 Node methodDeclaration = NodeUtil.newQualifiedNameNode( | |
215 compiler.getCodingConvention(), className + "." + public Name); | |
216 if (JSDocSourceNode.getJSDocInfo() != null) { | |
217 methodDeclaration.setJSDocInfo(JSDocSourceNode.getJSDocInfo( )); | |
218 scope.addChildBefore( | |
219 IR.exprResult(methodDeclaration).srcrefTree(exprResu lt), | |
220 exprResult); | |
221 changesMade = true; | |
222 } else { | |
223 compiler.report(JSError.make(JSDocSourceNode, CR_MAKE_PUBLIC _HAS_NO_JSDOC)); | |
224 } | |
225 publicAPIStrings.remove(publicName); | |
226 } | |
227 } | |
228 return changesMade; | |
229 } | |
230 | |
143 private void visitExportPath(Node crExportPathNode, Node parent) { | 231 private void visitExportPath(Node crExportPathNode, Node parent) { |
144 if (crExportPathNode.getChildCount() != 2) { | 232 if (crExportPathNode.getChildCount() != 2) { |
145 compiler.report(JSError.make(crExportPathNode, | 233 compiler.report(JSError.make(crExportPathNode, |
146 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); | 234 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); |
147 return; | 235 return; |
148 } | 236 } |
149 | 237 |
150 createAndInsertObjectsForQualifiedName(parent, | 238 createAndInsertObjectsForQualifiedName(parent, |
151 crExportPathNode.getChildAtIndex(1).getString()); | 239 crExportPathNode.getChildAtIndex(1).getString()); |
152 } | 240 } |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
335 } | 423 } |
336 } | 424 } |
337 } | 425 } |
338 | 426 |
339 private Node buildQualifiedName(Node internalName) { | 427 private Node buildQualifiedName(Node internalName) { |
340 String externalName = this.exports.get(internalName.getString()); | 428 String externalName = this.exports.get(internalName.getString()); |
341 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), | 429 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), |
342 this.namespaceName + "." + externalName).srcrefTree(internal Name); | 430 this.namespaceName + "." + externalName).srcrefTree(internal Name); |
343 } | 431 } |
344 } | 432 } |
345 | |
346 } | 433 } |
OLD | NEW |