private ConditionData condition() throws IOException { ConditionData data = new ConditionData(); if (mustMatchToken(Token.LP, "msg.no.paren.cond")) data.lp = ts.tokenBeg; data.condition = expr(); if (mustMatchToken(Token.RP, "msg.no.paren.after.cond")) data.rp = ts.tokenBeg; // Report strict warning on code like "if (a = 7) ...". Suppress the // warning if the condition is parenthesized, like "if ((a = 7)) ...". if (data.condition instanceof Assignment) { addStrictWarning("msg.equal.as.assign", "", data.condition.getPosition(), data.condition.getLength()); } return data; }
@Override public boolean visit(AstNode node) { if (node instanceof PropertyGet){ PropertyGet pg = (PropertyGet)node; AstNode target = pg.getTarget(); String propName = pg.getProperty().getIdentifier(); if (target instanceof KeywordLiteral && ConstraintGenUtil.isThis(target)){ if (node.getParent() instanceof Assignment){ Assignment a = (Assignment)node.getParent(); if (a.getLeft() == node){ writtenProperties.add(propName); } else { readProperties.add(propName); } } else { readProperties.add(propName); } readProperties.removeAll(writtenProperties); // if something is read and written, it should only be in the written set } } else if (node instanceof FunctionNode) { // don't recurse into nested function body return false; } return true; }
/** * generate constraints for assignments */ private void processAssignment(Assignment a) throws Error { AstNode left = a.getLeft(); AstNode right = a.getRight(); ITypeTerm expTerm = findOrCreateExpressionTerm(a); if (left instanceof Name){ processAssignToVariable(a, left, right, expTerm); } else if (left instanceof PropertyGet) { PropertyGet pg = (PropertyGet)left; if (pg.getProperty().getIdentifier().equals("prototype")){ processAssignToPrototype(a, left, right, expTerm); } else { processAssignToProperty(a, left, right, expTerm); } processExpression(pg.getTarget()); // TEST } else if (left instanceof ElementGet){ processIndexedAssignment(a, left, right, expTerm); } else { error("unsupported LHS type in Assignment: " + left.getClass().getName(), left); } }
/** * assignment to an array element or map element: v[e] = ... */ private void processIndexedAssignment(Assignment a, AstNode left, AstNode right, ITypeTerm expTerm) throws Error { ElementGet eg = (ElementGet)left; AstNode base = eg.getTarget(); AstNode element = eg.getElement(); // generate the appropriate constraints for the expression being indexed processExpression(base); // an assignment expression has the same type as its left-hand side ITypeTerm leftTerm = findOrCreateExpressionTerm(eg); ITypeTerm rightTerm = processExpression(right); addTypeEqualityConstraint(expTerm, leftTerm, a.getLineno(), null); // require index to be of the appropriate type ITypeTerm elementTerm = processExpression(element); ITypeTerm baseTerm = findOrCreateExpressionTerm(base); addTypeEqualityConstraint(elementTerm, findOrCreateKeyTerm(baseTerm, eg.getLineno()), a.getLineno(), (solution) -> genericTypeError("indexes must be strings or ints", locationOf(element))); processCopy(right, rightTerm, leftTerm, a.getLineno(), (solution) -> subtypeError("bad assignment of " + shortSrc(right) + " to " + shortSrc(left), solution.typeOfTerm(rightTerm), solution.typeOfTerm(leftTerm), locationOf(a))); }
private void visitAssignmentAtRoot(final Assignment node) { if (node.getRight() instanceof FunctionNode || ingoreAbstractMethod(node)) { if (node.getParent() instanceof ExpressionStatement) { try { visitMethodOrClass(((PropertyGet) node.getLeft()).toSource(), node.getJsDoc()); if (node.getRight() instanceof PropertyGet) { LOG.info("left:{}, right:{}", ((PropertyGet) node.getLeft()).toSource(), ((PropertyGet) node.getRight()).toSource()); } } catch (final ClassCastException e) { LOG.error("Node different then expected in file:{},", fileName, e); } } else { // LOG.debug("Node at linenr {} ignored in file:{}", // node.getLineno(), fileName); } } else if (node.getParent() instanceof ExpressionStatement) { visitMethodOrEnum(((PropertyGet) node.getLeft()).toSource(), node.getJsDoc(), node.getRight()); } }
private Node transformAssignment(Assignment node) { AstNode left = removeParens(node.getLeft()); Node target = null; if (isDestructuring(left)) { decompile(left); target = left; } else { target = transform(left); } decompiler.addToken(node.getType()); return createAssignment(node.getType(), target, transform(node.getRight())); }
public static Assignment findEnclosingAssignment(AstNode node) { AstNode current = node; while (current != null && !(current instanceof Assignment)){ current = current.getParent(); } Assignment a = (Assignment)current; return a; }
/** * generate constraints for a for-loop */ private void processForLoop(ForLoop loop) { AstNode initializer = loop.getInitializer(); if (initializer instanceof Assignment){ processAssignment((Assignment)initializer); } AstNode condition = loop.getCondition(); processExpression(condition); AstNode incrementExp = loop.getIncrement(); processExpression(incrementExp); }
/** * assignment to an object property */ private void processAssignToProperty(Assignment a, AstNode left, AstNode right, ITypeTerm expTerm) throws Error { PropertyGet pg = (PropertyGet)left; AstNode base = pg.getTarget(); Name prop = pg.getProperty(); ITypeTerm pgTerm = findOrCreateExpressionTerm(pg); ITypeTerm baseTerm; if (base instanceof KeywordLiteral && ConstraintGenUtil.isThis(base)){ baseTerm = findOrCreateThisTerm(base); } else { baseTerm = generateReceiverConstraints(pg); } int assignLineNo = a.getLineno(); // detect assignments of the form C.prototype.foo = ... if (base instanceof PropertyGet) { PropertyGet basePG = (PropertyGet) base; String baseProp = basePG.getProperty().getIdentifier(); if (baseProp.equals("prototype")) { checkForValidProtoPropAssign(a, pg, assignLineNo, basePG); } } ITypeTerm leftTerm = findOrCreatePropertyAccessTerm(baseTerm, prop.getIdentifier(), null); ITypeTerm rightTerm = processExpression(right); addTypeEqualityConstraint(pgTerm, leftTerm, assignLineNo, (solution) -> typeEqualityError("incompatible types", solution.typeOfTerm(leftTerm), solution.typeOfTerm(pgTerm), locationOf(pg))); addTypeEqualityConstraint(expTerm, leftTerm, assignLineNo, (solution) -> typeEqualityError("incompatible types", solution.typeOfTerm(leftTerm), solution.typeOfTerm(expTerm), locationOf(a))); processCopy(right, rightTerm, leftTerm, assignLineNo, (solution) -> badPropertyWrite(pg, solution.typeOfTerm(baseTerm), hasType(leftTerm, solution) ? solution.typeOfTerm(leftTerm) : null, solution.typeOfTerm(rightTerm))); }
/** * assignment to a variable */ private void processAssignToVariable(Assignment a, AstNode left, AstNode right, ITypeTerm expTerm) { ITypeTerm leftTerm = findOrCreateExpressionTerm(left); ITypeTerm nameTerm = findOrCreateNameDeclarationTerm((Name) left); // find unique representative for the Name addTypeEqualityConstraint(leftTerm, nameTerm, a.getLineno(), null); // equate to the LHS expression ITypeTerm rightTerm = processExpression(right); addTypeEqualityConstraint(expTerm, leftTerm, a.getLineno(), (solution) -> genericTypeError("assignment " + shortSrc(a), locationOf(a))); processCopy(right, rightTerm, leftTerm, a.getLineno(), (solution) -> subtypeError(shortSrc(right) + " assigned to " + shortSrc(left), solution.typeOfTerm(rightTerm), solution.typeOfTerm(leftTerm), locationOf(a))); }
@Override public boolean visit(final AstNode node) { if (isRootNode(node)) { if (node.getType() == Token.ASSIGN) { visitAssignment((Assignment) node); } else if (node.getType() == Token.CONST) { LOG.error("FIXME:Const node detected, not parsed in file:{}", fileName); } else { visitOtherNode(node); } } return true; }
private void reassignVariable(AstNode assign, CodeBlock block, int locationOffSet) { Assignment assignNode = (Assignment) assign; // maybe a variable AstNode leftNode = assignNode.getLeft(); // assign the variable to AstNode rightNode = assignNode.getRight(); String name = leftNode.getType() == Token.NAME ? ((Name) leftNode) .getIdentifier() : null; if (name != null) { int start = assignNode.getAbsolutePosition(); int offset = start + assignNode.getLength(); // check that the caret position is below the dot before looking for // the variable if (offset <= dot) { JavaScriptVariableDeclaration dec = provider .getVariableResolver().findDeclaration(name, dot); if (dec != null && (dec.getCodeBlock() == null || dec.getCodeBlock() .contains(dot))) { // Set reference to new type dec.setTypeDeclaration(rightNode, isPreProcessing()); } else { // assume we can add variable as we are trying to assign to // name addVariableToResolver(leftNode, rightNode, block, locationOffSet); } } } }
/** * For a {@link FunctionNode} representing a constructor, if the constructor C is * followed by a sequence of assignments of the form C.prototype.a = ...;, return * a set of all the properties written on the prototype. If the assignments do not * fit that form, return the empty set. * @param consNode * @return */ public static Set<String> getWrittenPrototypeProps(FunctionNode consNode) { Set<String> result = HashSetFactory.make(); AstNode parent = consNode.getParent(); boolean found = false; for (Node child: parent) { if (child instanceof EmptyStatement) { continue; } if (child.equals(consNode)) { found = true; } else if (found) { // looking for a statement of the form C.prototype.a = ...; boolean foundAssign = false; if (child instanceof ExpressionStatement) { AstNode expression = ((ExpressionStatement)child).getExpression(); if (expression instanceof Assignment) { Assignment assign = (Assignment) expression; AstNode lhs = assign.getLeft(); if (lhs instanceof PropertyGet) { PropertyGet pg = (PropertyGet) lhs; AstNode pgTarget = pg.getTarget(); if (pgTarget instanceof PropertyGet) { PropertyGet basePG = (PropertyGet) pgTarget; if (basePG.getProperty().getIdentifier().equals("prototype")) { // BINGO result.add(pg.getProperty().getIdentifier()); foundAssign = true; } } } } } if (!foundAssign) { // stop looking for more assignments break; } } } return result; }
/** * This method generates constraints for all relevant AstNodes. It delegates its work to various * processXXX() methods that handle AstNodes of type XXX. */ @Override public boolean visit(AstNode node) { if (node instanceof VariableInitializer){ processVariableInitializer(node); } else if (node instanceof ReturnStatement){ processReturnStatement((ReturnStatement)node); } else if (node instanceof ExpressionStatement){ processExpressionStatement((ExpressionStatement)node); } else if (node instanceof ForLoop){ processForLoop((ForLoop)node); } else if (node instanceof ForInLoop){ processForInLoop((ForInLoop)node); }else if (node instanceof WhileLoop){ processWhileLoop((WhileLoop)node); } else if (node instanceof DoLoop){ processDoLoop((DoLoop)node); } else if (node instanceof NewExpression){ processNewExpression((NewExpression)node); } else if (node instanceof FunctionCall){ processFunctionCall((FunctionCall)node); } else if (node instanceof ElementGet){ processElementGet((ElementGet)node); } else if (node instanceof FunctionNode){ processFunctionNode((FunctionNode)node); } else if (node instanceof IfStatement){ processIfStatement((IfStatement)node); } else if (node instanceof KeywordLiteral){ processKeywordLiteral((KeywordLiteral)node); } else if (node instanceof SwitchStatement){ processSwitchStatement((SwitchStatement)node); } else if (node instanceof SwitchCase){ processSwitchCase((SwitchCase)node); } else if ((node instanceof AstRoot) || //AstRoot: no constraints need to be generated (node instanceof BreakStatement) || //BreakStatement: no constraints need to be generated (node instanceof VariableDeclaration) || //VariableDeclaration: we generate constraints for its constituent VariableInitializer nodes (node instanceof Name) || //Name: generate constraints for complex expressions that refer to names (node instanceof NumberLiteral) || //NumberLiteral: generate constraints for complex expressions that refer to names (node instanceof StringLiteral) || //StringLiteral: generate constraints for complex expressions that refer to names (node instanceof Assignment) || // Assignment is a special case of InfixExpression (node instanceof ArrayLiteral) || (node instanceof UnaryExpression) || (node instanceof InfixExpression) || (node instanceof ConditionalExpression) || (node instanceof ParenthesizedExpression) || (node instanceof EmptyExpression) || (node instanceof ObjectLiteral) || (node instanceof EmptyStatement) || (node instanceof ContinueStatement) || (node instanceof Scope) || (node instanceof Block)){ // // occurs in programs with for loops -- nothing to be done here? /* nothing */ } else { error("unsupported node " + node.toSource().trim() + " of type: " + node.getClass().getName(), node); } return true; }
/** * assignment to the "prototype" property */ private void processAssignToPrototype(Assignment a, AstNode left, AstNode right, ITypeTerm expTerm) throws Error { PropertyGet pg = (PropertyGet)left; AstNode base = pg.getTarget(); ITypeTerm pgTerm = findOrCreateExpressionTerm(pg); if (base instanceof Name){ Name name = (Name)base; if (!validRHSForAssignToPrototype(right)) { error( "expression " + right.toSource() + " cannot be assigned to a constructor prototype (line " + right.getLineno() + ")", a); } // can only write to prototype immediately after declaration of // constructor of the same name AstNode parent = a.getParent(); if (!(parent instanceof ExpressionStatement)) { error( "assignment to prototype property not allowed here (line " + a.getLineno() + ")", a); return; } Node prev = getPredecessorNode(parent); if (!(prev instanceof FunctionNode)) { error( "assignment to prototype property only allowed after constructor declaration (line " + a.getLineno() + ")", a); return; } FunctionNode fn = (FunctionNode) prev; String functionName = fn.getName(); String identifier = name.getIdentifier(); if (!functionName.equals(identifier)) { error( "can only assign to prototype of function " + functionName + " here (line " + a.getLineno() + ")", a); return; } ITypeTerm baseTerm = findOrCreateExpressionTerm(base); // make term for expression ITypeTerm nameTerm = findOrCreateNameDeclarationTerm(name); // find unique representative for referenced Name addTypeEqualityConstraint(baseTerm, nameTerm, a.getLineno(), null); // equate them ITypeTerm protoTerm = findOrCreateProtoTerm(baseTerm, pg.getLineno()); ITypeTerm rightTerm = processExpression(right); addTypeEqualityConstraint(pgTerm, protoTerm, a.getLineno(), null); addTypeEqualityConstraint(rightTerm, protoTerm, a.getLineno(), null); addTypeEqualityConstraint(expTerm, protoTerm, a.getLineno(), null); } else { error("processAssignToPrototype: unsupported case for receiver expression: " + base.getClass().getName(), base); } }
private void checkForValidProtoPropAssign(Assignment a, PropertyGet pg, int assignLineNo, PropertyGet basePG) { AstNode baseTarget = basePG.getTarget(); if (!(baseTarget instanceof Name)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } Name baseName = (Name) baseTarget; AstNode parent = a.getParent(); if (!(parent instanceof ExpressionStatement)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } Node prev = getPredecessorNode(parent); if (prev instanceof FunctionNode) { FunctionNode fn = (FunctionNode) prev; String functionName = fn.getName(); String identifier = baseName.getIdentifier(); if (!functionName.equals(identifier)) { error("can only assign to prototype of function " + functionName + " here (line " + assignLineNo + ")", a); return; } } else if (prev instanceof ExpressionStatement) { // it needs to be an assignment either to C.prototype or C.prototype.foo // TODO clean up this gross code AstNode expression = ((ExpressionStatement)prev).getExpression(); if (!(expression instanceof Assignment)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } Assignment prevAssign = (Assignment) expression; AstNode prevLeft = prevAssign.getLeft(); if (!(prevLeft instanceof PropertyGet)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } PropertyGet prevPG = (PropertyGet) prevLeft; AstNode prevPGTarget = prevPG.getTarget(); if (prevPG.getProperty().getIdentifier().equals("prototype")) { checkForSameName(assignLineNo, baseName, prevPGTarget); } else if (prevPGTarget instanceof PropertyGet) { PropertyGet prevPGBasePG = (PropertyGet) prevPGTarget; if (!prevPGBasePG.getProperty().getIdentifier().equals("prototype")) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } checkForSameName(assignLineNo, baseName, prevPGBasePG.getTarget()); } else { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } } else { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } }
/** * for infix expressions, we ignore implicit coercions for now. For arithmetic * operators, we assume the type of the entire expression to be the same as that * of either operand. For comparison operators, we require operands to have * the same type, and assume that the result is a boolean. Note that Assignments * are also InfixExpressions and that some property-get operations show up as * InfixExpressions for which the operator is GETPROP. */ private ITypeTerm processInfixExpression(InfixExpression i) throws Error { int operator = i.getOperator(); AstNode leftOperand = i.getLeft(); AstNode rightOperand = i.getRight(); ITypeTerm iTerm = findOrCreateExpressionTerm(i); switch (operator){ case Token.GETPROP: return processPropertyGet(i, leftOperand, rightOperand); case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_RSH: processAssignment((Assignment)i); return iTerm; case Token.ADD: case Token.SUB: case Token.MUL: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.EQ: case Token.LE: case Token.LT: case Token.NE: case Token.GT: case Token.GE: case Token.SHNE: case Token.SHEQ: case Token.AND: case Token.OR: case Token.BITAND: case Token.BITXOR: case Token.LSH: case Token.RSH: case Token.URSH: case Token.IN: ITypeTerm leftTerm = processExpression(leftOperand); ITypeTerm rightTerm = processExpression(rightOperand); OperatorTerm opTerm = findOrCreateOperatorTerm(RhinoToIR.decodeRhinoOperator(operator), leftTerm, rightTerm, i.getLineno()); addTypeEqualityConstraint(iTerm, opTerm, i.getLineno(), (solution) -> binaryOperatorMisuse(i, solution.typeOfTerm(leftTerm), solution.typeOfTerm(rightTerm), solution.typeOfTerm(opTerm))); break; default: error("unexpected infix operator: " + operator + "(" + RhinoToIR.decodeRhinoOperator(operator) + ") in " + i.toSource(), i); } theMap.put(i, iTerm); return iTerm; }
private void visitAssignment(final Assignment node) { if (node.getLeft() instanceof PropertyGet) { visitAssignmentAtRoot(node); } }
private boolean ingoreAbstractMethod(final Assignment node) { return node.getRight() instanceof PropertyGet && !"goog.abstractMethod".equals( ((PropertyGet) node.getRight()).toSource()); }