private void handleReturn(Node node) {
    Node lastJump = null;
    for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext(); ) {
      Node curHandler = iter.next();
      if (NodeUtil.isFunction(curHandler)) {
        break;
      }
      if (NodeUtil.hasFinally(curHandler)) {
        if (lastJump == null) {
          createEdge(node, Branch.UNCOND, curHandler.getLastChild());
        } else {
          finallyMap.put(lastJump, computeFallThrough(curHandler.getLastChild()));
        }
        lastJump = curHandler;
      }
    }

    if (node.hasChildren()) {
      connectToPossibleExceptionHandler(node, node.getFirstChild());
    }

    if (lastJump == null) {
      createEdge(node, Branch.UNCOND, null);
    } else {
      finallyMap.put(lastJump, null);
    }
  }
 private boolean isFusableControlStatement(Node n) {
   switch (n.getToken()) {
     case IF:
     case THROW:
     case SWITCH:
     case EXPR_RESULT:
       return true;
     case RETURN:
       // We don't want to add a new return value.
       return n.hasChildren();
     case FOR:
       if (NodeUtil.isForIn(n)) {
         // Avoid cases where we have for(var x = foo() in a) { ....
         return !mayHaveSideEffects(n.getFirstChild());
       } else {
         // Avoid cases where we have for(var x;_;_) { ....
         return !n.getFirstChild().isVar();
       }
     case LABEL:
       return isFusableControlStatement(n.getLastChild());
     case BLOCK:
       return !n.isSyntheticBlock() && isFusableControlStatement(n.getFirstChild());
     default:
       break;
   }
   return false;
 }
Пример #3
0
 private void validateContinue(Node n) {
   validateNodeType(Token.CONTINUE, n);
   validateMaximumChildCount(n, 1);
   if (n.hasChildren()) {
     validateLabelName(n.getFirstChild());
   }
 }
Пример #4
0
  private void validateTry(Node n) {
    validateNodeType(Token.TRY, n);
    validateChildCountIn(n, 2, 3);
    validateBlock(n.getFirstChild());

    boolean seenCatchOrFinally = false;

    // Validate catch
    Node catches = n.getChildAtIndex(1);
    validateNodeType(Token.BLOCK, catches);
    validateMaximumChildCount(catches, 1);
    if (catches.hasChildren()) {
      validateCatch(catches.getFirstChild());
      seenCatchOrFinally = true;
    }

    // Validate finally
    if (n.getChildCount() == 3) {
      validateBlock(n.getLastChild());
      seenCatchOrFinally = true;
    }

    if (!seenCatchOrFinally) {
      violation("Missing catch or finally for try statement.", n);
    }
  }
Пример #5
0
 private void validateBreak(Node n) {
   validateNodeType(Token.BREAK, n);
   validateMaximumChildCount(n, 1);
   if (n.hasChildren()) {
     validateLabelName(n.getFirstChild());
   }
 }
Пример #6
0
 private void validateReturn(Node n) {
   validateNodeType(Token.RETURN, n);
   validateMaximumChildCount(n, 1);
   if (n.hasChildren()) {
     validateExpression(n.getFirstChild());
   }
 }
  private void handleContinue(Node node) {
    String label = null;
    if (node.hasChildren()) {
      label = node.getFirstChild().getString();
    }
    Node cur;
    Node lastJump;
    // Similar to handBreak's logic with a few minor variation.
    Node parent = node.getParent();
    for (cur = node, lastJump = node;
        !isContinueTarget(cur, parent, label);
        cur = parent, parent = parent.getParent()) {
      if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) {
        if (lastJump == node) {
          createEdge(lastJump, Branch.UNCOND, cur.getLastChild());
        } else {
          finallyMap.put(lastJump, computeFallThrough(cur.getLastChild()));
        }
        lastJump = cur;
      }
      Preconditions.checkState(parent != null, "Cannot find continue target.");
    }
    Node iter = cur;
    if (cur.getChildCount() == 4) {
      iter = cur.getFirstChild().getNext().getNext();
    }

    if (lastJump == node) {
      createEdge(node, Branch.UNCOND, iter);
    } else {
      finallyMap.put(lastJump, iter);
    }
  }
  /**
   * Inline a function that fulfills the requirements of canInlineReferenceDirectly into the call
   * site, replacing only the CALL node.
   */
  private Node inlineReturnValue(Node callNode, Node fnNode) {
    Node block = fnNode.getLastChild();
    Node callParentNode = callNode.getParent();

    // NOTE: As the normalize pass guarantees globals aren't being
    // shadowed and an expression can't introduce new names, there is
    // no need to check for conflicts.

    // Create an argName -> expression map, checking for side effects.
    Map<String, Node> argMap =
        FunctionArgumentInjector.getFunctionCallParameterMap(
            fnNode, callNode, this.safeNameIdSupplier);

    Node newExpression;
    if (!block.hasChildren()) {
      Node srcLocation = block;
      newExpression = NodeUtil.newUndefinedNode(srcLocation);
    } else {
      Node returnNode = block.getFirstChild();
      Preconditions.checkArgument(returnNode.getType() == Token.RETURN);

      // Clone the return node first.
      Node safeReturnNode = returnNode.cloneTree();
      Node inlineResult = FunctionArgumentInjector.inject(safeReturnNode, null, argMap);
      Preconditions.checkArgument(safeReturnNode == inlineResult);
      newExpression = safeReturnNode.removeFirstChild();
    }

    callParentNode.replaceChild(callNode, newExpression);
    return newExpression;
  }
