16e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 26e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 36e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// found in the LICENSE file. 46e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)package com.google.javascript.jscomp; 66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; 86e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import com.google.javascript.rhino.IR; 96e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import com.google.javascript.rhino.JSDocInfoBuilder; 106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import com.google.javascript.rhino.JSTypeExpression; 116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import com.google.javascript.rhino.Node; 126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import com.google.javascript.rhino.Token; 136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.ArrayList; 156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.Arrays; 166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.HashMap; 176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.HashSet; 186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.List; 196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.Map; 206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import java.util.Set; 216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Compiler pass for Chrome-specific needs. It handles the following Chrome JS features: 246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * <ul> 256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * <li>namespace declaration using {@code cr.define()}, 266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * <li>unquoted property declaration using {@code {cr|Object}.defineProperty()}. 276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * </ul> 286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * 296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * <p>For the details, see tests inside ChromePassTest.java. 306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)public class ChromePass extends AbstractPostOrderCallback implements CompilerPass { 326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) final AbstractCompiler compiler; 336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private Set<String> createdObjects; 356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final String CR_DEFINE = "cr.define"; 376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final String CR_EXPORT_PATH = "cr.exportPath"; 386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final String OBJECT_DEFINE_PROPERTY = "Object.defineProperty"; 396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final String CR_DEFINE_PROPERTY = "cr.defineProperty"; 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private static final String CR_MAKE_PUBLIC = "cr.makePublic"; 416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final String CR_DEFINE_COMMON_EXPLANATION = "It should be called like this:" 436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) + " cr.define('name.space', function() '{ ... return {Export: Internal}; }');"; 446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) static final DiagnosticType CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS = 466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DiagnosticType.error("JSC_CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS", 476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) "cr.define() should have exactly 2 arguments. " + CR_DEFINE_COMMON_EXPLANATION); 486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) static final DiagnosticType CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS = 506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DiagnosticType.error("JSC_CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS", 516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) "cr.exportPath() should have exactly 1 argument: namespace name."); 526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) static final DiagnosticType CR_DEFINE_INVALID_FIRST_ARGUMENT = 546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DiagnosticType.error("JSC_CR_DEFINE_INVALID_FIRST_ARGUMENT", 556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) "Invalid first argument for cr.define(). " + CR_DEFINE_COMMON_EXPLANATION); 566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) static final DiagnosticType CR_DEFINE_INVALID_SECOND_ARGUMENT = 586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DiagnosticType.error("JSC_CR_DEFINE_INVALID_SECOND_ARGUMENT", 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) "Invalid second argument for cr.define(). " + CR_DEFINE_COMMON_EXPLANATION); 606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) static final DiagnosticType CR_DEFINE_INVALID_RETURN_IN_FUNCTION = 626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DiagnosticType.error("JSC_CR_DEFINE_INVALID_RETURN_IN_SECOND_ARGUMENT", 636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) "Function passed as second argument of cr.define() should return the" 646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) + " dictionary in its last statement. " + CR_DEFINE_COMMON_EXPLANATION); 656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) static final DiagnosticType CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND = 676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DiagnosticType.error("JSC_CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND", 686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) "Invalid cr.PropertyKind passed to cr.defineProperty(): expected ATTR," 696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) + " BOOL_ATTR or JS, found \"{0}\"."); 706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci static final DiagnosticType CR_MAKE_PUBLIC_HAS_NO_JSDOC = 721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DiagnosticType.error("JSC_CR_MAKE_PUBLIC_HAS_NO_JSDOC", 731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "Private method exported by cr.makePublic() has no JSDoc."); 741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci static final DiagnosticType CR_MAKE_PUBLIC_MISSED_DECLARATION = 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DiagnosticType.error("JSC_CR_MAKE_PUBLIC_MISSED_DECLARATION", 771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "Method \"{1}_\" exported by cr.makePublic() on \"{0}\" has no declaration."); 781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci static final DiagnosticType CR_MAKE_PUBLIC_INVALID_SECOND_ARGUMENT = 801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DiagnosticType.error("JSC_CR_MAKE_PUBLIC_INVALID_SECOND_ARGUMENT", 811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "Invalid second argument passed to cr.makePublic(): should be array of " + 821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "strings."); 831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public ChromePass(AbstractCompiler compiler) { 856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.compiler = compiler; 866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // The global variable "cr" is declared in ui/webui/resources/js/cr.js. 876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.createdObjects = new HashSet<>(Arrays.asList("cr")); 886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void process(Node externs, Node root) { 926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) NodeTraversal.traverse(compiler, root, this); 936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void visit(NodeTraversal t, Node node, Node parent) { 976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (node.isCall()) { 986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node callee = node.getFirstChild(); 996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (callee.matchesQualifiedName(CR_DEFINE)) { 1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) visitNamespaceDefinition(node, parent); 1016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.reportCodeChange(); 1026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else if (callee.matchesQualifiedName(CR_EXPORT_PATH)) { 1036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) visitExportPath(node, parent); 1046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.reportCodeChange(); 1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else if (callee.matchesQualifiedName(OBJECT_DEFINE_PROPERTY) || 1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { 1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) visitPropertyDefinition(node, parent); 1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.reportCodeChange(); 1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (callee.matchesQualifiedName(CR_MAKE_PUBLIC)) { 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (visitMakePublic(node, parent)) { 1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compiler.reportCodeChange(); 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private void visitPropertyDefinition(Node call, Node parent) { 1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node callee = call.getFirstChild(); 1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) String target = call.getChildAtIndex(1).getQualifiedName(); 1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY) && !target.endsWith(".prototype")) { 1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) target += ".prototype"; 1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node property = call.getChildAtIndex(2); 1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node getPropNode = NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), 1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) target + "." + property.getString()).srcrefTree(call); 1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (callee.matchesQualifiedName(CR_DEFINE_PROPERTY)) { 1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) setJsDocWithType(getPropNode, getTypeByCrPropertyKind(call.getChildAtIndex(3))); 1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else { 1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) setJsDocWithType(getPropNode, new Node(Token.QMARK)); 1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node definitionNode = IR.exprResult(getPropNode).srcref(parent); 1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) parent.getParent().addChildAfter(definitionNode, parent); 1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private Node getTypeByCrPropertyKind(Node propertyKind) { 1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (propertyKind == null || propertyKind.matchesQualifiedName("cr.PropertyKind.JS")) { 1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return new Node(Token.QMARK); 1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (propertyKind.matchesQualifiedName("cr.PropertyKind.ATTR")) { 1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return IR.string("string"); 1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (propertyKind.matchesQualifiedName("cr.PropertyKind.BOOL_ATTR")) { 1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return IR.string("boolean"); 1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.report(JSError.make(propertyKind, CR_DEFINE_PROPERTY_INVALID_PROPERTY_KIND, 1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) propertyKind.getQualifiedName())); 1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return null; 1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private void setJsDocWithType(Node target, Node type) { 1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) JSDocInfoBuilder builder = new JSDocInfoBuilder(false); 1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) builder.recordType(new JSTypeExpression(type, "")); 1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) target.setJSDocInfo(builder.build(target)); 1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private boolean visitMakePublic(Node call, Node exprResult) { 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci boolean changesMade = false; 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node scope = exprResult.getParent(); 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String className = call.getChildAtIndex(1).getQualifiedName(); 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String prototype = className + ".prototype"; 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node methods = call.getChildAtIndex(2); 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (methods == null || !methods.isArrayLit()) { 1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compiler.report(JSError.make(exprResult, CR_MAKE_PUBLIC_INVALID_SECOND_ARGUMENT)); 1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return changesMade; 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Set<String> methodNames = new HashSet<>(); 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (Node methodName: methods.children()) { 1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!methodName.isString()) { 1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compiler.report(JSError.make(methodName, CR_MAKE_PUBLIC_INVALID_SECOND_ARGUMENT)); 1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return changesMade; 1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci methodNames.add(methodName.getString()); 1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (Node child: scope.children()) { 1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (isAssignmentToPrototype(child, prototype)) { 1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node objectLit = child.getFirstChild().getChildAtIndex(1); 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (Node stringKey : objectLit.children()) { 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String field = stringKey.getString(); 1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci changesMade |= maybeAddPublicDeclaration(field, methodNames, className, 1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci stringKey, scope, exprResult); 1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (isAssignmentToPrototypeMethod(child, prototype)) { 1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node assignNode = child.getFirstChild(); 1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String qualifiedName = assignNode.getFirstChild().getQualifiedName(); 1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String field = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); 1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci changesMade |= maybeAddPublicDeclaration(field, methodNames, className, 1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assignNode, scope, exprResult); 1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (isDummyPrototypeMethodDeclaration(child, prototype)) { 1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String qualifiedName = child.getFirstChild().getQualifiedName(); 1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String field = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); 1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci changesMade |= maybeAddPublicDeclaration(field, methodNames, className, 2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci child.getFirstChild(), scope, exprResult); 2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (String missedDeclaration : methodNames) { 2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compiler.report(JSError.make(exprResult, CR_MAKE_PUBLIC_MISSED_DECLARATION, className, 2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci missedDeclaration)); 2071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return changesMade; 2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private boolean isAssignmentToPrototype(Node node, String prototype) { 2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node assignNode; 2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return node.isExprResult() && (assignNode = node.getFirstChild()).isAssign() && 2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assignNode.getFirstChild().getQualifiedName().equals(prototype); 2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private boolean isAssignmentToPrototypeMethod(Node node, String prototype) { 2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node assignNode; 2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return node.isExprResult() && (assignNode = node.getFirstChild()).isAssign() && 2211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assignNode.getFirstChild().getQualifiedName().startsWith(prototype + "."); 2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private boolean isDummyPrototypeMethodDeclaration(Node node, String prototype) { 2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node getPropNode; 2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return node.isExprResult() && (getPropNode = node.getFirstChild()).isGetProp() && 2271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci getPropNode.getQualifiedName().startsWith(prototype + "."); 2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private boolean maybeAddPublicDeclaration(String field, Set<String> publicAPIStrings, 2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String className, Node jsDocSourceNode, Node scope, Node exprResult) { 2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci boolean changesMade = false; 2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (field.endsWith("_")) { 2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci String publicName = field.substring(0, field.length() - 1); 2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (publicAPIStrings.contains(publicName)) { 2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Node methodDeclaration = NodeUtil.newQualifiedNameNode( 2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compiler.getCodingConvention(), className + "." + publicName); 2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (jsDocSourceNode.getJSDocInfo() != null) { 2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci methodDeclaration.setJSDocInfo(jsDocSourceNode.getJSDocInfo()); 2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci scope.addChildBefore( 2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci IR.exprResult(methodDeclaration).srcrefTree(exprResult), 2421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci exprResult); 2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci changesMade = true; 2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else { 2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compiler.report(JSError.make(jsDocSourceNode, CR_MAKE_PUBLIC_HAS_NO_JSDOC)); 2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci publicAPIStrings.remove(publicName); 2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return changesMade; 2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private void visitExportPath(Node crExportPathNode, Node parent) { 2546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (crExportPathNode.getChildCount() != 2) { 2556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.report(JSError.make(crExportPathNode, 2566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) CR_EXPORT_PATH_WRONG_NUMBER_OF_ARGUMENTS)); 2576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 2586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) createAndInsertObjectsForQualifiedName(parent, 2616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) crExportPathNode.getChildAtIndex(1).getString()); 2626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private void createAndInsertObjectsForQualifiedName(Node scriptChild, String namespace) { 2656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) List<Node> objectsForQualifiedName = createObjectsForQualifiedName(namespace); 2666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (Node n : objectsForQualifiedName) { 2676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) scriptChild.getParent().addChildBefore(n, scriptChild); 2686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private void visitNamespaceDefinition(Node crDefineCallNode, Node parent) { 2726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (crDefineCallNode.getChildCount() != 3) { 2736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.report(JSError.make(crDefineCallNode, CR_DEFINE_WRONG_NUMBER_OF_ARGUMENTS)); 2746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node namespaceArg = crDefineCallNode.getChildAtIndex(1); 2776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node function = crDefineCallNode.getChildAtIndex(2); 2786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!namespaceArg.isString()) { 2806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_FIRST_ARGUMENT)); 2816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 2826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // TODO(vitalyp): Check namespace name for validity here. It should be a valid chain of 2856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // identifiers. 2866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) String namespace = namespaceArg.getString(); 2876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) createAndInsertObjectsForQualifiedName(parent, namespace); 2896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!function.isFunction()) { 2916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_SECOND_ARGUMENT)); 2926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 2936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node returnNode, objectLit; 2966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node functionBlock = function.getLastChild(); 2976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if ((returnNode = functionBlock.getLastChild()) == null || 2986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) !returnNode.isReturn() || 2996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) (objectLit = returnNode.getFirstChild()) == null || 3006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) !objectLit.isObjectLit()) { 3016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) compiler.report(JSError.make(namespaceArg, CR_DEFINE_INVALID_RETURN_IN_FUNCTION)); 3026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 3036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Map<String, String> exports = objectLitToMap(objectLit); 3066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) NodeTraversal.traverse(compiler, functionBlock, new RenameInternalsToExternalsCallback( 3086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) namespace, exports, functionBlock)); 3096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private Map<String, String> objectLitToMap(Node objectLit) { 3126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Map<String, String> res = new HashMap<String, String>(); 3136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (Node keyNode : objectLit.children()) { 3156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) String key = keyNode.getString(); 3166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // TODO(vitalyp): Can dict value be other than a simple NAME? What if NAME doesn't 3186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // refer to a function/constructor? 3196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) String value = keyNode.getFirstChild().getString(); 3206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) res.put(value, key); 3226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return res; 3256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** 3286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * For a string "a.b.c" produce the following JS IR: 3296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * 3306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * <p><pre> 3316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * var a = a || {}; 3326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * a.b = a.b || {}; 3336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * a.b.c = a.b.c || {};</pre> 3346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 3356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private List<Node> createObjectsForQualifiedName(String namespace) { 3366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) List<Node> objects = new ArrayList<>(); 3376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) String[] parts = namespace.split("\\."); 3386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) createObjectIfNew(objects, parts[0], true); 3406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (parts.length >= 2) { 3426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) StringBuilder currPrefix = new StringBuilder().append(parts[0]); 3436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (int i = 1; i < parts.length; ++i) { 3446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) currPrefix.append(".").append(parts[i]); 3456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) createObjectIfNew(objects, currPrefix.toString(), false); 3466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return objects; 3506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private void createObjectIfNew(List<Node> objects, String name, boolean needVar) { 3536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!createdObjects.contains(name)) { 3546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) objects.add(createJsNode((needVar ? "var " : "") + name + " = " + name + " || {};")); 3556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) createdObjects.add(name); 3566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private Node createJsNode(String code) { 3606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // The parent node after parseSyntheticCode() is SCRIPT node, we need to get rid of it. 3616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return compiler.parseSyntheticCode(code).removeFirstChild(); 3626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private class RenameInternalsToExternalsCallback extends AbstractPostOrderCallback { 3656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private final String namespaceName; 3666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private final Map<String, String> exports; 3676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private final Node namespaceBlock; 3686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public RenameInternalsToExternalsCallback(String namespaceName, 3706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Map<String, String> exports, Node namespaceBlock) { 3716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.namespaceName = namespaceName; 3726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.exports = exports; 3736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.namespaceBlock = namespaceBlock; 3746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 3776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void visit(NodeTraversal t, Node n, Node parent) { 3786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (n.isFunction() && parent == this.namespaceBlock && 3796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.exports.containsKey(n.getFirstChild().getString())) { 3806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // It's a top-level function/constructor definition. 3816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 3826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Change 3836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 3846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // /** Some doc */ 3856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // function internalName() {} 3866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 3876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // to 3886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 3896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // /** Some doc */ 3906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // my.namespace.name.externalName = function internalName() {}; 3916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 3926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // by looking up in this.exports for internalName to find the correspondent 3936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // externalName. 3946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node functionTree = n.cloneTree(); 3956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node exprResult = IR.exprResult( 3966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) IR.assign(buildQualifiedName(n.getFirstChild()), functionTree).srcref(n) 3976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ).srcref(n); 3986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (n.getJSDocInfo() != null) { 4006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) exprResult.getFirstChild().setJSDocInfo(n.getJSDocInfo()); 4016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) functionTree.removeProp(Node.JSDOC_INFO_PROP); 4026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.namespaceBlock.replaceChild(n, exprResult); 4046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else if (n.isName() && this.exports.containsKey(n.getString()) && 4056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) !parent.isFunction()) { 4066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (parent.isVar()) { 4076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (parent.getParent() == this.namespaceBlock) { 40803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // It's a top-level exported variable definition (maybe without an 40903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // assignment). 4106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Change 4116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 4126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // var enum = { 'one': 1, 'two': 2 }; 4136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 4146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // to 4156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 4166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // my.namespace.name.enum = { 'one': 1, 'two': 2 }; 4176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node varContent = n.removeFirstChild(); 41803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Node exprResult; 41903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (varContent == null) { 42003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) exprResult = IR.exprResult(buildQualifiedName(n)).srcref(parent); 42103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } else { 42203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) exprResult = IR.exprResult( 42303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) IR.assign(buildQualifiedName(n), varContent).srcref(parent) 42403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) ).srcref(parent); 42503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } 4266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (parent.getJSDocInfo() != null) { 4276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) exprResult.getFirstChild().setJSDocInfo(parent.getJSDocInfo().clone()); 4286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.namespaceBlock.replaceChild(parent, exprResult); 4306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else { 4326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // It's a local name referencing exported entity. Change to its global name. 4336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Node newNode = buildQualifiedName(n); 4346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (n.getJSDocInfo() != null) { 4356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) newNode.setJSDocInfo(n.getJSDocInfo().clone()); 4366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // If we alter the name of a called function, then it gets an explicit "this" 4396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // value. 4406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (parent.isCall()) { 4416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) parent.putBooleanProp(Node.FREE_CALL, false); 4426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) parent.replaceChild(n, newNode); 4456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private Node buildQualifiedName(Node internalName) { 4506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) String externalName = this.exports.get(internalName.getString()); 4516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), 4526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.namespaceName + "." + externalName).srcrefTree(internalName); 4536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 456