/** * generate constraints for a for-in loop */ private void processForInLoop(ForInLoop loop) { AstNode it = loop.getIterator(); AstNode obj = loop.getIteratedObject(); ITypeTerm objTerm = processExpression(obj); MapType mapType = new MapType(factory.freshTypeVar()); addSubTypeConstraint( objTerm, findOrCreateTypeTerm(mapType, loop.getLineno()), loop.getLineno(), (solution) -> typeEqualityError("for-in loop can only iterate over map objects; " + obj.toSource() + " is not a map", solution.typeOfTerm(objTerm), mapType, locationOf(obj))); if (it instanceof VariableDeclaration){ VariableDeclaration vd = (VariableDeclaration)it; for (VariableInitializer vi : vd.getVariables()){ AstNode target = vi.getTarget(); if (target instanceof Name){ ITypeTerm leftTerm = findOrCreateNameDeclarationTerm((Name)target); addSubTypeConstraint( leftTerm, findOrCreateTypeTerm(StringType.make(), loop.getLineno()), loop.getLineno(), (solution) -> typeEqualityError( "loop variable " + it.toSource() + " of for-in loop must have string type", solution.typeOfTerm(leftTerm), StringType.make(), locationOf(it))); } } } else { error("unhandled type of iterator in for-in loop: " + it.getClass().getName(), it); } }
/** * generate constraints for initializers */ private void processVariableInitializer(AstNode node) throws Error { VariableInitializer vi = (VariableInitializer)node; AstNode varname = vi.getTarget(); AstNode initializer = vi.getInitializer(); if (varname instanceof Name){ ITypeTerm leftTerm = findOrCreateNameDeclarationTerm((Name)varname); // declaration of variable, so no need to create ITerm for expression if (initializer != null){ ITypeTerm rightTerm = processExpression(initializer); processCopy(initializer, rightTerm, leftTerm, node.getLineno(), (solution) -> subtypeError("bad initialization of " + varname.toSource(), solution.typeOfTerm(rightTerm), solution.typeOfTerm(leftTerm), locationOf(node))); } // in case of multiple declarations of same identifier, // equate the type of this particular Name to the type of // the canonical Name ITypeTerm localTerm = factory.findOrCreateNameDeclTermNoLookup((Name) varname); if (localTerm != leftTerm) { addTypeEqualityConstraint(localTerm, leftTerm, node.getLineno(), (solution) -> typeEqualityError("different declarations within the same scope must have the same type", solution.typeOfTerm(localTerm), solution.typeOfTerm(leftTerm), locationOf(node))); } } else { error("unsupported type of VariableInitializer", varname); } }
private void print(VariableInitializer node) throws IOException { print(node.getTarget()); if (node.getInitializer() != null) { writer.ws().append('=').ws(); print(node.getInitializer()); } }
/** * Extract variable from node and add to code block */ private void processVariableNode(Node child, CodeBlock block, Set<Completion> set, String entered, int offset) { //check block can resolve variable or is pre-processing variables if(block.contains(dot) || isPreProcessing()) { VariableDeclaration varDec = (VariableDeclaration) child; List<VariableInitializer> vars = varDec.getVariables(); for (VariableInitializer var : vars) { extractVariableFromNode(var, block, offset); } } }
/** * Extract the variable from the Variable initializer and set the Type * * @param initializer AstNode from which to extract the variable * @param block code block to add the variable too * @param offset position of the variable in code */ private void extractVariableFromNode(VariableInitializer initializer, CodeBlock block, int offset) { AstNode target = initializer.getTarget(); if (target != null) { addVariableToResolver(target, initializer.getInitializer(), block, offset); } }
/** * Extract variable for each in loop. If the iteratedObject is an Array, * then resolve the variable to the array type otherwise set to iterator * object type * * @param initializer * @param block * @param offset * @param iteratedObject */ private void extractVariableForForEach(VariableInitializer initializer, CodeBlock block, int offset, AstNode iteratedObject) { AstNode target = initializer.getTarget(); if (target != null) { JavaScriptVariableDeclaration dec = extractVariableFromNode(target, block, offset); if (dec != null && iteratedObject != null && JavaScriptHelper.canResolveVariable(target, iteratedObject)) { // resolve the iterated object JavaScriptResolver resolver = provider.getJavaScriptEngine() .getJavaScriptResolver(provider); if (resolver != null) { TypeDeclaration iteratorDec = resolver .resolveNode(iteratedObject); if (iteratorDec instanceof ArrayTypeDeclaration) { // set type to array type dec dec .setTypeDeclaration(((ArrayTypeDeclaration) iteratorDec) .getArrayType()); } else { dec.setTypeDeclaration(iteratorDec); } } if (canAddVariable(block)) { provider.getVariableResolver().addLocalVariable(dec); } } } }
/** * Extract variable for in loop. If the iteratedObject is an Array, then * assume the variable to be a number otherwise do not attempt to resolve * * @param initializer * @param block * @param offset * @param iteratedObject */ private void extractVariableForForIn(VariableInitializer initializer, CodeBlock block, int offset, AstNode iteratedObject) { AstNode target = initializer.getTarget(); if (target != null) { JavaScriptVariableDeclaration dec = extractVariableFromNode(target, block, offset); if (dec != null && iteratedObject != null && JavaScriptHelper.canResolveVariable(target, iteratedObject)) { // resolve the iterated object JavaScriptResolver resolver = provider.getJavaScriptEngine() .getJavaScriptResolver(provider); if (resolver != null) { TypeDeclaration iteratorDec = resolver .resolveNode(iteratedObject); if (iteratorDec instanceof ArrayTypeDeclaration) { // always assume a number for arrays dec.setTypeDeclaration(provider.getTypesFactory().getTypeDeclaration( TypeDeclarations.ECMA_NUMBER)); } else { dec.setTypeDeclaration(provider.getTypesFactory() .getDefaultTypeDeclaration()); } } if (canAddVariable(block)) { provider.getVariableResolver().addLocalVariable(dec); } } } }
private Node transformVariableInitializers(VariableDeclaration node) { List<VariableInitializer> vars = node.getVariables(); int size = vars.size(), i = 0; for (VariableInitializer var : vars) { AstNode target = var.getTarget(); AstNode init = var.getInitializer(); Node left = null; if (var.isDestructuring()) { decompile(target); // decompile but don't transform left = target; } else { left = transform(target); } Node right = null; if (init != null) { decompiler.addToken(Token.ASSIGN); right = transform(init); } if (var.isDestructuring()) { if (right == null) { // TODO: should this ever happen? node.addChildToBack(left); } else { Node d = createDestructuringAssignment(node.getType(), left, right); node.addChildToBack(d); } } else { if (right != null) { left.addChildToBack(right); } node.addChildToBack(left); } if (i++ < size-1) { decompiler.addToken(Token.COMMA); } } return node; }
/** * 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; }