Пример #9
0
  /** For each node, update the block stack and reference collection as appropriate. */
  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
    if (n.isName()
        || n.isRest()
        || (n.isStringKey() && parent.isObjectPattern() && !n.hasChildren())) {
      Var v;
      if (n.getString().equals("arguments")) {
        v = t.getScope().getArgumentsVar();
      } else {
        v = t.getScope().getVar(n.getString());
      }

      if (v != null) {
        if (varFilter.apply(v)) {
          addReference(v, new Reference(n, t, peek(blockStack)));
        }

        if (v.getParentNode() != null
            && NodeUtil.isHoistedFunctionDeclaration(v.getParentNode())
            &&
            // If we're only traversing a narrow scope, do not try to climb outside.
            (narrowScope == null || narrowScope.getDepth() <= v.getScope().getDepth())) {
          outOfBandTraversal(v);
        }
      }
    }

    if (isBlockBoundary(n, parent)) {
      pop(blockStack);
    }
  }
 /**
  * Gets whether a node is a CALL node whose return value should be stripped. A call's return
  * value should be stripped if the function getting called is a static method in a class that
  * gets stripped. For example, if "goog.debug.Logger" is a strip name, then this function
  * returns true for a call such as "goog.debug.Logger.getLogger(...)". It may also simply be a
  * function that is getting stripped. For example, if "getLogger" is a strip name, but not
  * "goog.debug.Logger", this will still return true.
  *
  * @param n A node (typically a CALL node)
  * @return Whether the call's return value should be stripped
  */
 boolean isCallWhoseReturnValueShouldBeStripped(@Nullable Node n) {
   return n != null
       && (n.getType() == Token.CALL || n.getType() == Token.NEW)
       && n.hasChildren()
       && (qualifiedNameBeginsWithStripType(n.getFirstChild())
           || nameEndsWithFieldNameToStrip(n.getFirstChild()));
 }
  /**
   * 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);
    }
  }
Пример #12
0
    public void findNamedFunctions(NodeTraversal t, Node n, Node parent) {
      if (!NodeUtil.isStatement(n)) {
        // There aren't any interesting functions here.
        return;
      }

      switch (n.getType()) {
          // Functions expressions in the form of:
          //   var fooFn = function(x) { return ... }
        case Token.VAR:
          Preconditions.checkState(n.hasOneChild());
          Node nameNode = n.getFirstChild();
          if (nameNode.isName()
              && nameNode.hasChildren()
              && nameNode.getFirstChild().isFunction()) {
            maybeAddFunction(new FunctionVar(n), t.getModule());
          }
          break;

          // Named functions
          // function Foo(x) { return ... }
        case Token.FUNCTION:
          Preconditions.checkState(NodeUtil.isStatementBlock(parent) || parent.isLabel());
          if (!NodeUtil.isFunctionExpression(n)) {
            Function fn = new NamedFunction(n);
            maybeAddFunction(fn, t.getModule());
          }
          break;
      }
    }
 private JSType getNamedTypeHelper(
     Node n, DeclaredTypeRegistry registry, ImmutableList<String> outerTypeParameters)
     throws UnknownTypeException {
   String typeName = n.getString();
   switch (typeName) {
     case "boolean":
       return JSType.BOOLEAN;
     case "null":
       return JSType.NULL;
     case "number":
       return JSType.NUMBER;
     case "string":
       return JSType.STRING;
     case "undefined":
     case "void":
       return JSType.UNDEFINED;
     case "Function":
       return maybeMakeNullable(registry.getCommonTypes().qmarkFunction());
     case "Object":
       // We don't generally handle parameterized Object<...>, but we want to
       // at least not warn about inexistent properties on it, so we type it
       // as @dict.
       return maybeMakeNullable(n.hasChildren() ? JSType.TOP_DICT : JSType.TOP_OBJECT);
     default:
       return lookupTypeByName(typeName, n, registry, outerTypeParameters);
   }
 }
Пример #14
0
 private void validateYield(Node n) {
   validateEs6Feature("yield", n);
   validateNodeType(Token.YIELD, n);
   validateChildCountIn(n, 0, 1);
   if (n.hasChildren()) {
     validateExpression(n.getFirstChild());
   }
 }
Пример #15
0
 /** Converts extended object literal {a} to {a:a}. */
 private void visitStringKey(Node n) {
   if (!n.hasChildren()) {
     Node name = IR.name(n.getString());
     name.copyInformationFrom(n);
     n.addChildToBack(name);
     compiler.reportCodeChange();
   }
 }
 /** Converts extended object literal {a} to {a:a}. */
 private void visitStringKey(Node n) {
   if (!n.hasChildren()) {
     Node name = IR.name(n.getString());
     name.useSourceInfoIfMissingFrom(n);
     n.addChildToBack(name);
     compiler.reportCodeChange();
   }
 }
 void replaceNodeInPlace(Node n, Node replacement) {
   Node parent = n.getParent();
   if (n.hasChildren()) {
     Node children = n.removeChildren();
     replacement.addChildrenToFront(children);
   }
   parent.replaceChild(n, replacement);
 }
 private JSType getNominalTypeHelper(
     RawNominalType rawType,
     Node n,
     DeclaredTypeRegistry registry,
     ImmutableList<String> outerTypeParameters)
     throws UnknownTypeException {
   NominalType uninstantiated = rawType.getAsNominalType();
   if (!rawType.isGeneric() && !n.hasChildren()) {
     return rawType.getInstanceWithNullability(NULLABLE_TYPES_BY_DEFAULT);
   }
   ImmutableList.Builder<JSType> typeList = ImmutableList.builder();
   if (n.hasChildren()) {
     // Compute instantiation of polymorphic class/interface.
     Preconditions.checkState(n.getFirstChild().isBlock(), n);
     for (Node child : n.getFirstChild().children()) {
       typeList.add(getTypeFromCommentHelper(child, registry, outerTypeParameters));
     }
   }
   ImmutableList<JSType> typeArguments = typeList.build();
   ImmutableList<String> typeParameters = rawType.getTypeParameters();
   int typeArgsSize = typeArguments.size();
   int typeParamsSize = typeParameters.size();
   if (typeArgsSize != typeParamsSize) {
     // We used to also warn when (typeArgsSize < typeParamsSize), but it
     // happens so often that we stopped. Array, Object and goog.Promise are
     // common culprits, but many other types as well.
     if (typeArgsSize > typeParamsSize) {
       warnings.add(
           JSError.make(
               n,
               INVALID_GENERICS_INSTANTIATION,
               uninstantiated.getName(),
               String.valueOf(typeParamsSize),
               String.valueOf(typeArgsSize)));
     }
     return maybeMakeNullable(
         JSType.fromObjectType(
             ObjectType.fromNominalType(
                 uninstantiated.instantiateGenerics(
                     fixLengthOfTypeList(typeParameters.size(), typeArguments)))));
   }
   return maybeMakeNullable(
       JSType.fromObjectType(
           ObjectType.fromNominalType(uninstantiated.instantiateGenerics(typeArguments))));
 }
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (NodeUtil.isVarDeclaration(n) && removable.contains(n.getString())) {
     parent.removeChild(n);
     if (!parent.hasChildren()) {
       parent.getParent().removeChild(parent);
     }
   }
 }
 private boolean hasShorthandAssignment(Node objLit) {
   Preconditions.checkState(objLit.isObjectLit());
   for (Node property : objLit.children()) {
     if (property.isStringKey() && !property.hasChildren()) {
       return true;
     }
   }
   return false;
 }
  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();
  }
  private static void reportIfWasEmpty(NodeTraversal t, Node block) {
    Preconditions.checkState(block.isBlock());

    // A semicolon is distinguished from a block without children by
    // annotating it with EMPTY_BLOCK.  Blocks without children are
    // usually intentional, especially with loops.
    if (!block.hasChildren() && block.isAddedBlock()) {
      t.getCompiler().report(t.makeError(block, SUSPICIOUS_SEMICOLON));
    }
  }
  /** @return The difference between the function definition cost and inline cost. */
  private static int inlineCostDelta(Node fnNode, Set<String> namesToAlias, InliningMode mode) {
    // The part of the function that is never inlined:
    //    "function xx(xx,xx){}" (15 + (param count * 3) -1;
    int paramCount = NodeUtil.getFnParameters(fnNode).getChildCount();
    int commaCount = (paramCount > 1) ? paramCount - 1 : 0;
    int costDeltaFunctionOverhead =
        15 + commaCount + (paramCount * InlineCostEstimator.ESTIMATED_IDENTIFIER_COST);

    Node block = fnNode.getLastChild();
    if (!block.hasChildren()) {
      // Assume the inline cost is zero for empty functions.
      return -costDeltaFunctionOverhead;
    }

    if (mode == InliningMode.DIRECT) {
      // The part of the function that is inlined using direct inlining:
      //    "return " (7)
      return -(costDeltaFunctionOverhead + 7);
    } else {
      int aliasCount = namesToAlias.size();

      // Originally, we estimated purely base on the function code size, relying
      // on later optimizations. But that did not produce good results, so here
      // we try to estimate the something closer to the actual inlined coded.

      // NOTE 1: Result overhead is only if there is an assignment, but
      // getting that information would require some refactoring.
      // NOTE 2: The aliasing overhead is currently an under-estimate,
      // as some parameters are aliased because of the parameters used.
      // Perhaps we should just assume all parameters will be aliased?
      final int INLINE_BLOCK_OVERHEAD = 4; // "X:{}"
      final int PER_RETURN_OVERHEAD = 2; // "return" --> "break X"
      final int PER_RETURN_RESULT_OVERHEAD = 3; // "XX="
      final int PER_ALIAS_OVERHEAD = 3; // "XX="

      // TODO(johnlenz): Counting the number of returns is relatively expensive
      //   this information should be determined during the traversal and
      //   cached.
      int returnCount =
          NodeUtil.getNodeTypeReferenceCount(
              block, Token.RETURN, new NodeUtil.MatchShallowStatement());
      int resultCount = (returnCount > 0) ? returnCount - 1 : 0;
      int baseOverhead = (returnCount > 0) ? INLINE_BLOCK_OVERHEAD : 0;

      int overhead =
          baseOverhead
              + returnCount * PER_RETURN_OVERHEAD
              + resultCount * PER_RETURN_RESULT_OVERHEAD
              + aliasCount * PER_ALIAS_OVERHEAD;

      return (overhead - costDeltaFunctionOverhead);
    }
  }
