private void handleFor(Node forNode) { if (forNode.getChildCount() == 4) { // We have for (init; cond; iter) { body } Node init = forNode.getFirstChild(); Node cond = init.getNext(); Node iter = cond.getNext(); Node body = iter.getNext(); // After initialization, we transfer to the FOR which is in charge of // checking the condition (for the first time). createEdge(init, Branch.UNCOND, forNode); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode)); // The end of the body will have a unconditional branch to our iter // (handled by calling computeFollowNode of the last instruction of the // body. Our iter will jump to the forNode again to another condition // check. createEdge(iter, Branch.UNCOND, forNode); connectToPossibleExceptionHandler(init, init); connectToPossibleExceptionHandler(forNode, cond); connectToPossibleExceptionHandler(iter, iter); } else { // We have for (item in collection) { body } Node item = forNode.getFirstChild(); Node collection = item.getNext(); Node body = collection.getNext(); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode)); connectToPossibleExceptionHandler(forNode, collection); } }
Node tryFuseStatementsAggressively(Node n) { if (!NodeUtil.isStatementBlock(n)) { return n; } Node cur = n.getFirstChild(); while (cur != null) { if (!cur.isExprResult()) { cur = cur.getNext(); continue; } Node next = cur.getNext(); while (next != null && next.isExprResult()) { next = next.getNext(); } if (cur.getNext() != next) { cur = fuseIntoOneStatement(n, cur, next); reportCodeChange(); } if (cur.isExprResult() && next != null && isFusableControlStatement(next)) { fuseExpressionIntoControlFlowStatement(cur, next); reportCodeChange(); next = next.getNext(); } cur = next; } return n; }
private void scanRoot(Node n) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } 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(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n); } }
private boolean matchesNodeShape(Node template, Node ast) { if (isTemplateParameterNode(template)) { // Match the entire expression but only if it is an expression. return !NodeUtil.isStatement(ast); } else if (isTemplateLocalNameNode(template)) { // Match any name. Maybe match locals here. if (!ast.isName()) { return false; } } else if (template.isCall()) { // Loosely match CALL nodes. isEquivalentToShallow checks free calls against non-free calls, // but the template should ignore that distinction. if (ast == null || !ast.isCall() || ast.getChildCount() != template.getChildCount()) { return false; } // But check any children. } else if (!template.isEquivalentToShallow(ast)) { return false; } // isEquivalentToShallow guarantees the child counts match Node templateChild = template.getFirstChild(); Node astChild = ast.getFirstChild(); while (templateChild != null) { if (!matchesNodeShape(templateChild, astChild)) { return false; } templateChild = templateChild.getNext(); astChild = astChild.getNext(); } return true; }
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); } }
/** Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body // ES6 "arrow" function may not have a block as a body. traverseBranch(body, n); popScope(); }
@Override public String toDebugHashCodeString() { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return super.toDebugHashCodeString(); } StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getDebugHashCodeStringOf(typeOfThis)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); while (p != null) { b.append(", "); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); } } b.append(")"); b.append(": "); b.append(getDebugHashCodeStringOf(call.returnType)); return b.toString(); }
/** * Notice that "call" and "bind" have the same argument signature, except that all the arguments * of "bind" (except the first) are optional. */ private FunctionType getCallOrBindSignature(boolean isCall) { boolean isBind = !isCall; FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(isCall ? getReturnType() : getBindReturnType(-1)) .withTemplateKeys(getTemplateTypeMap().getTemplateKeys()); Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType(registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); if (isBind) { // The arguments of bind() are unique in that they are all // optional but not undefinable. for (Node current = thisTypeNode.getNext(); current != null; current = current.getNext()) { current.setOptionalArg(true); } } else if (isCall) { // The first argument of call() is optional iff all the arguments // are optional. It's sufficient to check the first argument. Node firstArg = thisTypeNode.getNext(); if (firstArg == null || firstArg.isOptionalArg() || firstArg.isVarArgs()) { thisTypeNode.setOptionalArg(true); } } builder.withParamsNode(params); } return builder.build(); }
private void validateParametersEs6(Node n) { boolean defaultParams = false; for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.isRest()) { if (c.getNext() != null) { violation("Rest parameters must come after all other parameters.", c); } validateRest(c); } else if (c.isDefaultValue()) { defaultParams = true; validateDefaultValue(Token.PARAM_LIST, c); } else { if (defaultParams) { violation( "Cannot have a parameter without a default value," + " after one with a default value.", c); } if (c.isName()) { validateName(c); } else if (c.isArrayPattern()) { validateArrayPattern(Token.PARAM_LIST, c); } else { validateObjectPattern(Token.PARAM_LIST, c); } } } }
private void visitObjectWithComputedProperty(Node obj, Node parent) { Preconditions.checkArgument(obj.isObjectLit()); List<Node> props = new ArrayList<>(); Node currElement = obj.getFirstChild(); while (currElement != null) { if (currElement.getBooleanProp(Node.COMPUTED_PROP_GETTER) || currElement.getBooleanProp(Node.COMPUTED_PROP_SETTER)) { cannotConvertYet(currElement, "computed getter/setter"); return; } else if (currElement.isGetterDef() || currElement.isSetterDef()) { currElement = currElement.getNext(); } else { Node nextNode = currElement.getNext(); obj.removeChild(currElement); props.add(currElement); currElement = nextNode; } } String objName = FRESH_COMP_PROP_VAR + compiler.getUniqueNameIdSupplier().get(); props = Lists.reverse(props); Node result = IR.name(objName); for (Node propdef : props) { if (propdef.isComputedProp()) { Node propertyExpression = propdef.removeFirstChild(); Node value = propdef.removeFirstChild(); result = IR.comma(IR.assign(IR.getelem(IR.name(objName), propertyExpression), value), result); } else { if (!propdef.hasChildren()) { Node name = IR.name(propdef.getString()).useSourceInfoIfMissingFrom(propdef); propdef.addChildToBack(name); } Node val = propdef.removeFirstChild(); propdef.setType(Token.STRING); int type = propdef.isQuotedString() ? Token.GETELEM : Token.GETPROP; Node access = new Node(type, IR.name(objName), propdef); result = IR.comma(IR.assign(access, val), result); } } Node statement = obj; while (!NodeUtil.isStatement(statement)) { statement = statement.getParent(); } result.useSourceInfoIfMissingFromForTree(obj); parent.replaceChild(obj, result); Node var = IR.var(IR.name(objName), obj); var.useSourceInfoIfMissingFromForTree(statement); statement.getParent().addChildBefore(var, statement); compiler.reportCodeChange(); }
/** * Determines whether a function can be inlined at a particular call site. There are several * criteria that the function and reference must hold in order for the functions to be inlined: 1) * If a call's arguments have side effects, the corresponding argument in the function must only * be referenced once. For instance, this will not be inlined: * * <pre> * function foo(a) { return a + a } * x = foo(i++); * </pre> */ private CanInlineResult canInlineReferenceDirectly(Node callNode, Node fnNode) { if (!isDirectCallNodeReplacementPossible(fnNode)) { return CanInlineResult.NO; } Node block = fnNode.getLastChild(); // CALL NODE: [ NAME, ARG1, ARG2, ... ] Node cArg = callNode.getFirstChild().getNext(); // Functions called via 'call' and 'apply' have a this-object as // the first parameter, but this is not part of the called function's // parameter list. if (callNode.getFirstChild().getType() != Token.NAME) { if (NodeUtil.isFunctionObjectCall(callNode)) { // TODO(johnlenz): Support replace this with a value. Preconditions.checkNotNull(cArg); Preconditions.checkState(cArg.getType() == Token.THIS); cArg = cArg.getNext(); } else { // ".apply" call should be filtered before this. Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode)); } } // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ] Node fnParam = NodeUtil.getFnParameters(fnNode).getFirstChild(); while (cArg != null || fnParam != null) { // For each named parameter check if a mutable argument use more than one. if (fnParam != null) { if (cArg != null) { // Check for arguments that are evaluated more than once. // Note: Unlike block inlining, there it is not possible that a // parameter reference will be in a loop. if (NodeUtil.mayEffectMutableState(cArg) && NodeUtil.getNameReferenceCount(block, fnParam.getString()) > 1) { return CanInlineResult.NO; } } // Move to the next name. fnParam = fnParam.getNext(); } // For every call argument check for side-effects, even if there // isn't a named parameter to match. if (cArg != null) { if (NodeUtil.mayHaveSideEffects(cArg)) { return CanInlineResult.NO; } cArg = cArg.getNext(); } } return CanInlineResult.YES; }
private boolean matchesTemplate(Node template, Node ast) { while (template != null) { if (ast == null || !matchesNode(template, ast)) { return false; } template = template.getNext(); ast = ast.getNext(); } return true; }
/** 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; } } }
/** * Infer the parameter and return types of a function from the parameter and return types of the * function it is overriding. * * @param oldType The function being overridden. Does nothing if this is null. * @param paramsParent The LP node of the function that we're assigning to. If null, that just * means we're not initializing this to a function literal. */ FunctionTypeBuilder inferFromOverriddenFunction( @Nullable FunctionType oldType, @Nullable Node paramsParent) { if (oldType == null) { return this; } returnType = oldType.getReturnType(); returnTypeInferred = oldType.isReturnTypeInferred(); if (paramsParent == null) { // Not a function literal. parametersNode = oldType.getParametersNode(); if (parametersNode == null) { parametersNode = new FunctionParamBuilder(typeRegistry).build(); } } else { // We're overriding with a function literal. Apply type information // to each parameter of the literal. FunctionParamBuilder paramBuilder = new FunctionParamBuilder(typeRegistry); Iterator<Node> oldParams = oldType.getParameters().iterator(); boolean warnedAboutArgList = false; boolean oldParamsListHitOptArgs = false; for (Node currentParam = paramsParent.getFirstChild(); currentParam != null; currentParam = currentParam.getNext()) { if (oldParams.hasNext()) { Node oldParam = oldParams.next(); Node newParam = paramBuilder.newParameterFromNode(oldParam); oldParamsListHitOptArgs = oldParamsListHitOptArgs || oldParam.isVarArgs() || oldParam.isOptionalArg(); // The subclass method might right its var_args as individual // arguments. if (currentParam.getNext() != null && newParam.isVarArgs()) { newParam.setVarArgs(false); newParam.setOptionalArg(true); } } else { warnedAboutArgList |= addParameter( paramBuilder, typeRegistry.getNativeType(UNKNOWN_TYPE), warnedAboutArgList, codingConvention.isOptionalParameter(currentParam) || oldParamsListHitOptArgs, codingConvention.isVarArgsParameter(currentParam)); } } parametersNode = paramBuilder.build(); } return this; }
private void fillInFunTypeBuilder( Node jsdocNode, RawNominalType ownerType, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters, FunctionTypeBuilder builder) throws UnknownTypeException { Node child = jsdocNode.getFirstChild(); if (child.getType() == Token.THIS) { if (ownerType == null) { builder.addReceiverType(getThisOrNewType(child.getFirstChild(), registry, typeParameters)); } child = child.getNext(); } else if (child.getType() == Token.NEW) { Node newTypeNode = child.getFirstChild(); JSType t = getThisOrNewType(newTypeNode, registry, typeParameters); if (!t.isSubtypeOf(JSType.TOP_OBJECT) && (!t.hasTypeVariable() || t.hasScalar())) { warnings.add(JSError.make(newTypeNode, NEW_EXPECTS_OBJECT_OR_TYPEVAR, t.toString())); } builder.addNominalType(t); child = child.getNext(); } if (child.getType() == Token.PARAM_LIST) { for (Node arg = child.getFirstChild(); arg != null; arg = arg.getNext()) { try { switch (arg.getType()) { case Token.EQUALS: builder.addOptFormal( getTypeFromCommentHelper(arg.getFirstChild(), registry, typeParameters)); break; case Token.ELLIPSIS: Node restNode = arg.getFirstChild(); builder.addRestFormals( restNode == null ? JSType.UNKNOWN : getTypeFromCommentHelper(restNode, registry, typeParameters)); break; default: builder.addReqFormal(getTypeFromCommentHelper(arg, registry, typeParameters)); break; } } catch (FunctionTypeBuilder.WrongParameterOrderException e) { warnings.add(JSError.make(jsdocNode, WRONG_PARAMETER_ORDER)); builder.addPlaceholderFormal(); } } child = child.getNext(); } builder.addRetType(getTypeFromCommentHelper(child, registry, typeParameters)); }
public void testLinenoCharnoObjectLiteral() throws Exception { Node n = parse("\n\n var a = {a:0\n,b :1};").getFirstChild().getFirstChild().getFirstChild(); assertEquals(Token.OBJECTLIT, n.getType()); assertEquals(3, n.getLineno()); assertEquals(9, n.getCharno()); Node key = n.getFirstChild(); assertEquals(Token.STRING_KEY, key.getType()); assertEquals(3, key.getLineno()); assertEquals(10, key.getCharno()); Node value = key.getFirstChild(); assertEquals(Token.NUMBER, value.getType()); assertEquals(3, value.getLineno()); assertEquals(12, value.getCharno()); key = key.getNext(); assertEquals(Token.STRING_KEY, key.getType()); assertEquals(4, key.getLineno()); assertEquals(1, key.getCharno()); value = key.getFirstChild(); assertEquals(Token.NUMBER, value.getType()); assertEquals(4, value.getLineno()); assertEquals(4, value.getCharno()); }
private void addStatement(GenerateNodeContext context, Node stmt) { CodingConvention convention = compiler.getCodingConvention(); Node n = context.getNode(); Node exprRoot = n; while (!NodeUtil.isStatementBlock(exprRoot.getParent())) { exprRoot = exprRoot.getParent(); } // It's important that any class-building calls (goog.inherits) // come right after the class definition, so move the export after that. while (true) { Node next = exprRoot.getNext(); if (next != null && NodeUtil.isExprCall(next) && convention.getClassesDefinedByCall(next.getFirstChild()) != null) { exprRoot = next; } else { break; } } Node block = exprRoot.getParent(); block.addChildAfter(stmt, exprRoot); }
/** Traverses a parse tree recursively with a scope, starting at that scope's root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.isFunction()) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. if (inputId == null) { inputId = NodeUtil.getInputId(n); } sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else if (n.isBlock()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); } sourceName = getSourceName(n); curNode = n; pushScope(s); traverseBranch(n, n.getParent()); popScope(); } else { Preconditions.checkState(s.isGlobal(), "Expected global scope. Got:", s); traverseWithScope(n, s); } }
/** Traverses a branch. */ private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) { return; } if (type == Token.FUNCTION) { traverseFunction(n, parent); } else if (useBlockScope && NodeUtil.createsBlockScope(n)) { traverseBlockScope(n); } else { for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } } curNode = n; callback.visit(this, n, parent); }
/** @return Whether the name is used in a way that might be a candidate for inlining. */ static boolean isCandidateUsage(Node name) { Node parent = name.getParent(); Preconditions.checkState(name.isName()); if (parent.isVar() || parent.isFunction()) { // This is a declaration. Duplicate declarations are handle during // function candidate gathering. return true; } if (parent.isCall() && parent.getFirstChild() == name) { // This is a normal reference to the function. return true; } // Check for a ".call" to the named function: // CALL // GETPROP/GETELEM // NAME // STRING == "call" // This-Value // Function-parameter-1 // ... if (NodeUtil.isGet(parent) && name == parent.getFirstChild() && name.getNext().isString() && name.getNext().getString().equals("call")) { Node gramps = name.getAncestor(2); if (gramps.isCall() && gramps.getFirstChild() == parent) { // Yep, a ".call". return true; } } return false; }
/** Like Node.getNext, that null is used to signal the child before the block. */ private static Node childAfter(Node parent, @Nullable Node siblingBefore) { if (siblingBefore == null) { return parent.getFirstChild(); } else { return siblingBefore.getNext(); } }
private void tryConvertOperandsToNumber(Node n) { Node next; for (Node c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); tryConvertToNumber(c); } }
void visitSubtree(Node n, Node parent) { if (n.isCall()) { boolean isModuleDetected = codingConvention.extractIsModuleFile(n, parent); if (isModuleDetected) { this.isModuleFile = true; } String require = codingConvention.extractClassNameIfRequire(n, parent); if (require != null) { requires.add(require); } String provide = codingConvention.extractClassNameIfProvide(n, parent); if (provide != null) { provides.add(provide); } return; } else if (parent != null && !parent.isExprResult() && !parent.isScript()) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } }
/** Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { Node child = n.getFirstChild(); while (child != null) { // Maybe STRING, NUMBER, GET, SET if (child.getType() != Token.NUMBER) { // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { if (showInvalidationWarnings) { compiler.report( JSError.make( t.getSourceName(), child, INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString())); } } } child = child.getNext(); } }
private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } }
/** * Expand a jQuery.expandedEach call * * <p>Expanded jQuery.expandedEach calls will replace the GETELEM nodes of a property assignment * with GETPROP nodes to allow for renaming. */ private void maybeExpandJqueryEachCall(NodeTraversal t, Node n) { Node objectToLoopOver = n.getChildAtIndex(1); if (objectToLoopOver == null) { return; } Node callbackFunction = objectToLoopOver.getNext(); if (callbackFunction == null || !callbackFunction.isFunction()) { return; } // Run the peephole optimizations on the first argument to handle // cases like ("a " + "b").split(" ") peepholePasses.process(null, n.getChildAtIndex(1)); // Create a reference tree Node nClone = n.cloneTree(); objectToLoopOver = nClone.getChildAtIndex(1); // Check to see if the first argument is something we recognize and can // expand. if (!objectToLoopOver.isObjectLit() && !(objectToLoopOver.isArrayLit() && isArrayLitValidForExpansion(objectToLoopOver))) { t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR, (String) null); return; } // Find all references to the callback function arguments List<Node> keyNodeReferences = new ArrayList<>(); List<Node> valueNodeReferences = new ArrayList<>(); new NodeTraversal( compiler, new FindCallbackArgumentReferences( callbackFunction, keyNodeReferences, valueNodeReferences, objectToLoopOver.isArrayLit())) .traverseInnerNode( NodeUtil.getFunctionBody(callbackFunction), callbackFunction, t.getScope()); if (keyNodeReferences.isEmpty()) { // We didn't do anything useful ... t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String) null); return; } Node fncBlock = tryExpandJqueryEachCall( t, nClone, callbackFunction, keyNodeReferences, valueNodeReferences); if (fncBlock != null && fncBlock.hasChildren()) { replaceOriginalJqueryEachCall(n, fncBlock); } else { // We didn't do anything useful ... t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String) null); } }
@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 JSType getRecordTypeHelper( Node n, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters) throws UnknownTypeException { Map<String, Property> props = new LinkedHashMap<>(); for (Node propNode = n.getFirstFirstChild(); propNode != null; propNode = propNode.getNext()) { boolean isPropDeclared = propNode.getType() == Token.COLON; Node propNameNode = isPropDeclared ? propNode.getFirstChild() : propNode; String propName = propNameNode.getString(); if (propName.startsWith("'") || propName.startsWith("\"")) { propName = propName.substring(1, propName.length() - 1); } JSType propType = !isPropDeclared ? JSType.UNKNOWN : getTypeFromCommentHelper(propNode.getLastChild(), registry, typeParameters); Property prop; if (propType.equals(JSType.UNDEFINED) || isUnionWithUndefined(propNode.getLastChild())) { prop = Property.makeOptional(null, propType, propType); } else { prop = Property.make(propType, propType); } props.put(propName, prop); } return JSType.fromObjectType(ObjectType.fromProperties(props)); }
static ClassDeclarationMetadata create(Node classNode, Node parent) { Node classNameNode = classNode.getFirstChild(); Node superClassNameNode = classNameNode.getNext(); // If this is a class statement, or a class expression in a simple // assignment or var statement, convert it. In any other case, the // code is too dynamic, so return null. if (NodeUtil.isStatement(classNode)) { return new ClassDeclarationMetadata( classNode, classNameNode.getString(), false, classNameNode, superClassNameNode); } else if (parent.isAssign() && parent.getParent().isExprResult()) { // Add members after the EXPR_RESULT node: // example.C = class {}; example.C.prototype.foo = function() {}; String fullClassName = parent.getFirstChild().getQualifiedName(); if (fullClassName == null) { return null; } return new ClassDeclarationMetadata( parent.getParent(), fullClassName, true, classNameNode, superClassNameNode); } else if (parent.isName()) { // Add members after the 'var' statement. // var C = class {}; C.prototype.foo = function() {}; return new ClassDeclarationMetadata( parent.getParent(), parent.getString(), true, classNameNode, superClassNameNode); } else { // Cannot handle this class declaration. return null; } }
/** Checks that object literal keys or class method names are valid. */ private static void checkObjectLiteralOrClass(NodeTraversal t, Node n) { Set<String> getters = new HashSet<>(); Set<String> setters = new HashSet<>(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!key.isSetterDef()) { // normal property and getter cases if (!getters.add(key.getString())) { if (n.isClassMembers()) { t.report(key, DUPLICATE_CLASS_METHODS); } else { t.report(key, DUPLICATE_OBJECT_KEY); } } } if (!key.isGetterDef()) { // normal property and setter cases if (!setters.add(key.getString())) { if (n.isClassMembers()) { t.report(key, DUPLICATE_CLASS_METHODS); } else { t.report(key, DUPLICATE_OBJECT_KEY); } } } } }