private static boolean isLhsOfEnhancedForExpression(Node n) { Node parent = n.getParent(); if (NodeUtil.isNameDeclaration(parent)) { return isLhsOfEnhancedForExpression(parent); } return NodeUtil.isEnhancedFor(parent) && parent.getFirstChild() == n; }
private void validateVarOrOptionalExpression(Node n) { if (NodeUtil.isNameDeclaration(n)) { validateNameDeclarationHelper(n.getType(), n); } else { validateOptionalExpression(n); } }
private void validateVarOrAssignmentTarget(Node n) { if (NodeUtil.isNameDeclaration(n)) { // Only one NAME can be declared for FOR-IN expressions. validateChildCount(n, 1); validateNameDeclarationHelper(n.getType(), n); } else { validateAssignmentTarget(n); } }
private void rewriteClassDefinition(Node n, Node parent, NodeTraversal t) { if (parent.getParent().isConst() || parent.getParent().isLet()) { compiler.report(JSError.make(n, POLYMER_INVALID_DECLARATION)); return; } ClassDefinition def = extractClassDefinition(n); if (def != null) { if (NodeUtil.isNameDeclaration(parent.getParent()) || parent.isAssign()) { rewritePolymerClass(parent.getParent(), def, t); } else { rewritePolymerClass(parent, def, t); } } }
private void visitForOf(Node node, Node parent) { Node variable = node.removeFirstChild(); Node iterable = node.removeFirstChild(); Node body = node.removeFirstChild(); Node iterName = IR.name(ITER_BASE + compiler.getUniqueNameIdSupplier().get()); Node getNext = IR.call(IR.getprop(iterName.cloneTree(), IR.string("next"))); String variableName; int declType; if (variable.isName()) { declType = Token.NAME; variableName = variable.getQualifiedName(); } else { Preconditions.checkState( NodeUtil.isNameDeclaration(variable), "Expected var, let, or const. Got %s", variable); declType = variable.getType(); variableName = variable.getFirstChild().getQualifiedName(); } Node iterResult = IR.name(ITER_RESULT + variableName); Node makeIter = IR.call(NodeUtil.newQName(compiler, MAKE_ITER), iterable); compiler.needsEs6Runtime = true; Node init = IR.var(iterName.cloneTree(), makeIter); Node initIterResult = iterResult.cloneTree(); initIterResult.addChildToFront(getNext.cloneTree()); init.addChildToBack(initIterResult); Node cond = IR.not(IR.getprop(iterResult.cloneTree(), IR.string("done"))); Node incr = IR.assign(iterResult.cloneTree(), getNext.cloneTree()); Node declarationOrAssign; if (declType == Token.NAME) { declarationOrAssign = IR.exprResult( IR.assign( IR.name(variableName), IR.getprop(iterResult.cloneTree(), IR.string("value")))); } else { declarationOrAssign = new Node(declType, IR.name(variableName)); declarationOrAssign .getFirstChild() .addChildToBack(IR.getprop(iterResult.cloneTree(), IR.string("value"))); } body.addChildToFront(declarationOrAssign); Node newFor = IR.forNode(init, cond, incr, body); newFor.useSourceInfoIfMissingFromForTree(node); parent.replaceChild(node, newFor); compiler.reportCodeChange(); }
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); } } }
/** * Returns whether the node is the right hand side of an assignment or initialization of a * variable named *_ID of *_ID_. */ private boolean insideAssignmentToIdConstant(Node n) { Node parent = n.getParent(); if (parent.isAssign()) { String qname = parent.getFirstChild().getQualifiedName(); return qname != null && isIdName(qname); } else if (parent.isName()) { Node grandParent = parent.getParent(); if (grandParent != null && NodeUtil.isNameDeclaration(grandParent)) { String name = parent.getString(); return isIdName(name); } else { return false; } } else { return false; } }
/** * If this returns true, check for @extends and @implements annotations on this node. Otherwise, * it's probably an alias for an existing class, so skip those annotations. * * @return Whether the given node declares a function. True for the following forms: * <li> * <pre>function foo() {}</pre> * <li> * <pre>var foo = function() {};</pre> * <li> * <pre>foo.bar = function() {};</pre> */ private boolean declaresFunction(Node n) { if (n.isFunction()) { return true; } if (n.isAssign() && n.getLastChild().isFunction()) { return true; } if (NodeUtil.isNameDeclaration(n) && n.getFirstChild().hasChildren() && n.getFirstFirstChild().isFunction()) { return true; } return false; }
private void validateArrayPattern(int type, Node n) { validateNodeType(Token.ARRAY_PATTERN, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // When the array 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.isRest()) { validateRest(c); } else if (c.isEmpty()) { validateChildless(c); } else { // The members of the array pattern can be simple names, // or nested array/object patterns, e.g. "var [a,[b,c]]=[1,[2,3]];" validateNameDeclarationChild(type, c); } } }
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()); }
/** * Validates the class definition and if valid, destructively extracts the class definition from * the AST. */ private ClassDefinition extractClassDefinition(Node callNode) { Node descriptor = NodeUtil.getArgumentForCallOrNew(callNode, 0); if (descriptor == null || !descriptor.isObjectLit()) { // report bad class definition compiler.report(JSError.make(callNode, POLYMER_DESCRIPTOR_NOT_VALID)); return null; } int paramCount = callNode.getChildCount() - 1; if (paramCount != 1) { compiler.report(JSError.make(callNode, POLYMER_UNEXPECTED_PARAMS)); return null; } Node elName = NodeUtil.getFirstPropMatchingKey(descriptor, "is"); if (elName == null) { compiler.report(JSError.make(callNode, POLYMER_MISSING_IS)); return null; } String elNameString = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, elName.getString()); elNameString += "Element"; Node target; if (NodeUtil.isNameDeclaration(callNode.getParent().getParent())) { target = IR.name(callNode.getParent().getString()); } else if (callNode.getParent().isAssign()) { target = callNode.getParent().getFirstChild().cloneTree(); } else { target = IR.name(elNameString); } target.useSourceInfoIfMissingFrom(callNode); JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(target); JSDocInfo ctorInfo = null; Node constructor = NodeUtil.getFirstPropMatchingKey(descriptor, "factoryImpl"); if (constructor == null) { constructor = IR.function(IR.name(""), IR.paramList(), IR.block()); constructor.useSourceInfoFromForTree(callNode); } else { ctorInfo = NodeUtil.getBestJSDocInfo(constructor); } Node baseClass = NodeUtil.getFirstPropMatchingKey(descriptor, "extends"); String nativeBaseElement = baseClass == null ? null : baseClass.getString(); Node behaviorArray = NodeUtil.getFirstPropMatchingKey(descriptor, "behaviors"); List<BehaviorDefinition> behaviors = extractBehaviors(behaviorArray); List<MemberDefinition> allProperties = new LinkedList<>(); for (BehaviorDefinition behavior : behaviors) { overwriteMembersIfPresent(allProperties, behavior.props); } overwriteMembersIfPresent(allProperties, extractProperties(descriptor)); ClassDefinition def = new ClassDefinition( target, descriptor, classInfo, new MemberDefinition(ctorInfo, null, constructor), nativeBaseElement, allProperties, behaviors); return def; }
private void transformLoopClosure() { if (loopObjectMap.isEmpty()) { return; } for (Node loopNode : loopObjectMap.keySet()) { // Introduce objects to reflect the captured scope variables. // Fields are initially left as undefined to avoid cases like: // var $jscomp$loop$0 = {i: 0, j: $jscomp$loop$0.i} // They are initialized lazily by changing declarations into assignments // later. LoopObject object = loopObjectMap.get(loopNode); Node objectLit = IR.objectlit(); Node objectLitNextIteration = IR.objectlit(); for (Var var : object.vars) { objectLit.addChildToBack(IR.stringKey(var.name, IR.name("undefined"))); objectLitNextIteration.addChildToBack( IR.stringKey(var.name, IR.getprop(IR.name(object.name), IR.string(var.name)))); } Node updateLoopObject = IR.assign(IR.name(object.name), objectLitNextIteration); loopNode .getParent() .addChildBefore( IR.var(IR.name(object.name), objectLit).useSourceInfoIfMissingFromForTree(loopNode), loopNode); if (NodeUtil.isVanillaFor(loopNode)) { // For // The initializer is pulled out and placed prior to the loop. Node initializer = loopNode.getFirstChild(); loopNode.replaceChild(initializer, IR.empty()); if (!initializer.isEmpty()) { loopNode.getParent().addChildBefore(initializer, loopNode); } Node increment = loopNode.getChildAtIndex(2); if (increment.isEmpty()) { loopNode.replaceChild( increment, updateLoopObject.useSourceInfoIfMissingFromForTree(loopNode)); } else { Node placeHolder = IR.empty(); loopNode.replaceChild(increment, placeHolder); loopNode.replaceChild( placeHolder, IR.comma(updateLoopObject, increment).useSourceInfoIfMissingFromForTree(loopNode)); } } else if (loopNode.isDo()) { // do-while, put at the end of the block loopNode .getFirstChild() .addChildToBack( IR.exprResult(updateLoopObject).useSourceInfoIfMissingFromForTree(loopNode)); } else { // For-in, for-of or while, put at the end of the block loopNode .getLastChild() .addChildToBack( IR.exprResult(updateLoopObject).useSourceInfoIfMissingFromForTree(loopNode)); } // For captured variables, change declarations to assignments on the // corresponding field of the introduced object. Rename all references // accordingly. for (Var var : object.vars) { for (Node reference : referenceMap.get(var)) { // For-of and for-in declarations are not altered, since they are // used as temporary variables for assignment. if (NodeUtil.isEnhancedFor(loopNode) && loopNode.getFirstChild() == reference.getParent()) { loopNode .getLastChild() .addChildToFront( IR.exprResult( IR.assign( IR.getprop(IR.name(object.name), IR.string(var.name)), IR.name(var.name))) .useSourceInfoIfMissingFromForTree(reference)); } else { if (NodeUtil.isNameDeclaration(reference.getParent())) { Node declaration = reference.getParent(); Node grandParent = declaration.getParent(); // Normalize: "let i = 0, j = 0;" becomes "let i = 0; let j = 0;" while (declaration.getChildCount() > 1) { Node name = declaration.getLastChild(); grandParent.addChildAfter( IR.declaration(name.detachFromParent(), declaration.getType()) .useSourceInfoIfMissingFromForTree(declaration), declaration); } // Change declaration to assignment, or just drop it if there's // no initial value. if (reference.hasChildren()) { declaration = reference.getParent(); // Might have changed now Node newReference = IR.name(var.name); Node replacement = IR.exprResult(IR.assign(newReference, reference.removeFirstChild())) .useSourceInfoIfMissingFromForTree(declaration); grandParent.replaceChild(declaration, replacement); reference = newReference; } else { grandParent.removeChild(declaration); } } // Change reference to GETPROP. reference .getParent() .replaceChild( reference, IR.getprop(IR.name(object.name), IR.string(var.name)) .useSourceInfoIfMissingFromForTree(reference)); } } } } // Create wrapper functions and call them. for (Node function : functionLoopObjectsMap.keySet()) { Node returnNode = IR.returnNode(); Collection<LoopObject> objects = functionLoopObjectsMap.get(function); Node[] objectNames = new Node[objects.size()]; Node[] objectNamesForCall = new Node[objects.size()]; int i = 0; for (LoopObject object : objects) { objectNames[i] = IR.name(object.name); objectNamesForCall[i] = IR.name(object.name); i++; } Node iife = IR.function(IR.name(""), IR.paramList(objectNames), IR.block(returnNode)); Node call = IR.call(iife, objectNamesForCall); call.putBooleanProp(Node.FREE_CALL, true); function .getParent() .replaceChild(function, call.useSourceInfoIfMissingFromForTree(function)); returnNode.addChildToFront(function); } }
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(); }