Chromium Code Reviews| 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 c7f3c12daccb071f705a6a6a9c896653b3045a37..0485915d6d19c10ddcb31fb92188663bf3067c7c 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 |
| @@ -37,6 +37,7 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
| 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_MAKE_PUBLIC = "cr.makePublic"; |
| private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be called like this:" |
| + " cr.define('name.space', function() '{ ... return {Export: Internal}; }');"; |
| @@ -67,6 +68,14 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
| "Invalid cr.PropertyKind passed to cr.defineProperty(): expected ATTR," |
| + " BOOL_ATTR or JS, found \"{0}\"."); |
| + static final DiagnosticType CR_MAKE_PUBLIC_HAS_NO_JSDOC = |
| + DiagnosticType.error("JSC_CR_MAKE_PUBLIC_HAS_NO_JSDOC", |
| + "Private method exported by cr.makePublic() has no JSDoc."); |
| + |
| + static final DiagnosticType CR_MAKE_PUBLIC_MISSED_DECLARATION = |
| + DiagnosticType.error("CR_MAKE_PUBLIC_MISSED_DECLARATION", |
| + "Method \"{1}_\" exported by cr.makePublic() on \"{0}\" has no declaration."); |
| + |
| public ChromePass(AbstractCompiler compiler) { |
| this.compiler = compiler; |
| // The global variable "cr" is declared in ui/webui/resources/js/cr.js. |
| @@ -92,6 +101,10 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
| callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { |
| visitPropertyDefinition(node, parent); |
| compiler.reportCodeChange(); |
| + } else if (callee.matchesQualifiedName(CR_MAKE_PUBLIC)) { |
| + if (visitMakePublic(node, parent)) { |
| + compiler.reportCodeChange(); |
| + } |
| } |
| } |
| } |
| @@ -140,6 +153,81 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
| target.setJSDocInfo(builder.build(target)); |
| } |
| + private boolean visitMakePublic(Node call, Node exprResult) { |
| + boolean changesMade = false; |
| + Node scope = exprResult.getParent(); |
| + String className = call.getChildAtIndex(1).getQualifiedName(); |
| + String prototype = className + ".prototype"; |
| + Node publicAPI = call.getChildAtIndex(2); |
| + |
| + 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.
|
| + |
| + Set<String> publicAPIStrings = new HashSet<>(); |
| + for (Node methodName: publicAPI.children()) { |
| + assert methodName.isString(); |
|
Tyler Breisacher (Chromium)
2014/09/09 19:34:45
Same here.
Vitaly Pavlenko
2014/09/09 20:31:57
Done.
|
| + publicAPIStrings.add(methodName.getString()); |
| + } |
| + |
| + for (Node child: scope.children()) { |
| + if (isAssignmentToPrototype(child, prototype)) { |
| + Node objectLit = child.getFirstChild().getChildAtIndex(1); |
| + for (Node stringKey : objectLit.children()) { |
| + String field = stringKey.getString(); |
| + changesMade |= maybeAddPublicDeclaration(field, publicAPIStrings, className, |
| + stringKey, scope, exprResult); |
| + } |
| + } else if (isAssignmentToPrototypeMethod(child, prototype)) { |
| + Node assignNode = child.getFirstChild(); |
| + String qualifiedName = assignNode.getFirstChild().getQualifiedName(); |
| + String field = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); |
| + changesMade |= maybeAddPublicDeclaration(field, publicAPIStrings, className, |
| + assignNode, scope, exprResult); |
| + } |
| + } |
| + |
| + for (String missedDeclaration : publicAPIStrings) { |
| + compiler.report(JSError.make(exprResult, CR_MAKE_PUBLIC_MISSED_DECLARATION, className, |
| + missedDeclaration)); |
| + } |
| + |
| + return changesMade; |
| + } |
| + |
| + private boolean isAssignmentToPrototype(Node node, String prototype) { |
| + Node assignNode; |
| + return node.isExprResult() && (assignNode = node.getFirstChild()).isAssign() && |
| + assignNode.getFirstChild().getQualifiedName().equals(prototype); |
| + } |
| + |
| + private boolean isAssignmentToPrototypeMethod(Node node, String prototype) { |
| + Node assignNode; |
| + return node.isExprResult() && (assignNode = node.getFirstChild()).isAssign() && |
| + assignNode.getFirstChild().getQualifiedName().startsWith(prototype + "."); |
| + } |
| + |
| + private boolean maybeAddPublicDeclaration(String field, Set<String> publicAPIStrings, |
| + String className, Node JSDocSourceNode, Node scope, Node exprResult) { |
| + boolean changesMade = false; |
| + if (field.endsWith("_")) { |
| + String publicName = field.substring(0, field.length() - 1); |
| + if (publicAPIStrings.contains(publicName)) { |
| + Node methodDeclaration = NodeUtil.newQualifiedNameNode( |
| + compiler.getCodingConvention(), className + "." + publicName); |
| + if (JSDocSourceNode.getJSDocInfo() != null) { |
| + methodDeclaration.setJSDocInfo(JSDocSourceNode.getJSDocInfo()); |
| + scope.addChildBefore( |
| + IR.exprResult(methodDeclaration).srcrefTree(exprResult), |
| + exprResult); |
| + changesMade = true; |
| + } else { |
| + compiler.report(JSError.make(JSDocSourceNode, CR_MAKE_PUBLIC_HAS_NO_JSDOC)); |
| + } |
| + publicAPIStrings.remove(publicName); |
| + } |
| + } |
| + return changesMade; |
| + } |
| + |
| private void visitExportPath(Node crExportPathNode, Node parent) { |
| if (crExportPathNode.getChildCount() != 2) { |
| compiler.report(JSError.make(crExportPathNode, |
| @@ -342,5 +430,4 @@ public class ChromePass extends AbstractPostOrderCallback implements CompilerPas |
| this.namespaceName + "." + externalName).srcrefTree(internalName); |
| } |
| } |
| - |
| } |