Пример #24
0
  private void validateMinimumChildCount(Node n, int i) {
    boolean valid = false;
    if (i == 1) {
      valid = n.hasChildren();
    } else if (i == 2) {
      valid = n.hasMoreThanOneChild();
    } else {
      valid = n.getChildCount() >= i;
    }

    if (!valid) {
      violation("Expected at least " + i + " children, but was " + n.getChildCount(), n);
    }
  }
  /** Remove useless calls: Object.defineProperties(o, {}) -> o */
  private Node tryFoldCall(Node n) {
    Preconditions.checkArgument(n.isCall());

    if (NodeUtil.isObjectDefinePropertiesDefinition(n)) {
      Node srcObj = n.getLastChild();
      if (srcObj.isObjectLit() && !srcObj.hasChildren()) {
        Node parent = n.getParent();
        Node destObj = n.getSecondChild().detachFromParent();
        parent.replaceChild(n, destObj);
        reportCodeChange();
      }
    }
    return n;
  }
  /**
   * Build parameter and local information for the template and replace the references in the
   * template 'fn' with placeholder nodes use to facility matching.
   */
  private void prepTemplatePlaceholders(Node fn) {
    final List<String> locals = templateLocals;
    final List<String> params = templateParams;
    final Map<String, JSType> paramTypes = new HashMap<>();

    // drop the function name so it isn't include in the name maps
    String fnName = fn.getFirstChild().getString();
    fn.getFirstChild().setString("");

    // Build a list of parameter names and types.
    Node templateParametersNode = fn.getSecondChild();
    JSDocInfo info = NodeUtil.getBestJSDocInfo(fn);
    if (templateParametersNode.hasChildren()) {
      Preconditions.checkNotNull(
          info, "Missing JSDoc declaration for template function %s", fnName);
    }
    for (Node paramNode : templateParametersNode.children()) {
      String name = paramNode.getString();
      JSTypeExpression expression = info.getParameterType(name);
      Preconditions.checkNotNull(
          expression, "Missing JSDoc for parameter %s of template function %s", name, fnName);
      JSType type = expression.evaluate(null, compiler.getTypeIRegistry());
      Preconditions.checkNotNull(type);
      params.add(name);
      paramTypes.put(name, type);
    }

    // Find references to local variables and parameters and replace them.
    traverse(
        fn,
        new Visitor() {
          @Override
          public void visit(Node n) {
            if (n.isName()) {
              Node parent = n.getParent();
              String name = n.getString();
              if (!name.isEmpty() && parent.isVar() && !locals.contains(name)) {
                locals.add(n.getString());
              }

              if (params.contains(name)) {
                JSType type = paramTypes.get(name);
                replaceNodeInPlace(n, createTemplateParameterNode(params.indexOf(name), type));
              } else if (locals.contains(name)) {
                replaceNodeInPlace(n, createTemplateLocalNameNode(locals.indexOf(name)));
              }
            }
          }
        });
  }
