/** * create constraint variable for the array literal. create subtype constraints between expressions * in the literal and the array's element type */ private ITypeTerm processArrayLiteral(ArrayLiteral lit) { ITypeTerm arrayTerm = findOrCreateArrayLiteralTerm(lit); ITypeTerm elemTerm = findOrCreateIndexedTerm(arrayTerm, lit.getLineno()); List<AstNode> elements = lit.getElements(); for (AstNode litElem : elements){ ITypeTerm litElemTerm = processExpression(litElem); processCopy(litElem, litElemTerm, elemTerm, lit.getLineno(), (solution) -> subtypeError("array cannot contain " + shortSrc(litElem), solution.typeOfTerm(litElemTerm), solution.typeOfTerm(elemTerm), locationOf(litElem))); } createArrayConstraints(arrayTerm, lit); return arrayTerm; }
/** * Create array type from AstNode and try to determine the array type * @param typeNode * @param provider * @return */ private static TypeDeclaration createArrayType(AstNode typeNode, SourceCompletionProvider provider) { TypeDeclaration array = getTypeDeclaration(TypeDeclarations.ECMA_ARRAY, provider); if(array != null) { //create a new ArrayTypeDeclaration ArrayTypeDeclaration arrayDec = new ArrayTypeDeclaration(array.getPackageName(), array.getAPITypeName(), array.getJSName()); ArrayLiteral arrayLit = (ArrayLiteral) typeNode; //iterate through array and resolve the underlying types arrayDec.setArrayType(findArrayType(arrayLit, provider)); return arrayDec; } else { return null; } }
/** * Find the array type from ArrayLiteral. Iterates through elements and checks all the types are the same * @param arrayLit * @param provider * @return TypeDeclaration if all elements are of the same type else TypeDeclarationFactory.getDefaultTypeDeclaration(); */ private static TypeDeclaration findArrayType(ArrayLiteral arrayLit, SourceCompletionProvider provider) { TypeDeclaration dec = null; boolean first = true; JavaScriptResolver resolver = provider.getJavaScriptEngine().getJavaScriptResolver(provider); for (AstNode element : arrayLit.getElements()) { TypeDeclaration elementType = resolver.resolveNode(element); if(first) { dec = elementType; first = false; } else { if(elementType != null && !elementType.equals(dec)) { dec = provider.getTypesFactory().getDefaultTypeDeclaration(); break; } } } return dec != null ? dec : provider.getTypesFactory().getDefaultTypeDeclaration(); }
private Node transformArrayLiteral(ArrayLiteral node) { if (node.isDestructuring()) { return node; } decompiler.addToken(Token.LB); List<AstNode> elems = node.getElements(); Node array = new Node(Token.ARRAYLIT); List<Integer> skipIndexes = null; for (int i = 0; i < elems.size(); ++i) { AstNode elem = elems.get(i); if (elem.getType() != Token.EMPTY) { array.addChildToBack(transform(elem)); } else { if (skipIndexes == null) { skipIndexes = new ArrayList<Integer>(); } skipIndexes.add(i); } if (i < elems.size() - 1) decompiler.addToken(Token.COMMA); } decompiler.addToken(Token.RB); array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, node.getDestructuringLength()); if (skipIndexes != null) { int[] skips = new int[skipIndexes.size()]; for (int i = 0; i < skipIndexes.size(); i++) skips[i] = skipIndexes.get(i); array.putProp(Node.SKIP_INDEXES_PROP, skips); } return array; }
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())); } }
void decompileArrayLiteral(ArrayLiteral node) { decompiler.addToken(Token.LB); List<AstNode> elems = node.getElements(); int size = elems.size(); for (int i = 0; i < size; i++) { AstNode elem = elems.get(i); decompile(elem); if (i < size - 1) { decompiler.addToken(Token.COMMA); } } decompiler.addToken(Token.RB); }
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()); } }
boolean destructuringArray(ArrayLiteral array, int variableType, String tempName, Node parent, List<String> destructuringNames) { boolean empty = true; int setOp = variableType == Token.CONST ? Token.SETCONST : Token.SETNAME; int index = 0; for (AstNode n : array.getElements()) { if (n.getType() == Token.EMPTY) { index++; continue; } Node rightElem = new Node(Token.GETELEM, createName(tempName), createNumber(index)); if (n.getType() == Token.NAME) { String name = n.getString(); parent.addChildToBack(new Node(setOp, createName(Token.BINDNAME, name, null), rightElem)); if (variableType != -1) { defineSymbol(variableType, name, true); destructuringNames.add(name); } } else { parent.addChildToBack (destructuringAssignmentHelper (variableType, n, rightElem, currentScriptOrFn.getNextTempName())); } index++; empty = false; } return empty; }
/** * Find or create a term representing the type of an array literal */ public ITypeTerm findOrCreateArrayLiteralTerm(ArrayLiteral n) { if (!arrayLiteralTerms.containsKey(n)){ arrayLiteralTerms.put(n, new ArrayLiteralTerm(n)); } return arrayLiteralTerms.get(n); }
/** * Returns string array literal values. */ private List<String> arrayStringLiteral(final AstNode node) { checkState(node instanceof ArrayLiteral, node, "Expected array literal only"); List<String> result = new ArrayList<>(); //noinspection ConstantConditions ArrayLiteral array = (ArrayLiteral) node; for (AstNode element : array.getElements()) { result.add(stringLiteral(element)); } return result; }
/** * Returns string literal or array of string literals. * * @see #stringLiteral(AstNode) * @see #arrayStringLiteral(AstNode) */ private List<String> stringLiterals(final AstNode node) { // string literal or array of string literals if (node instanceof StringLiteral) { return Collections.singletonList(stringLiteral(node)); } else if (node instanceof ArrayLiteral) { return arrayStringLiteral(node); } else { throw reportError(node, "Expected string literal or array of string literal only"); } }
/** * Generate IR for a for..in loop. */ private Node createForIn(int declType, Node loop, Node lhs, Node obj, Node body, boolean isForEach, boolean isForOf) { int destructuring = -1; int destructuringLen = 0; Node lvalue; int type = lhs.getType(); if (type == Token.VAR || type == Token.LET) { Node kid = lhs.getLastChild(); int kidType = kid.getType(); if (kidType == Token.ARRAYLIT || kidType == Token.OBJECTLIT) { type = destructuring = kidType; lvalue = kid; destructuringLen = 0; if (kid instanceof ArrayLiteral) destructuringLen = ((ArrayLiteral) kid).getDestructuringLength(); } else if (kidType == Token.NAME) { lvalue = Node.newString(Token.NAME, kid.getString()); } else { reportError("msg.bad.for.in.lhs"); return null; } } else if (type == Token.ARRAYLIT || type == Token.OBJECTLIT) { destructuring = type; lvalue = lhs; destructuringLen = 0; if (lhs instanceof ArrayLiteral) destructuringLen = ((ArrayLiteral) lhs).getDestructuringLength(); } else { lvalue = makeReference(lhs); if (lvalue == null) { reportError("msg.bad.for.in.lhs"); return null; } } Node localBlock = new Node(Token.LOCAL_BLOCK); int initType = isForEach ? Token.ENUM_INIT_VALUES : isForOf ? Token.ENUM_INIT_VALUES_IN_ORDER : (destructuring != -1 ? Token.ENUM_INIT_ARRAY : Token.ENUM_INIT_KEYS); Node init = new Node(initType, obj); init.putProp(Node.LOCAL_BLOCK_PROP, localBlock); Node cond = new Node(Token.ENUM_NEXT); cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock); Node id = new Node(Token.ENUM_ID); id.putProp(Node.LOCAL_BLOCK_PROP, localBlock); Node newBody = new Node(Token.BLOCK); Node assign; if (destructuring != -1) { assign = createDestructuringAssignment(declType, lvalue, id); if (!isForEach && !isForOf && (destructuring == Token.OBJECTLIT || destructuringLen != 2)) { // destructuring assignment is only allowed in for..each or // with an array type of length 2 (to hold key and value) reportError("msg.bad.for.in.destruct"); } } else { assign = simpleAssignment(lvalue, id); } newBody.addChildToBack(new Node(Token.EXPR_VOID, assign)); newBody.addChildToBack(body); loop = createLoop((Jump)loop, LOOP_WHILE, newBody, cond, null, null); loop.addChildToFront(init); if (type == Token.VAR || type == Token.LET) loop.addChildToFront(lhs); localBlock.addChildToBack(loop); return localBlock; }
/** * May return an {@link ArrayLiteral} or {@link ArrayComprehension}. */ private AstNode arrayLiteral() throws IOException { if (currentToken != Token.LB) codeBug(); int pos = ts.tokenBeg, end = ts.tokenEnd; List<AstNode> elements = new ArrayList<AstNode>(); ArrayLiteral pn = new ArrayLiteral(pos); boolean after_lb_or_comma = true; int afterComma = -1; int skipCount = 0; for (;;) { int tt = peekToken(); if (tt == Token.COMMA) { consumeToken(); afterComma = ts.tokenEnd; if (!after_lb_or_comma) { after_lb_or_comma = true; } else { elements.add(new EmptyExpression(ts.tokenBeg, 1)); skipCount++; } } else if (tt == Token.RB) { consumeToken(); // for ([a,] in obj) is legal, but for ([a] in obj) is // not since we have both key and value supplied. The // trick is that [a,] and [a] are equivalent in other // array literal contexts. So we calculate a special // length value just for destructuring assignment. end = ts.tokenEnd; pn.setDestructuringLength(elements.size() + (after_lb_or_comma ? 1 : 0)); pn.setSkipCount(skipCount); if (afterComma != -1) warnTrailingComma(pos, elements, afterComma); break; } else if (tt == Token.FOR && !after_lb_or_comma && elements.size() == 1) { return arrayComprehension(elements.get(0), pos); } else if (tt == Token.EOF) { reportError("msg.no.bracket.arg"); break; } else { if (!after_lb_or_comma) { reportError("msg.no.bracket.arg"); } elements.add(assignExpr()); after_lb_or_comma = false; afterComma = -1; } } for (AstNode e : elements) { pn.addElement(e); } pn.setLength(end - pos); return pn; }
Node destructuringAssignmentHelper(int variableType, Node left, Node right, String tempName) { Scope result = createScopeNode(Token.LETEXPR, left.getLineno()); result.addChildToFront(new Node(Token.LET, createName(Token.NAME, tempName, right))); try { pushScope(result); defineSymbol(Token.LET, tempName, true); } finally { popScope(); } Node comma = new Node(Token.COMMA); result.addChildToBack(comma); List<String> destructuringNames = new ArrayList<String>(); boolean empty = true; switch (left.getType()) { case Token.ARRAYLIT: empty = destructuringArray((ArrayLiteral)left, variableType, tempName, comma, destructuringNames); break; case Token.OBJECTLIT: empty = destructuringObject((ObjectLiteral)left, variableType, tempName, comma, destructuringNames); break; case Token.GETPROP: case Token.GETELEM: switch (variableType) { case Token.CONST: case Token.LET: case Token.VAR: reportError("msg.bad.assign.left"); } comma.addChildToBack(simpleAssignment(left, createName(tempName))); break; default: reportError("msg.bad.assign.left"); } if (empty) { // Don't want a COMMA node with no children. Just add a zero. comma.addChildToBack(createNumber(0)); } result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames); return result; }
public ArrayLiteralTerm(ArrayLiteral n){ super(n); this.type = new ArrayType(new AnyType()); }
/** * 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 ITypeTerm findOrCreateArrayLiteralTerm(ArrayLiteral lit){ ITypeTerm t = factory.findOrCreateArrayLiteralTerm(lit); generator.addTermLineNumber(t, lit.getLineno()); return t; }
private void print(ArrayLiteral node) throws IOException { writer.append('['); printList(node.getElements()); writer.append(']'); }