/** * For an ElementGet a[x], generate constraint |a[x]| = Elem(a), and return Elem(a). * Also require the index to be an integer */ private ITypeTerm processElementGet(ElementGet eg) { AstNode target = eg.getTarget(); AstNode element = eg.getElement(); ITypeTerm egTerm = findOrCreateExpressionTerm(eg); ITypeTerm targetExp = processExpression(target); // for an expression e1[e2], generate constraint Elem(|e1|) = |e1[e2]| ITypeTerm elementVariable = findOrCreateIndexedTerm(targetExp, eg.getLineno()); addTypeEqualityConstraint(elementVariable, egTerm, eg.getLineno(), null); // for arrays we want to restrict the index expression to be an integer, and // for maps, we want to restrict it to be a string. Since we don't know at // constraint generation time whether an array or map is being indexed, we // generate a constraint that requires the index expression to be equal to // the "key type" of the base expression's type. The key type for an ArrayType // is integer, and the key type for a MapType is (for now) string. ITypeTerm indexTerm = processExpression(element); ITypeTerm keyTerm = findOrCreateKeyTerm(targetExp, eg.getLineno()); addTypeEqualityConstraint(indexTerm, keyTerm, eg.getLineno(), (solution) -> typeEqualityError("wrong key type used on " + describeNode(target, targetExp, solution), solution.typeOfTerm(indexTerm), solution.typeOfTerm(keyTerm), locationOf(eg))); return egTerm; }
/** * 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))); }
/** * Check the Get Element and extract the Array type from the variable * e.g * var a = [1, 2, 3]; * var b = a[1]; //b resolves to Number * @param node * @param provider * @return */ private static TypeDeclaration findGetElementType(AstNode node, SourceCompletionProvider provider) { ElementGet getElement = (ElementGet) node; //get target AstNode target = getElement.getTarget(); if(target != null) { JavaScriptCompletionResolver resolver = new JavaScriptCompletionResolver(provider); TypeDeclaration typeDec = resolver.resolveNode(target); if(typeDec != null) { if(typeDec instanceof ArrayTypeDeclaration) { return ((ArrayTypeDeclaration) typeDec).getArrayType(); } } } return null; }
private Node transformElementGet(ElementGet node) { // OPT: could optimize to createPropertyGet // iff elem is string that can not be number Node target = transform(node.getTarget()); decompiler.addToken(Token.LB); Node element = transform(node.getElement()); decompiler.addToken(Token.RB); return new Node(Token.GETELEM, target, element); }
void decompile(AstNode node) { switch (node.getType()) { case Token.ARRAYLIT: decompileArrayLiteral((ArrayLiteral)node); break; case Token.OBJECTLIT: decompileObjectLiteral((ObjectLiteral)node); break; case Token.STRING: decompiler.addString(((StringLiteral)node).getValue()); break; case Token.NAME: decompiler.addName(((Name)node).getIdentifier()); break; case Token.NUMBER: decompiler.addNumber(((NumberLiteral)node).getNumber()); break; case Token.GETPROP: decompilePropertyGet((PropertyGet)node); break; case Token.EMPTY: break; case Token.GETELEM: decompileElementGet((ElementGet) node); break; case Token.THIS: decompiler.addToken(node.getType()); break; default: Kit.codeBug("unexpected token: " + Token.typeToName(node.getType())); } }
/** * 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; }
@Override public boolean visit(AstNode node) { if (Token.GETELEM == node.getType()){ ElementGet gNode = (ElementGet)node; if( gNode.getTarget().getType()==Token.NAME && arrayName.equals(safeGetString(gNode.getTarget()))) { // foo["bar"] AstNode el = gNode.getElement(); resultSoFar.add(safeGetString(el)); } } return true; }
@Override public boolean visit(AstNode node) { switch (node.getType()) { case Token.GETPROP: // foo.bar (firstChild="foo", lastChild="bar") PropertyGet propertyGet = (PropertyGet)node; int propType = propertyGet.getProperty().getType(); if(propertyGet.getTarget().getType()==Token.NAME && (propType == Token.STRING || propType == Token.NAME) && objectName.equals(safeGetString(propertyGet.getTarget())) ){ resultSoFar.add(safeGetString(propertyGet.getProperty())); } break; case Token.GETELEM: // foo["bar"] ElementGet elemGet = (ElementGet)node; int elemType = elemGet.getElement().getType(); if(elemGet.getTarget().getType()==Token.NAME && (elemType == Token.STRING || elemType==Token.NAME) && objectName.equals(safeGetString(elemGet.getTarget())) ){ resultSoFar.add(safeGetString(elemGet.getElement())); } break; default: break; } return true; }
void decompileElementGet(ElementGet node) { decompile(node.getTarget()); decompiler.addToken(Token.LB); decompile(node.getElement()); decompiler.addToken(Token.RB); }
protected Node simpleAssignment(Node left, Node right) { int nodeType = left.getType(); switch (nodeType) { case Token.NAME: String name = ((Name) left).getIdentifier(); if (inUseStrictDirective && ("eval".equals(name) || "arguments".equals(name))) { reportError("msg.bad.id.strict", name); } left.setType(Token.BINDNAME); return new Node(Token.SETNAME, left, right); case Token.GETPROP: case Token.GETELEM: { Node obj, id; // If it's a PropertyGet or ElementGet, we're in the parse pass. // We could alternately have PropertyGet and ElementGet // override getFirstChild/getLastChild and return the appropriate // field, but that seems just as ugly as this casting. if (left instanceof PropertyGet) { obj = ((PropertyGet)left).getTarget(); id = ((PropertyGet)left).getProperty(); } else if (left instanceof ElementGet) { obj = ((ElementGet)left).getTarget(); id = ((ElementGet)left).getElement(); } else { // This branch is called during IRFactory transform pass. obj = left.getFirstChild(); id = left.getLastChild(); } int type; if (nodeType == Token.GETPROP) { type = Token.SETPROP; // TODO(stevey) - see https://bugzilla.mozilla.org/show_bug.cgi?id=492036 // The new AST code generates NAME tokens for GETPROP ids where the old parser // generated STRING nodes. If we don't set the type to STRING below, this will // cause java.lang.VerifyError in codegen for code like // "var obj={p:3};[obj.p]=[9];" id.setType(Token.STRING); } else { type = Token.SETELEM; } return new Node(type, obj, id, right); } case Token.GET_REF: { Node ref = left.getFirstChild(); checkMutableReference(ref); return new Node(Token.SET_REF, ref, right); } } throw codeBug(); }
/** * 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); } }
private void print(ElementGet node) throws IOException { print(node.getTarget(), PRECEDENCE_MEMBER); writer.append('['); print(node.getElement()); writer.append(']'); }