/** 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); } }
/** @return The first property in the objlit that matches the key. */ private static Node extractProperty(Node objlit, String keyName) { for (Node keyNode : objlit.children()) { if (keyNode.getString().equals(keyName)) { return keyNode.isStringKey() ? keyNode.getFirstChild() : null; } } return null; }
// Only unquoted plain properties are currently supported. private static boolean validateObjLit(Node objlit) { for (Node key : objlit.children()) { if (!key.isStringKey() || key.isQuotedString()) { return false; } } return true; }
private boolean hasShorthandAssignment(Node objLit) { Preconditions.checkState(objLit.isObjectLit()); for (Node property : objLit.children()) { if (property.isStringKey() && !property.hasChildren()) { return true; } } return false; }
private boolean isContainedInGoogDefineClass(Node n) { while (n != null) { n = n.getParent(); if (n.isCall()) { if (isGoogDefineClass(n)) { return true; } } else if (!n.isObjectLit() && !n.isStringKey()) { break; } } return false; }
private void validateObjectPattern(int type, Node n) { validateNodeType(Token.OBJECT_PATTERN, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // When the object pattern is a direct child of a var/let/const node, // the last element is the RHS of the assignment. if (c == n.getLastChild() && NodeUtil.isNameDeclaration(n.getParent())) { validateExpression(c); } else if (c.isStringKey()) { validateObjectPatternStringKey(type, c); } else { // Nested destructuring pattern. validateNameDeclarationChild(type, c); } } }
/** * @return A list of functions from a behavior which should be copied to the element prototype. */ private List<MemberDefinition> getBehaviorFunctionsToCopy(Node behaviorObjLit) { Preconditions.checkState(behaviorObjLit.isObjectLit()); ImmutableList.Builder<MemberDefinition> functionsToCopy = ImmutableList.builder(); for (Node keyNode : behaviorObjLit.children()) { if ((keyNode.isStringKey() && keyNode.getFirstChild().isFunction() || keyNode.isMemberFunctionDef()) && !behaviorNamesNotToCopy.contains(keyNode.getString())) { functionsToCopy.add( new MemberDefinition( NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.getFirstChild())); } } return functionsToCopy.build(); }
private static boolean isDeclarationHelper(Node node) { Node parent = node.getParent(); // Special case for class B extends A, A is not a declaration. if (parent.isClass() && node != parent.getFirstChild()) { return false; } // This condition can be true during InlineVariables. if (parent.getParent() == null) { return false; } if (NodeUtil.isNameDeclaration(parent.getParent()) && node == parent.getLastChild()) { // Unless it is something like "for (var/let/const a of x){}", // this is the RHS of a var/let/const and thus not a declaration. if (parent.getParent().getParent() == null || !parent.getParent().getParent().isForOf()) { return false; } } // Special cases for destructuring patterns. if (parent.isDestructuringPattern() || (parent.isStringKey() && parent.getParent().isObjectPattern()) || (parent.isComputedProp() && parent.getParent().isObjectPattern() && node == parent.getLastChild()) || (parent.isDefaultValue() && node == parent.getFirstChild())) { return isDeclarationHelper(parent); } // Special case for arrow function if (parent.isArrowFunction()) { return node == parent.getFirstChild(); } return DECLARATION_PARENTS.contains(parent.getType()); }
private void visitArrayPattern(NodeTraversal t, Node arrayPattern, Node parent) { Node rhs, nodeToDetach; if (NodeUtil.isNameDeclaration(parent) && !NodeUtil.isEnhancedFor(parent.getParent())) { // The array pattern is the only child, because Es6SplitVariableDeclarations // has already run. Preconditions.checkState(arrayPattern.getNext() == null); rhs = arrayPattern.getLastChild(); nodeToDetach = parent; } else if (parent.isAssign()) { rhs = arrayPattern.getNext(); nodeToDetach = parent.getParent(); Preconditions.checkState(nodeToDetach.isExprResult()); } else if (parent.isArrayPattern() || parent.isDefaultValue() || parent.isStringKey()) { // This is a nested array pattern. Don't do anything now; we'll visit it // after visiting the parent. return; } else if (NodeUtil.isEnhancedFor(parent) || NodeUtil.isEnhancedFor(parent.getParent())) { visitDestructuringPatternInEnhancedFor(arrayPattern); return; } else { Preconditions.checkState(parent.isCatch() || parent.isForOf()); cannotConvertYet( arrayPattern, "ARRAY_PATTERN that is a child of a " + Token.name(parent.getType())); return; } // Convert 'var [x, y] = rhs' to: // var temp = rhs; // var x = temp[0]; // var y = temp[1]; String tempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++); Node tempDecl = IR.var(IR.name(tempVarName), rhs.detachFromParent()) .useSourceInfoIfMissingFromForTree(arrayPattern); nodeToDetach.getParent().addChildBefore(tempDecl, nodeToDetach); int i = 0; for (Node child = arrayPattern.getFirstChild(), next; child != null; child = next, i++) { next = child.getNext(); if (child.isEmpty()) { continue; } Node newLHS, newRHS; if (child.isDefaultValue()) { Node getElem = IR.getelem(IR.name(tempVarName), IR.number(i)); // [x = defaultValue] = rhs; // becomes // var temp = rhs; // x = (temp[0] === undefined) ? defaultValue : temp[0]; newLHS = child.getFirstChild().detachFromParent(); newRHS = defaultValueHook(getElem, child.getLastChild().detachFromParent()); } else if (child.isRest()) { newLHS = child.detachFromParent(); newLHS.setType(Token.NAME); // [].slice.call(temp, i) newRHS = IR.call( IR.getprop(IR.getprop(IR.arraylit(), IR.string("slice")), IR.string("call")), IR.name(tempVarName), IR.number(i)); } else { newLHS = child.detachFromParent(); newRHS = IR.getelem(IR.name(tempVarName), IR.number(i)); } Node newNode; if (parent.isAssign()) { Node assignment = IR.assign(newLHS, newRHS); newNode = IR.exprResult(assignment); } else { newNode = IR.declaration(newLHS, newRHS, parent.getType()); } newNode.useSourceInfoIfMissingFromForTree(arrayPattern); nodeToDetach.getParent().addChildBefore(newNode, nodeToDetach); // Explicitly visit the LHS of the new node since it may be a nested // destructuring pattern. visit(t, newLHS, newLHS.getParent()); } nodeToDetach.detachFromParent(); compiler.reportCodeChange(); }
private void visitObjectPattern(NodeTraversal t, Node objectPattern, Node parent) { Node rhs, nodeToDetach; if (NodeUtil.isNameDeclaration(parent) && !NodeUtil.isEnhancedFor(parent.getParent())) { rhs = objectPattern.getLastChild(); nodeToDetach = parent; } else if (parent.isAssign() && parent.getParent().isExprResult()) { rhs = parent.getLastChild(); nodeToDetach = parent.getParent(); } else if (parent.isStringKey() || parent.isArrayPattern() || parent.isDefaultValue()) { // Nested object pattern; do nothing. We will visit it after rewriting the parent. return; } else if (NodeUtil.isEnhancedFor(parent) || NodeUtil.isEnhancedFor(parent.getParent())) { visitDestructuringPatternInEnhancedFor(objectPattern); return; } else { Preconditions.checkState(parent.isCatch(), parent); cannotConvertYet( objectPattern, "OBJECT_PATTERN that is a child of a " + Token.name(parent.getType())); return; } // Convert 'var {a: b, c: d} = rhs' to: // var temp = rhs; // var b = temp.a; // var d = temp.c; String tempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++); Node tempDecl = IR.var(IR.name(tempVarName), rhs.detachFromParent()) .useSourceInfoIfMissingFromForTree(objectPattern); nodeToDetach.getParent().addChildBefore(tempDecl, nodeToDetach); for (Node child = objectPattern.getFirstChild(), next; child != null; child = next) { next = child.getNext(); Node newLHS, newRHS; if (child.isStringKey()) { Preconditions.checkState(child.hasChildren()); Node getprop = new Node( child.isQuotedString() ? Token.GETELEM : Token.GETPROP, IR.name(tempVarName), IR.string(child.getString())); Node value = child.removeFirstChild(); if (!value.isDefaultValue()) { newLHS = value; newRHS = getprop; } else { newLHS = value.removeFirstChild(); Node defaultValue = value.removeFirstChild(); newRHS = defaultValueHook(getprop, defaultValue); } } else if (child.isComputedProp()) { if (child.getLastChild().isDefaultValue()) { newLHS = child.getLastChild().removeFirstChild(); Node getelem = IR.getelem(IR.name(tempVarName), child.removeFirstChild()); String intermediateTempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++); Node intermediateDecl = IR.var(IR.name(intermediateTempVarName), getelem); intermediateDecl.useSourceInfoIfMissingFromForTree(child); nodeToDetach.getParent().addChildBefore(intermediateDecl, nodeToDetach); newRHS = defaultValueHook( IR.name(intermediateTempVarName), child.getLastChild().removeFirstChild()); } else { newRHS = IR.getelem(IR.name(tempVarName), child.removeFirstChild()); newLHS = child.removeFirstChild(); } } else if (child.isDefaultValue()) { newLHS = child.removeFirstChild(); Node defaultValue = child.removeFirstChild(); Node getprop = IR.getprop(IR.name(tempVarName), IR.string(newLHS.getString())); newRHS = defaultValueHook(getprop, defaultValue); } else { throw new IllegalStateException("Unexpected OBJECT_PATTERN child: " + child); } Node newNode; if (NodeUtil.isNameDeclaration(parent)) { newNode = IR.declaration(newLHS, newRHS, parent.getType()); } else if (parent.isAssign()) { newNode = IR.exprResult(IR.assign(newLHS, newRHS)); } else { throw new IllegalStateException("not reached"); } newNode.useSourceInfoIfMissingFromForTree(child); nodeToDetach.getParent().addChildBefore(newNode, nodeToDetach); // Explicitly visit the LHS of the new node since it may be a nested // destructuring pattern. visit(t, newLHS, newLHS.getParent()); } nodeToDetach.detachFromParent(); compiler.reportCodeChange(); }