@Override public void visit(NodeTraversal t, Node n, Node parent) { // Verify the source file is annotated. if (doSanityChecks && sourceFile != null) { Preconditions.checkState(sourceFile.equals(n.getProp(Node.SOURCENAME_PROP))); } // Annotate the original name. switch (n.getType()) { case Token.GETPROP: Node propNode = n.getLastChild(); setOriginalName(n, propNode.getString()); break; case Token.FUNCTION: String functionName = NodeUtil.getNearestFunctionName(n); if (functionName != null) { setOriginalName(n, functionName); } break; case Token.NAME: setOriginalName(n, n.getString()); break; case Token.OBJECTLIT: for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { // We only want keys were unquoted. if (!key.isQuotedString()) { setOriginalName(key, key.getString()); } } break; } }
private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } }
@Test public void canUseModuleInternalTypedefsInJsDoc() { CompilerUtil compiler = createCompiler(path("foo.js")); compiler.compile( createSourceFile( path("foo.js"), "/** @typedef {{x: number}} */", "var Variable;", "", "/**", " * @param {Variable} a .", " * @param {Variable} b .", " * @return {Variable} .", " */", "exports.add = function(a, b) {", " return {x: a.x + b.x};", "};")); Scope scope = compiler.getCompiler().getTopScope(); Var var = scope.getVar("module$foo"); JSType type = var.getInitialValue().getJSType().toObjectType().getPropertyType("add"); assertTrue(type.isFunctionType()); JSDocInfo info = type.getJSDocInfo(); Node node = info.getTypeNodes().iterator().next(); assertTrue(node.isString()); assertEquals("$jscomp.scope.Variable", node.getString()); assertEquals("Variable", node.getProp(Node.ORIGINALNAME_PROP)); }
@Test public void savesOriginalTypeNameInJsDoc() { CompilerUtil compiler = createCompiler(path("foo.js")); compiler.compile( createSourceFile( path("foo.js"), "/** @constructor */", "var Builder = function(){};", "/** @return {!Builder} . */", "Builder.prototype.returnThis = function() { return this; };", "exports.Builder = Builder")); Scope scope = compiler.getCompiler().getTopScope(); Var var = scope.getVar("module$foo"); JSType type = var.getInitialValue().getJSType().findPropertyType("Builder"); assertTrue(type.isConstructor()); type = type.toObjectType().getTypeOfThis(); assertEquals("$jscomp.scope.Builder", type.toString()); type = type.toObjectType().getPropertyType("returnThis"); assertTrue(type.toString(), type.isFunctionType()); JSDocInfo info = type.getJSDocInfo(); assertNotNull(info); Node node = getOnlyElement(info.getTypeNodes()); assertEquals(Token.BANG, node.getType()); node = node.getFirstChild(); assertTrue(node.isString()); assertEquals("$jscomp.scope.Builder", node.getString()); assertEquals("Builder", node.getProp(Node.ORIGINALNAME_PROP)); }
public void testPreserveAnnotatedName() { Node root = new Node(Token.SCRIPT); Node name = Node.newString("foo"); name.putProp(Node.ORIGINALNAME_PROP, "bar"); root.addChildToBack(name); NodeTraversal.traverseEs6(new Compiler(), root, new SourceInformationAnnotator("", false)); assertEquals(name.getProp(Node.ORIGINALNAME_PROP), "bar"); }
/** Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null; ) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString(); declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null; ) { Node next = child.getNext(); scanVars(child, n); child = next; } } }
void setOriginalName(Node n, String name) { if (!name.isEmpty() && n.getProp(Node.ORIGINALNAME_PROP) == null) { n.putProp(Node.ORIGINALNAME_PROP, name); } }