package depends.extractor.python.union; import depends.entity.*; import depends.entity.repo.IdGenerator; import depends.extractor.HandlerContext; import depends.extractor.python.PythonHandlerContext; import depends.extractor.python.PythonParser.*; import depends.extractor.python.PythonParserBaseVisitor; import depends.relations.IBindingResolver; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RuleContext; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ExpressionUsage { HandlerContext context; IdGenerator idGenerator; private boolean exprStarted=false; private IBindingResolver bindingResolver; public ExpressionUsage(PythonHandlerContext context, IdGenerator idGenerator, IBindingResolver bindingResolver) { this.context = context; this.idGenerator = idGenerator; this.bindingResolver = bindingResolver; } /** * Auto deduce variable type from assignment. for example: c = new C() then c is * type of C * */ private void deduceVarTypeInCaseOfAssignment(Expr_stmtContext expr, Expression expression) { List names = getName(expr.testlist_star_expr()); // TODO: should handle list properly; String varName = null; if (names.size() == 1) varName = names.get(0); if (varName == null) return; VarEntity var = context.lastContainer().lookupVarLocally(varName); if (var != null) { expression.addDeducedTypeVar(var); } } private List getName(Testlist_star_exprContext testlist_star_expr) { List names = new ArrayList<>(); testlist_star_expr.accept(new NameCollector(names)); return names; } public void foundExpression(ParserRuleContext ctx) { if (!isStartOfContainerRule(ctx)) { return ; } if (context.lastContainer().containsExpression(ctx)) return; if (ctx.getParent() instanceof TrailerContext) return; Expression parent = findParentInStack(ctx); Expression expression = parent; if (ctx.getParent().getChildCount()==1 && parent!=null) { //如果就是自己,则无需创建新的Expression }else { /* create expression and link it with parent*/ expression = new Expression(idGenerator.generateId()); expression.setLine(ctx.getStart().getLine()); expression.setText(ctx.getText()); context.lastContainer().addExpression(ctx,expression); expression.setParent(parent); } if (ctx instanceof Expr_stmtContext) { Expr_stmtContext exprAssign = (Expr_stmtContext)ctx; if (exprAssign.assign_part()!=null) { expression.setSet(true); expression.setIdentifier(exprAssign.testlist_star_expr().getText()); if (isValidIdentifier(expression.getIdentifier())) { makeSureVarExist(expression.getIdentifier(), ctx); } deduceVarTypeInCaseOfAssignment((Expr_stmtContext)ctx,expression); } } if (ctx instanceof Raise_stmtContext) { expression.setThrow (true); } if (ctx instanceof Return_stmtContext) { deduceReturnTypeInCaseOfReturn((Return_stmtContext)ctx,expression); } if (ctx instanceof ExprContext) { processExprContext((ExprContext)ctx, expression); } } private void deduceReturnTypeInCaseOfReturn(Return_stmtContext ctx, Expression expression) { FunctionEntity currentFunction = context.currentFunction(); if (currentFunction == null) return; expression.addDeducedTypeFunction(currentFunction); } private void makeSureVarExist(GenericName identifier, ParserRuleContext ctx) { if (null==context.foundEntityWithName(identifier)) { VarEntity var = context.foundVarDefinition(context.lastContainer(), identifier.getName(),ctx.getStart().getLine()); var.setLine(ctx.getStart().getLine()); } } private boolean isValidIdentifier(GenericName identifier) { Pattern p = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*"); Matcher m = p.matcher(identifier.getName()); return m.matches(); } private void processExprContext(ExprContext exprCtx, Expression expression) { //func_call, member_access, subscript member, and atom Expression lastExpression = null; if (exprCtx.atom()!=null) { //atom Expression atomExpr = new Expression(idGenerator.generateId()); atomExpr.setLine(exprCtx.atom().getStart().getLine()); atomExpr.setParent(expression); atomExpr.setText(exprCtx.atom().getText()); atomExpr.setIdentifier(exprCtx.atom().getText()); context.lastContainer().addExpression(exprCtx.atom(),atomExpr); processAtom(exprCtx.atom(),atomExpr); lastExpression = atomExpr; if (exprCtx.trailer()==null || exprCtx.trailer().size()==0) { //do nothing; it is just an id; }else { for (TrailerContext trailer:exprCtx.trailer()) { if (trailer.name()!=null) { Expression trailerExpr = new Expression(idGenerator.generateId()); trailerExpr.setLine(trailer.getStart().getLine()); trailerExpr.setText(trailer.getText()); context.lastContainer().addExpression(trailer,trailerExpr); trailerExpr.setParent(expression); //doted name = member access or method call trailerExpr.setDot(true);; trailerExpr.setIdentifier(trailer.name().getText()); if (trailer.arguments()!=null) { if (trailer.arguments().OPEN_PAREN()!=null) { foundCallStyleExpressionWithDot(trailerExpr,lastExpression.getIdentifier(), trailer); }else { //subscript list, do nothing } } lastExpression.setParent(trailerExpr); lastExpression = trailerExpr; }else { //direct call, or direct data access if (trailer.arguments()!=null) { if (trailer.arguments().OPEN_PAREN()!=null) { foundCallStyleExpressionWithoutDot(lastExpression, trailer.arguments()); }else { //subscript list, do nothing } } } } } }else { /** expr | expr op=POWER expr | op=(ADD | MINUS | NOT_OP) expr | expr op=(STAR | DIV | MOD | IDIV | AT) expr | expr op=(ADD | MINUS) expr | expr op=(LEFT_SHIFT | RIGHT_SHIFT) expr | expr op=AND_OP expr | expr op=XOR expr | expr op=OR_OP expr ;*/ } } private boolean isStartOfContainerRule(ParserRuleContext ctx) { if (this.exprStarted) return true; return ctx instanceof ExprContext || ctx instanceof Expr_stmtContext || ctx instanceof Del_stmtContext || ctx instanceof Return_stmtContext || ctx instanceof Raise_stmtContext || ctx instanceof Raise_stmtContext || ctx instanceof Yield_stmtContext || ctx instanceof Assert_stmtContext; } private void foundCallStyleExpressionWithDot(Expression theExpression, GenericName varName, ParserRuleContext ctx) { GenericName funcName = theExpression.getIdentifier(); Entity prefixEntity = context.foundEntityWithName(varName); if (prefixEntity instanceof VarEntity) { ((VarEntity) prefixEntity).addFunctionCall(funcName); } Entity typeEntity = context.foundEntityWithName(funcName); if (typeEntity instanceof TypeEntity && typeEntity.getId() > 0) { theExpression.setCreate(true); theExpression.setType(typeEntity.getType(), typeEntity, bindingResolver); theExpression.setRawType(typeEntity.getRawName()); return; } theExpression.setCall(true); } private void foundCallStyleExpressionWithoutDot(Expression theExpression, ParserRuleContext ctx) { GenericName funcName = theExpression.getIdentifier(); Entity typeEntity = context.foundEntityWithName(funcName); if (typeEntity instanceof TypeEntity && typeEntity.getId() > 0) { theExpression.getParent().setCreate(true); theExpression.setType(typeEntity.getType(), typeEntity, bindingResolver); theExpression.getParent().setRawType(typeEntity.getRawName()); return; } theExpression.setCall(true); } private void processAtom(AtomContext atom, Expression expression) { if (atom.name()!=null) { expression.setIdentifier(atom.getText()); return; } if (atom.STRING()!=null || atom.NONE()!=null || atom.number()!=null) { expression.setRawType(""); expression.setIdentifier(""); return; } if (atom.EXEC()!=null || atom.PRINT()!=null || atom.ELLIPSIS()!=null) { return; } // : OPEN_PAREN (yield_expr | testlist_comp)? CLOSE_PAREN // | OPEN_BRACKET testlist_comp? CLOSE_BRACKET // | OPEN_BRACE dictorsetmaker? CLOSE_BRACE // | REVERSE_QUOTE testlist COMMA? REVERSE_QUOTE return; } private Expression findParentInStack(RuleContext ctx) { if (ctx==null) return null; if (ctx.parent==null) return null; if (context.lastContainer()==null) { return null; } if (context.lastContainer().expressions().containsKey(ctx.parent)) return context.lastContainer().expressions().get(ctx.parent); return findParentInStack(ctx.parent); } public void startExpr() { this.exprStarted = true; } public void stopExpr() { this.exprStarted = false; } } class NameCollector extends PythonParserBaseVisitor{ private List names; NameCollector(List names){ this.names = names; } @Override public Void visitAtom(AtomContext ctx) { if (ctx.name()!=null) names.add(ctx.name().getText()); return super.visitAtom(ctx); } }