| 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("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 |
| 70 public ChromePass(AbstractCompiler compiler) { | 84 public ChromePass(AbstractCompiler compiler) { |
| 71 this.compiler = compiler; | 85 this.compiler = compiler; |
| 72 // The global variable "cr" is declared in ui/webui/resources/js/cr.js. | 86 // The global variable "cr" is declared in ui/webui/resources/js/cr.js. |
| 73 this.createdObjects = new HashSet<>(Arrays.asList("cr")); | 87 this.createdObjects = new HashSet<>(Arrays.asList("cr")); |
| 74 } | 88 } |
| 75 | 89 |
| 76 @Override | 90 @Override |
| 77 public void process(Node externs, Node root) { | 91 public void process(Node externs, Node root) { |
| 78 NodeTraversal.traverse(compiler, root, this); | 92 NodeTraversal.traverse(compiler, root, this); |
| 79 } | 93 } |
| 80 | 94 |
| 81 @Override | 95 @Override |
| 82 public void visit(NodeTraversal t, Node node, Node parent) { | 96 public void visit(NodeTraversal t, Node node, Node parent) { |
| 83 if (node.isCall()) { | 97 if (node.isCall()) { |
| 84 Node callee = node.getFirstChild(); | 98 Node callee = node.getFirstChild(); |
| 85 if (callee.matchesQualifiedName(CR_DEFINE)) { | 99 if (callee.matchesQualifiedName(CR_DEFINE)) { |
| 86 visitNamespaceDefinition(node, parent); | 100 visitNamespaceDefinition(node, parent); |
| 87 compiler.reportCodeChange(); | 101 compiler.reportCodeChange(); |
| 88 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { | 102 } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { |
| 89 visitExportPath(node, parent); | 103 visitExportPath(node, parent); |
| 90 compiler.reportCodeChange(); | 104 compiler.reportCodeChange(); |
| 91 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || | 105 } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || |
| 92 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { | 106 callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { |
| 93 visitPropertyDefinition(node, parent); | 107 visitPropertyDefinition(node, parent); |
| 94 compiler.reportCodeChange(); | 108 compiler.reportCodeChange(); |
| 109 } else if (callee.matchesQualifiedName(CR_MAKE_PUBLIC)) { |
| 110 if (visitMakePublic(node, parent)) { |
| 111 compiler.reportCodeChange(); |
| 112 } |
| 95 } | 113 } |
| 96 } | 114 } |
| 97 } | 115 } |
| 98 | 116 |
| 99 private void visitPropertyDefinition(Node call, Node parent) { | 117 private void visitPropertyDefinition(Node call, Node parent) { |
| 100 Node callee = call.getFirstChild(); | 118 Node callee = call.getFirstChild(); |
| 101 String target = call.getChildAtIndex(1).getQualifiedName(); | 119 String target = call.getChildAtIndex(1).getQualifiedName(); |
| 102 if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith(
".prototype")) { | 120 if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith(
".prototype")) { |
| 103 target += ".prototype"; | 121 target += ".prototype"; |
| 104 } | 122 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 133 propertyKind.getQualifiedName())); | 151 propertyKind.getQualifiedName())); |
| 134 return null; | 152 return null; |
| 135 } | 153 } |
| 136 | 154 |
| 137 private void setJsDocWithType(Node target, Node type) { | 155 private void setJsDocWithType(Node target, Node type) { |
| 138 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); | 156 JSDocInfoBuilder builder = new JSDocInfoBuilder(false); |
| 139 builder.recordType(new JSTypeExpression(type, "")); | 157 builder.recordType(new JSTypeExpression(type, "")); |
| 140 target.setJSDocInfo(builder.build(target)); | 158 target.setJSDocInfo(builder.build(target)); |
| 141 } | 159 } |
| 142 | 160 |
| 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 |
| 143 private void visitExportPath(Node crExportPathNode, Node parent) { | 253 private void visitExportPath(Node crExportPathNode, Node parent) { |
| 144 if (crExportPathNode.getChildCount() != 2) { | 254 if (crExportPathNode.getChildCount() != 2) { |
| 145 compiler.report(JSError.make(crExportPathNode, | 255 compiler.report(JSError.make(crExportPathNode, |
| 146 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); | 256 CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); |
| 147 return; | 257 return; |
| 148 } | 258 } |
| 149 | 259 |
| 150 createAndInsertObjectsForQualifiedName(parent, | 260 createAndInsertObjectsForQualifiedName(parent, |
| 151 crExportPathNode.getChildAtIndex(1).getString()); | 261 crExportPathNode.getChildAtIndex(1).getString()); |
| 152 } | 262 } |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 } | 445 } |
| 336 } | 446 } |
| 337 } | 447 } |
| 338 | 448 |
| 339 private Node buildQualifiedName(Node internalName) { | 449 private Node buildQualifiedName(Node internalName) { |
| 340 String externalName = this.exports.get(internalName.getString()); | 450 String externalName = this.exports.get(internalName.getString()); |
| 341 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), | 451 return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), |
| 342 this.namespaceName + "." + externalName).srcrefTree(internal
Name); | 452 this.namespaceName + "." + externalName).srcrefTree(internal
Name); |
| 343 } | 453 } |
| 344 } | 454 } |
| 345 | |
| 346 } | 455 } |
| OLD | NEW |