/** * call a function through a closure */ private void processClosureCall(FunctionCall fc) { AstNode target = fc.getTarget(); ITypeTerm funVar = processExpression(target); // for call foo(E_1,...,E_n), equate the type of the call to ret(foo) FunctionCallTerm callTerm = findOrCreateFunctionCallTerm(fc); callTerm.setTarget(funVar); ITypeTerm retTerm = findOrCreateFunctionReturnTerm(funVar, fc.getArguments().size(), fc.getLineno(), fc); addTypeEqualityConstraint(callTerm, retTerm, fc.getLineno(), null); // for call foo(E_1,...,E_n), generate constraints |E_i| <: Param(foo,i) for (int i=0; i < fc.getArguments().size(); i++){ AstNode arg = fc.getArguments().get(i); ITypeTerm argExp = processExpression(arg); ITypeTerm paramExp = findOrCreateFunctionParamTerm(funVar, i, fc.getArguments().size(), fc.getLineno()); processCopy(arg, argExp, paramExp, fc.getLineno(), (solution) -> subtypeError("bad argument " + shortSrc(arg) + " passed to function", solution.typeOfTerm(argExp), solution.typeOfTerm(paramExp), locationOf(arg))); } }
private MethodReference getJavaMethodSelector(AstNode node) { if (!(node instanceof FunctionCall)) { return null; } FunctionCall call = (FunctionCall) node; if (!isJavaMethodRepository(call.getTarget())) { return null; } if (call.getArguments().size() != 1) { diagnostics.error(location, "javaMethods.get method should take exactly one argument"); return null; } StringBuilder nameBuilder = new StringBuilder(); if (!extractMethodName(call.getArguments().get(0), nameBuilder)) { diagnostics.error(location, "javaMethods.get method should take string constant"); return null; } MethodReference method = MethodReference.parseIfPossible(nameBuilder.toString()); if (method == null) { diagnostics.error(location, "Wrong method reference: " + nameBuilder); } return method; }
private boolean tryJavaInvocation(FunctionCall node) throws IOException { if (!(node.getTarget() instanceof PropertyGet)) { return false; } PropertyGet propertyGet = (PropertyGet) node.getTarget(); String callMethod = getJavaMethod(propertyGet.getTarget()); if (callMethod == null || !propertyGet.getProperty().getIdentifier().equals("invoke")) { return false; } MethodReference method = MethodReference.parseIfPossible(callMethod); if (method == null) { return false; } writer.appendMethodBody(method).append('('); printList(node.getArguments()); writer.append(')'); return true; }
private String createLookupString(FunctionCall fn) { StringBuilder sb = new StringBuilder(); String name = ""; switch(fn.getTarget().getType()) { case Token.NAME : name = ((Name) fn.getTarget()).getIdentifier(); break; } sb.append(name); sb.append("("); Iterator<AstNode> i = fn.getArguments().iterator(); while (i.hasNext()) { i.next(); sb.append("p"); if(i.hasNext()) sb.append(","); } sb.append(")"); return sb.toString(); }
@Override public String getFunctionNameLookup(FunctionCall call, SourceCompletionProvider provider) { if (call != null) { StringBuilder sb = new StringBuilder(); if (call.getTarget() instanceof PropertyGet) { PropertyGet get = (PropertyGet) call.getTarget(); sb.append(get.getProperty().getIdentifier()); } sb.append("("); int count = call.getArguments().size(); for (int i = 0; i < count; i++) { sb.append("p"); if (i < count - 1) { sb.append(","); } } sb.append(")"); return sb.toString(); } return null; }
private Node transformFunctionCall(FunctionCall node) { Node call = createCallOrNew(Token.CALL, transform(node.getTarget())); call.setLineno(node.getLineno()); decompiler.addToken(Token.LP); List<AstNode> args = node.getArguments(); for (int i = 0; i < args.size(); i++) { AstNode arg = args.get(i); call.addChildToBack(transform(arg)); if (i < args.size() - 1) { decompiler.addToken(Token.COMMA); } } decompiler.addToken(Token.RP); return call; }
/** * Finds the declaration of the called function. Assumes that the call's target * is a Name, which is passed as the second parameter. * */ static FunctionNode findFunDecl(FunctionCall fc, Name funName){ List<FunctionNode> funsFound = findFunDecl2(fc, new ArrayList<FunctionNode>()); for (int i=0; i < funsFound.size(); i++){ FunctionNode fun = funsFound.get(i); if (funName.getIdentifier().equals(fun.getName())){ return fun; } } return null; }
/** * Syntactically identify module imports */ private boolean isSyntacticModuleRequire(FunctionCall fc) { AstNode target = fc.getTarget(); if (target instanceof Name) { Name name = (Name)target; return name.getIdentifier().equals("require") && fc.getArguments().size() == 1 && fc.getArguments().get(0) instanceof StringLiteral; } else { return false; } }
@Override public boolean visit(AstNode node) { if( Token.NAME == node.getType()){ if(node.getParent().getType() == Token.CALL){ FunctionCall parent = (FunctionCall)node.getParent(); if(parent.getTarget()==node){ resultSoFar.add(safeGetString(node)); } } } return true; }
@Override public boolean visit(AstNode node) { if (node instanceof FunctionCall) { return visit((FunctionCall) node); } return true; }
private void print(FunctionCall node, int precedence) throws IOException { if (tryJavaInvocation(node)) { return; } if (precedence < PRECEDENCE_FUNCTION) { writer.append('('); } int innerPrecedence = node instanceof NewExpression ? PRECEDENCE_FUNCTION - 1 : PRECEDENCE_FUNCTION; if (node instanceof NewExpression) { writer.append("new "); } print(node.getTarget(), innerPrecedence); writer.append('('); printList(node.getArguments()); writer.append(')'); if (node instanceof NewExpression) { writer.ws(); NewExpression newExpr = (NewExpression) node; if (newExpr.getInitializer() != null) { print(newExpr.getInitializer()); } } if (precedence < PRECEDENCE_FUNCTION) { writer.append(')'); } }
/** * Iterate back up through parent nodes and check whether inside a function * * If the node is a function, then the Parsed parent node structure is: * FunctionCall * --> PropertyGet * --> Name * * Anything other structure should be rejected. * * @param node * @return */ public static FunctionCall findFunctionCallFromNode(AstNode node) { AstNode parent = node; for(int i=0; i<3; i++) { if(parent == null || (parent instanceof AstRoot)) break; if (parent instanceof FunctionCall) { return (FunctionCall) parent; } parent = parent.getParent(); } return null; }
@Override public String getFunctionNameLookup(FunctionCall call, SourceCompletionProvider provider) { if (call != null) { StringBuilder sb = new StringBuilder(); if (call.getTarget() instanceof PropertyGet) { PropertyGet get = (PropertyGet) call.getTarget(); sb.append(get.getProperty().getIdentifier()); } sb.append("("); int count = call.getArguments().size(); for (int i = 0; i < count; i++) { AstNode paramNode = call.getArguments().get(i); JavaScriptResolver resolver = provider.getJavaScriptEngine().getJavaScriptResolver(provider); Logger.log("PARAM: " + JavaScriptHelper.convertNodeToSource(paramNode)); try { TypeDeclaration type = resolver.resolveParamNode(JavaScriptHelper.convertNodeToSource(paramNode)); String resolved = type != null ? type.getQualifiedName() : "any"; sb.append(resolved); if (i < count - 1) { sb.append(","); } } catch(IOException io){io.printStackTrace();} } sb.append(")"); return sb.toString(); } return null; }
/** * Get all nodes within AstNode and add to an ArrayList * * @param node */ private void collectAllNodes(AstNode node) { if (node.getType() == Token.CALL) { // collect all argument nodes FunctionCall call = (FunctionCall) node; Iterator<AstNode> args = call.getArguments().iterator(); while (args.hasNext()) { AstNode arg = args.next(); VisitorAll all = new VisitorAll(); arg.visit(all); paramNodes.addAll(all.getAllNodes()); } } }
/** * Check the function that a name may belong to contains this actual * parameter * * @param node Node to check * @return true if the function contains the parameter */ private boolean isParameter(AstNode node) { if (paramNodes.contains(node)) return true; // get all params from this function too FunctionCall fc = JavaScriptHelper.findFunctionCallFromNode(node); if (fc != null && !(node == fc)) { collectAllNodes(fc); if (paramNodes.contains(node)) { return true; } } return false; }
public FunctionCallTerm(FunctionCall n){ super(n); this.type = new AnyType(); }
public FunctionCall getFunctionCall(){ return (FunctionCall)getNode(); }
public FunctionCallTerm findOrCreateFunctionCallTerm(FunctionCall n) { if (!functionCallTerms.containsKey(n)){ functionCallTerms.put(n, new FunctionCallTerm(n)); } return functionCallTerms.get(n); }
/** * 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 FunctionCallTerm findOrCreateFunctionCallTerm(FunctionCall fc){ FunctionCallTerm t = factory.findOrCreateFunctionCallTerm(fc); generator.addTermLineNumber(t, fc.getLineno()); return t; }
private boolean visit(FunctionCall call) { if (!(call.getTarget() instanceof PropertyGet)) { return true; } PropertyGet propertyGet = (PropertyGet) call.getTarget(); MethodReference methodRef = getJavaMethodSelector(propertyGet.getTarget()); if (methodRef == null || !propertyGet.getProperty().getIdentifier().equals("invoke")) { return true; } for (AstNode arg : call.getArguments()) { arg.visit(this); } MethodReader method = classSource.resolve(methodRef); if (method == null) { diagnostics.error(location, "Java method not found: {{m0}}", methodRef); return false; } int requiredParams = methodRef.parameterCount(); if (!method.hasModifier(ElementModifier.STATIC)) { ++requiredParams; } if (call.getArguments().size() != requiredParams) { diagnostics.error(location, "Invalid number of arguments for method {{m0}}. Expected: " + requiredParams + ", encountered: " + call.getArguments().size(), methodRef); } MethodReference caller = createCallbackMethod(method); MethodReference delegate = repository.methodMap.get(location.getMethod()); repository.callbackCallees.put(caller, methodRef); repository.callbackMethods.computeIfAbsent(delegate, key -> new HashSet<>()).add(caller); validateSignature(method); StringLiteral newTarget = new StringLiteral(); newTarget.setValue("$$JSO$$_" + caller); propertyGet.setTarget(newTarget); return false; }
@Override public boolean visit(AstNode node) { Logger.log(JavaScriptHelper.convertNodeToSource(node)); Logger.log(node.shortName()); if(!validNode(node)) { //invalid node found, set last completion invalid and stop processing lastJavaScriptType = null; return false; } if (ignore(node, ignoreParams)) return true; JavaScriptType jsType = null; TypeDeclaration dec = null; //only resolve native type if last type is null //otherwise it can be assumed that this is part of multi depth - e.g "".length.toString() if(lastJavaScriptType == null) { dec = resolveNativeType(node); if(dec == null && node.getType() == Token.NAME) { lastJavaScriptType = null; return false; } } else { dec = resolveTypeFromLastJavaScriptType(node); } if (dec != null) { // lookup JavaScript completions type jsType = provider.getJavaScriptTypesFactory().getCachedType( dec, provider.getJarManager(), provider, JavaScriptHelper.convertNodeToSource(node)); if (jsType != null) { lastJavaScriptType = jsType; // stop here return false; } } else if (lastJavaScriptType != null) { if (node.getType() == Token.NAME) { // lookup from source name jsType = lookupFromName(node, lastJavaScriptType); if (jsType == null) { // lookup name through the functions of // lastJavaScriptType jsType = lookupFunctionCompletion(node, lastJavaScriptType); } lastJavaScriptType = jsType; } } else if(node instanceof FunctionCall) { FunctionCall fn = (FunctionCall) node; String lookupText = createLookupString(fn); JavaScriptFunctionDeclaration funcDec = provider.getVariableResolver().findFunctionDeclaration(lookupText); if(funcDec != null) { jsType = provider.getJavaScriptTypesFactory().getCachedType( funcDec.getTypeDeclaration(), provider.getJarManager(), provider, JavaScriptHelper.convertNodeToSource(node)); if (jsType != null) { lastJavaScriptType = jsType; // stop here return false; } } } return true; }
/** * for function calls, return a constraint variable that corresponds to the * callee's return type. The generation of constraints that require the type of each actual * parameter to be a subtype of the type of the corresponding formal parameter happens * in processFunctionCallParams */ private ITypeTerm processFunctionCallExpression(FunctionCall fc) { return findOrCreateFunctionCallTerm(fc); }
/** * @param node AstNode to look for function * @return function lookup name from it's AstNode. i.e concat function name * and parameters. If no function is found, then return null */ public static String getFunctionNameLookup(AstNode node, SourceCompletionProvider provider) { FunctionCall call = findFunctionCallFromNode(node); return provider.getJavaScriptEngine().getJavaScriptResolver(provider).getFunctionNameLookup(call, provider); }
/** * Returns same string format as {@link #getLookupText(JSMethodData, String)} but from AstNode Function * @param call * @param provider * @return */ public abstract String getFunctionNameLookup(FunctionCall call, SourceCompletionProvider provider);