@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; }
/** * Check if an AstNode corresponds to a boolean constant. */ static boolean isBooleanConstant(AstNode node){ if (node instanceof KeywordLiteral){ KeywordLiteral kw = (KeywordLiteral)node; if (kw.toSource().equals("true") || kw.toSource().equals("false")){ return true; } } return false; }
/** * Check if an AstNode corresponds to "null" */ static boolean isNullConstant(AstNode node){ if (node instanceof KeywordLiteral){ KeywordLiteral kw = (KeywordLiteral)node; if (kw.toSource().equals("null")){ return true; } } return false; }
/** * Check if an AstNode corresponds to the "this" reference. * * @param node * @return */ public static boolean isThis(AstNode node){ if (node instanceof KeywordLiteral){ KeywordLiteral kw = (KeywordLiteral)node; if (kw.toSource().equals("this")){ return true; } } return false; }
/** * 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))); }
/** * Generate constraints for the (possibly nested) receiver of a property-get expression. */ private ITypeTerm generateReceiverConstraints(PropertyGet pg) throws Error { AstNode base = pg.getTarget(); ITypeTerm baseTerm = findOrCreateExpressionTerm(base); // make term for expression if (base instanceof Name) { Name name = (Name)base; ITypeTerm nameTerm = findOrCreateNameDeclarationTerm(name); // find unique representative for referenced Name addTypeEqualityConstraint(baseTerm, nameTerm, base.getLineno(), null); } else if (base instanceof PropertyGet) { PropertyGet basePG = (PropertyGet)base; ITypeTerm bbaseTerm = generateReceiverConstraints(basePG); String baseProperty = basePG.getProperty().getIdentifier(); ITypeTerm basePATerm; if (basePG.getProperty().getIdentifier().equals("prototype")){ basePATerm = findOrCreateProtoTerm(bbaseTerm, pg.getLineno()); } else { basePATerm = findOrCreatePropertyAccessTerm(bbaseTerm, baseProperty, basePG); } addTypeEqualityConstraint(baseTerm, basePATerm, basePG.getLineno(), null); } else if (base instanceof KeywordLiteral && ConstraintGenUtil.isThis(base)){ processExpression(base); //error("unsupported property get with base this: "+pg.toSource()); } else if (base instanceof ElementGet) { ElementGet baseEGet = (ElementGet) base; processElementGet(baseEGet); } else { System.err.println("base = " + base.toSource() + ", type = " + base.getClass().getName() ); error("unsupported property get: " + pg.toSource(), pg); } return baseTerm; }
/** * 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; }
/** * Creates constraints for the subtree rooted at a designated expression node, * and returns a constraint variable corresponding to the root of the tree. */ private ITypeTerm processExpression(AstNode n){ ITypeTerm cached = theMap.get(n); if (cached != null) return cached; if (n instanceof Name){ return processVariableReference((Name)n); } else if (n instanceof NumberLiteral){ return processNumericConstant((NumberLiteral)n); } else if (n instanceof StringLiteral){ return processStringLiteral((StringLiteral)n); } else if (ConstraintGenUtil.isBooleanConstant(n)){ return processBooleanConstant(n); } else if (n instanceof UnaryExpression){ return processUnaryExpression((UnaryExpression)n); } else if (n instanceof InfixExpression){ return processInfixExpression((InfixExpression)n); } else if (n instanceof FunctionCall){ return processFunctionCallExpression((FunctionCall)n); } else if (n instanceof ArrayLiteral){ return processArrayLiteral((ArrayLiteral)n); } else if (n instanceof ElementGet){ return processElementGet((ElementGet)n); } else if (n instanceof ParenthesizedExpression) { return processParenthesizedExpression((ParenthesizedExpression)n); } else if (n instanceof ConditionalExpression) { return processConditionalExpression((ConditionalExpression)n); } else if (n instanceof ObjectLiteral) { return processObjectLiteral((ObjectLiteral)n); } else if (n instanceof KeywordLiteral){ return processKeywordLiteral((KeywordLiteral)n); } else if (n instanceof FunctionNode){ return processFunctionNode((FunctionNode)n); } else if (n instanceof EmptyExpression){ return processEmptyExpression((EmptyExpression)n); } else { System.err.println(n.toSource()); return expError("unimplemented case in findOrCreateExpressionVariable: " + n.getClass().getName(), n); } }