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();
  }
Example #9
0
  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();
      }
    }
Example #25
0
 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);
         }
       }
     }
   }
 }