Пример #27
0
 private void validateObjectLitGetKey(Node n) {
   validateNodeType(Token.GETTER_DEF, n);
   validateChildCount(n);
   validateObjectLiteralKeyName(n);
   Node function = n.getFirstChild();
   validateFunctionExpression(function);
   // objlit get functions must be nameless, and must have zero parameters.
   if (!function.getFirstChild().getString().isEmpty()) {
     violation("Expected unnamed function expression.", n);
   }
   Node functionParams = function.getChildAtIndex(1);
   if (functionParams.hasChildren()) {
     violation("get methods must not have parameters.", n);
   }
 }
 /**
  * Removes declarations of any variables whose names are strip names or whose whose rvalues are
  * static method calls on strip types. Builds a set of removed variables so that all references
  * to them can be removed.
  *
  * @param t The traversal
  * @param n A VAR node
  * @param parent {@code n}'s parent
  */
 void removeVarDeclarationsByNameOrRvalue(NodeTraversal t, Node n, Node parent) {
   for (Node nameNode = n.getFirstChild(); nameNode != null; nameNode = nameNode.getNext()) {
     String name = nameNode.getString();
     if (isStripName(name) || isCallWhoseReturnValueShouldBeStripped(nameNode.getFirstChild())) {
       // Remove the NAME.
       Scope scope = t.getScope();
       varsToRemove.add(scope.getVar(name));
       n.removeChild(nameNode);
       compiler.reportCodeChange();
     }
   }
   if (!n.hasChildren()) {
     // Must also remove the VAR.
     replaceWithEmpty(n, parent);
     compiler.reportCodeChange();
   }
 }
 /**
  * Flattens a particular prefix of a single name reference.
  *
  * @param alias A flattened prefix name (e.g. "a$b")
  * @param n The node corresponding to a subproperty name (e.g. "a.b.c.d")
  * @param depth The difference in depth between the property name and the prefix name (e.g. 2)
  * @param originalName String version of the property name.
  */
 private void flattenNameRefAtDepth(String alias, Node n, int depth, String originalName) {
   // This method has to work for both GETPROP chains and, in rare cases,
   // OBJLIT keys, possibly nested. That's why we check for children before
   // proceeding. In the OBJLIT case, we don't need to do anything.
   int nType = n.getType();
   boolean isQName = nType == Token.NAME || nType == Token.GETPROP;
   boolean isObjKey = NodeUtil.isObjectLitKey(n);
   Preconditions.checkState(isObjKey || isQName);
   if (isQName) {
     for (int i = 1; i < depth && n.hasChildren(); i++) {
       n = n.getFirstChild();
     }
     if (n.isGetProp() && n.getFirstChild().isGetProp()) {
       flattenNameRef(alias, n.getFirstChild(), n, originalName);
     }
   }
 }
Пример #30
0
 private void validateTemplateLit(Node n) {
   validateEs6Feature("template literal", n);
   validateNodeType(Token.TEMPLATELIT, n);
   if (!n.hasChildren()) {
     return;
   }
   for (int i = 0; i < n.getChildCount(); i++) {
     Node child = n.getChildAtIndex(i);
     // If the first child is not a STRING, this is a tagged template.
     if (i == 0 && !child.isString()) {
       validateExpression(child);
     } else if (child.isString()) {
       validateString(child);
     } else {
       validateTemplateLitSub(child);
     }
   }
 }