public static TypeErrorMessage binaryOperatorMisuse(InfixExpression node, Type t1, Type t2, Type outType) { TypeErrorMessage msg = genericTypeError("misuse of binary operator " + AstNode.operatorToString(node.getOperator()) + " in \"" + shortSrc(node) + '"', locationOf(node)); if (node.getOperator() == Token.IN) { if (!unconstrained(t1) && !Types.isEqual(t1, StringType.make())) { msg = msg.withNote("left operand has type " + describeType(t1) + " instead of " + StringType.make()); } if (!unconstrained(t2) && !Types.isMapType(t2)) { msg = msg.withNote("right operand has type " + describeType(t2) + " instead of " + new MapType(new DefaultType())); } } else { if (!unconstrained(t1)) { msg = msg.withNote("left operand has type " + describeType(t1)); } if (!unconstrained(t2)) { msg = msg.withNote("right operand has type " + describeType(t2)); } } if (!unconstrained(outType)) { msg = msg.withNote("result type is " + describeType(outType)); } return msg; }
public static TypeErrorMessage badPropertyRead(InfixExpression node, Type baseType, Type fieldType, Type expectedType) { assert node.getRight() instanceof Name; String fieldName = ((Name) node.getRight()).getIdentifier(); if (fieldType != null && hasProperty(baseType, fieldName)) { return genericTypeError("property '" + fieldName + "' does not have type " + describeType(expectedType), locationOf(node)) .withNote("type is " + describeType(fieldType)); } else if (baseType instanceof MapType) { return genericTypeError("cannot read property '" + fieldName + "' from map object " + shortSrc(node.getLeft()) + " using '.' syntax; write " + shortSrc(node.getLeft()) + "['" + fieldName + "'] instead", locationOf(node)); } else { return genericTypeError("read of missing property '" + fieldName + "' on " + shortSrc(node.getLeft()), locationOf(node)) .withNote("type is " + describeType(baseType)); } }
@Override public boolean visit(AstNode node) { if (!(node instanceof InfixExpression)) // ignore infix expression { JavaScriptResolver resolver = provider.getJavaScriptEngine().getJavaScriptResolver(provider); TypeDeclaration dec = resolver.resolveNode(node); boolean isNumber = TypeDeclarations.ECMA_NUMBER.equals(dec.getAPITypeName()) || TypeDeclarations.ECMA_BOOLEAN.equals(dec.getAPITypeName()); if(isNumber && (type == null || (isNumber && TypeDeclarations.ECMA_NUMBER.equals(type)))) { type = TypeDeclarations.ECMA_NUMBER; } else { type = TypeDeclarations.ECMA_STRING; } } return true; }
/** * Use a visitor to visit all the nodes to work out which type to return e.g * 1 + 1 returns Number 1 + "" returns String true returns Boolean etc.. * * @param node * @return */ private static TypeDeclaration getTypeFromInFixExpression(AstNode node, SourceCompletionProvider provider) { InfixExpression infix = (InfixExpression) node; switch(infix.getType()) { case Token.ADD: case Token.SUB: case Token.MOD: case Token.MUL: case Token.DIV: { InfixVisitor visitor = new InfixVisitor(provider); infix.visit(visitor); return getTypeDeclaration(visitor.type, provider); } } //else AstNode rightExp = infix.getRight(); JavaScriptResolver resolver = provider.getJavaScriptEngine().getJavaScriptResolver(provider); TypeDeclaration dec = resolver.resolveNode(rightExp); return dec; }
private Node transformInfix(InfixExpression node) { Node left = transform(node.getLeft()); decompiler.addToken(node.getType()); Node right = transform(node.getRight()); if (node instanceof XmlDotQuery) { decompiler.addToken(Token.RP); } return createBinary(node.getType(), left, right); }
private void arrowFunctionParams(FunctionNode fnNode, AstNode params, Map<String, Node> destructuring, Set<String> paramNames) { if (params instanceof ArrayLiteral || params instanceof ObjectLiteral) { markDestructuring(params); fnNode.addParam(params); String pname = currentScriptOrFn.getNextTempName(); defineSymbol(Token.LP, pname, false); destructuring.put(pname, params); } else if (params instanceof InfixExpression && params.getType() == Token.COMMA) { arrowFunctionParams(fnNode, ((InfixExpression)params).getLeft(), destructuring, paramNames); arrowFunctionParams(fnNode, ((InfixExpression)params).getRight(), destructuring, paramNames); } else if (params instanceof Name) { fnNode.addParam(params); String paramName = ((Name)params).getIdentifier(); defineSymbol(Token.LP, paramName); if (this.inUseStrictDirective) { if ("eval".equals(paramName) || "arguments".equals(paramName)) { reportError("msg.bad.id.strict", paramName); } if (paramNames.contains(paramName)) addError("msg.dup.param.strict", paramName); paramNames.add(paramName); } } else { reportError("msg.no.parm", params.getPosition(), params.getLength()); fnNode.addParam(makeErrorNode()); } }
private boolean extractMethodName(AstNode node, StringBuilder sb) { if (node.getType() == Token.ADD) { InfixExpression infix = (InfixExpression) node; return extractMethodName(infix.getLeft(), sb) && extractMethodName(infix.getRight(), sb); } else if (node.getType() == Token.STRING) { sb.append(((StringLiteral) node).getValue()); return true; } else { return false; } }
/** * Extract variable from binary operator e.g <, >, = etc... */ private void processInfix(Node child, CodeBlock block, Set<Completion> set, String entered, int offset) { InfixExpression epre = (InfixExpression) child; AstNode target = epre.getLeft(); if (canProcessNode(target)) { extractVariableFromNode(target, block, offset); addCodeBlock(epre, set, entered, block, offset); } }
/** * 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); } }
/** * 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 printInfix(InfixExpression node, int precedence) throws IOException { int innerPrecedence = getPrecedence(node.getType()); if (innerPrecedence > precedence) { writer.append('('); } int leftPrecedence; switch (node.getType()) { case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: leftPrecedence = innerPrecedence - 1; break; default: leftPrecedence = innerPrecedence; } print(node.getLeft(), leftPrecedence); String op = AstNode.operatorToString(node.getType()); boolean ws = requiresWhitespaces(node.getType()); if (ws || op.startsWith("-")) { writer.append(' '); } else { writer.ws(); } writer.append(op); if (ws) { writer.append(' '); } else { writer.ws(); } int rightPrecedence; switch (node.getType()) { case Token.DIV: case Token.MOD: case Token.SUB: case Token.LSH: case Token.RSH: case Token.URSH: case Token.IN: case Token.INSTANCEOF: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.GT: case Token.GE: case Token.LT: case Token.LE: rightPrecedence = innerPrecedence - 1; break; default: rightPrecedence = innerPrecedence; } print(node.getRight(), rightPrecedence); if (innerPrecedence > precedence) { writer.append(')'); } }
public static boolean isInfixOnly(AstNode typeNode) { return typeNode instanceof InfixExpression && typeNode.getClass().getName().equals(INFIX); }