diff --git a/src/main/java/kalang/compiler/compile/AstBuilder.java b/src/main/java/kalang/compiler/compile/AstBuilder.java index ae5412e8..ea22c3c9 100644 --- a/src/main/java/kalang/compiler/compile/AstBuilder.java +++ b/src/main/java/kalang/compiler/compile/AstBuilder.java @@ -108,13 +108,9 @@ static class VarInfo{ protected ClassNode thisClazz; private ClassNode topClass; - - private boolean returned = false; - - private MethodNode method; - - private VarTable overrideTypes = new VarTable(); - + + private MethodContext methodCtx; + //TODO merge SemanticAnalyzer.assignedVars //private VarTable assignedNullables = new VarTable(); @@ -124,10 +120,7 @@ static class VarInfo{ NULLSTATE_MUST_NONNULL = 1, NULLSTATE_UNKNOWN = 2, NULLSTATE_NULLABLE = 3; - - private VarTable nullState = new VarTable(); - - private VarTable varTables = new VarTable(); + //private final HashMap methodBodys = new HashMap<>(); @Nonnull @@ -149,16 +142,16 @@ static class VarInfo{ private final CompilationUnit compilationUnit; private void newOverrideTypeStack(){ - overrideTypes = new VarTable(overrideTypes); + methodCtx.overrideTypes = new VarTable(methodCtx.overrideTypes); } private void popOverrideTypeStack(){ - overrideTypes = overrideTypes.getParent(); + methodCtx.overrideTypes = methodCtx.overrideTypes.getParent(); } private void removeOverrideType(ExprNode expr){ VarObject key = getOverrideTypeKey(expr); - if(key!=null) overrideTypes.remove(key, true); + if(key!=null) methodCtx.overrideTypes.remove(key, true); } @Nullable @@ -178,7 +171,7 @@ private VarObject getOverrideTypeKey(ExprNode expr){ private void changeTypeTemporarilyIfCould(ExprNode expr,Type type){ VarObject key = getOverrideTypeKey(expr); if(key!=null){ - overrideTypes.put(key, type); + methodCtx.overrideTypes.put(key, type); } } @@ -186,7 +179,7 @@ private void onNull(ExprNode expr,boolean onTrue,boolean isEQ){ boolean mustNull = (onTrue && isEQ) || (!onTrue && !isEQ); VarObject key = this.getOverrideTypeKey(expr); if(key!=null){ - nullState.put(key,mustNull ? NULLSTATE_MUST_NULL : NULLSTATE_MUST_NONNULL); + methodCtx.nullState.put(key,mustNull ? NULLSTATE_MUST_NULL : NULLSTATE_MUST_NONNULL); } } @@ -486,7 +479,7 @@ public Object visit(ParseTree tree) { ex.printStackTrace(System.err); return null; } - if(tree instanceof StatContext && returned){ + if(tree instanceof StatContext && methodCtx.returned){ diagnosisReporter.report(Diagnosis.Kind.ERROR ,"unreachable statement" , (StatContext) tree @@ -507,11 +500,11 @@ BlockStmt wrapBlock(Statement... statms){ } private void newFrame(){ - this.varTables = this.varTables.newStack(); + methodCtx.newFrame(); } private void popFrame(){ - this.varTables = this.varTables.popStack(); + methodCtx.popFrame(); } BlockStmt newBlock(){ @@ -541,7 +534,7 @@ protected void mapAst(@Nonnull AstNode node,@Nonnull Token token){ public ThrowStmt visitThrowStat(KalangParser.ThrowStatContext ctx) { ThrowStmt ts = new ThrowStmt(visitExpression(ctx.expression())); mapAst(ts, ctx); - this.returned = true; + this.methodCtx.returned = true; return ts; } @@ -761,29 +754,29 @@ public AstNode visitIfStat(IfStatContext ctx) { BlockStmt trueBody = null; BlockStmt falseBody = null; VarTable trueAssigned,falseAssigned; - this.nullState = trueAssigned = this.nullState.newStack(); + this.methodCtx.nullState = trueAssigned = this.methodCtx.nullState.newStack(); newOverrideTypeStack(); onIf(expr, true); if (ctx.trueStmt != null) { trueBody=requireBlock(ctx.trueStmt); } popOverrideTypeStack(); - this.nullState = this.nullState.popStack(); - boolean trueReturned = this.returned; - this.returned = false; - this.nullState = falseAssigned = this.nullState.newStack(); + this.methodCtx.nullState = this.methodCtx.nullState.popStack(); + boolean trueReturned = this.methodCtx.returned; + this.methodCtx.returned = false; + this.methodCtx.nullState = falseAssigned = this.methodCtx.nullState.newStack(); newOverrideTypeStack(); onIf(expr,false); if (ctx.falseStmt != null) { falseBody=requireBlock(ctx.falseStmt); } popOverrideTypeStack(); - this.nullState = this.nullState.popStack(); + this.methodCtx.nullState = this.methodCtx.nullState.popStack(); handleMultiBranchedAssign(trueAssigned.vars(),falseAssigned.vars()); - boolean falseReturned = this.returned; + boolean falseReturned = this.methodCtx.returned; if(trueReturned) onIf(expr,false); if(falseReturned) onIf(expr,true); - this.returned = falseReturned && trueReturned; + this.methodCtx.returned = falseReturned && trueReturned; IfStmt ifStmt = new IfStmt(expr,trueBody,falseBody); mapAst(ifStmt,ctx); return ifStmt; @@ -816,7 +809,7 @@ private void handleMultiBranchedAssign(Map... assignedTable){ } } for(Map.Entry e:ret.entrySet()){ - this.nullState.put(e.getKey(), e.getValue()); + this.methodCtx.nullState.put(e.getKey(), e.getValue()); } } @@ -849,8 +842,8 @@ public AstNode visitReturnStat(ReturnStatContext ctx) { if (ctx.expression() != null) { rs.expr = visitExpression(ctx.expression()); } - if(!semanticAnalyzer.validateReturnStmt(method, rs)) return null; - this.returned = true; + if(!semanticAnalyzer.validateReturnStmt(methodCtx.method, rs)) return null; + this.methodCtx.returned = true; return rs; } @@ -1205,7 +1198,7 @@ private void onAssign(ExprNode to,ExprNode expr){ }else{ throw Exceptions.unexceptedValue(type); } - nullState.put(key, ns); + methodCtx.nullState.put(key, ns); } } } @@ -1538,8 +1531,8 @@ private boolean isDefindedId(String id){ @Nullable private ParameterNode getNamedParameter(String name){ - if (method != null) { - for (ParameterNode p : method.getParameters()) { + if (methodCtx != null) { + for (ParameterNode p : methodCtx.method.getParameters()) { if (p.getName().equals(name)) { return p; } @@ -1550,7 +1543,7 @@ private ParameterNode getNamedParameter(String name){ @Nullable private LocalVarNode getNamedLocalVar(String name){ - return this.varTables.get(name); + return methodCtx!=null ? this.methodCtx.varTables.get(name) : null; } @Nullable @@ -1774,12 +1767,12 @@ public AstNode visitCastExpr(CastExprContext ctx) { public AstNode visitTryStat(TryStatContext ctx) { //TODO handle multi-branched assign BlockStmt tryExecStmt = requireBlock(ctx.exec); - boolean tryReturned = this.returned; + boolean tryReturned = this.methodCtx.returned; List tryCatchBlocks = new LinkedList<>(); if (ctx.catchTypes != null) { for (int i = 0; i < ctx.catchTypes.size(); i++) { this.newFrame(); - this.returned = false; + this.methodCtx.returned = false; String vName = ctx.catchVarNames.get(i).getText(); String vType = ctx.catchTypes.get(i).getText(); LocalVarNode vo = this.declareLocalVar(vName,requireClassType(vType, ctx.catchTypes.get(i).start),Modifier.FINAL,ctx); @@ -1787,7 +1780,7 @@ public AstNode visitTryStat(TryStatContext ctx) { BlockStmt catchExecStmt = requireBlock(ctx.catchExec.get(i)); CatchBlock catchStmt = new CatchBlock(vo,catchExecStmt); tryCatchBlocks.add(catchStmt); - this.returned = this.returned && tryReturned; + this.methodCtx.returned = this.methodCtx.returned && tryReturned; this.popFrame(); } } @@ -2043,7 +2036,7 @@ public Object visitLambdaExpr(KalangParser.LambdaExprContext ctx) { LocalVarNode tmpVar = this.declareTempLocalVar(type); LambdaExpr ms = new LambdaExpr(tmpVar,functionType); Map accessibleVars = new HashMap(); - VarTable vtb = this.varTables; + VarTable vtb = this.methodCtx.varTables; while(vtb!=null) { for(Map.Entry v:vtb.vars().entrySet()) { String name = v.getKey(); @@ -2054,7 +2047,7 @@ public Object visitLambdaExpr(KalangParser.LambdaExprContext ctx) { } vtb = vtb.getParent(); } - ParameterNode[] paramNodes = this.method.getParameters(); + ParameterNode[] paramNodes = this.methodCtx.method.getParameters(); for(ParameterNode p:paramNodes) { String name = p.getName(); if (!accessibleVars.containsKey(name)) { @@ -2189,7 +2182,7 @@ private LocalVarNode declareLocalVar(String name,Type type,int modifier,ParserRu return null; } if(name!=null){ - this.varTables.put(name,localVarNode); + this.methodCtx.varTables.put(name,localVarNode); } return localVarNode; } @@ -2346,11 +2339,11 @@ public Object visitArrayExpr(KalangParser.ArrayExprContext ctx) { } private Type getVarObjectType(VarObject p) { - Type type = this.overrideTypes.get(p); + Type type = this.methodCtx.overrideTypes.get(p); if(type==null) type = p.getType(); //TODO handle other object type if(type instanceof ClassType){ - Integer ns = nullState.get(p); + Integer ns = methodCtx.nullState.get(p); NullableKind nullable; if(ns==null){ nullable = ((ObjectType) type).getNullable(); @@ -2467,7 +2460,7 @@ public void importStaticMember(ClassNode classNode,@Nullable String name) { } private boolean isInConstructor(){ - return "".equals(this.method.getName()); + return "".equals(this.methodCtx.method.getName()); } @Nullable @@ -2608,7 +2601,7 @@ private ClassNode createFunctionClassNode(ClassType type,LambdaExpr lambdaExpr,K } String lambdaName = this.thisClazz.name + "$" + ++anonymousClassCounter; ClassNode oldClass = thisClazz; - MethodNode oldMethod = method; + MethodContext oldMethodCtx = this.methodCtx; ClassNode classNode = thisClazz = new ClassNode(lambdaName,Modifier.PUBLIC); classNode.setSuperType(Types.getRootType()); Map accessibleVars = lambdaExpr.getAccessibleVarObjects(); @@ -2656,36 +2649,36 @@ private ClassNode createFunctionClassNode(ClassType type,LambdaExpr lambdaExpr,K } if (returnType.equals(Types.getVoidClassType())) { bs.statements.add(new ReturnStmt(new ConstExpr(null))); - returned = true; + methodCtx.returned = true; } methodNode.getBody().statements.add(bs); checkMethod(); //TODO check return thisClazz = oldClass; - method = oldMethod; + methodCtx = oldMethodCtx; return classNode; } private void enterMethod(MethodNode method) { - this.method = method; - returned = false; + methodCtx = new MethodContext(this.thisClazz,method); + methodCtx.returned = false; } private void checkMethod() { - MethodNode m = this.method; + MethodNode m = this.methodCtx.method; boolean needReturn = ( m.getType() != null && !m.getType().equals(Types.VOID_TYPE) ); BlockStmt mbody = m.getBody(); - if (mbody != null && needReturn && !returned) { + if (mbody != null && needReturn && !methodCtx.returned) { ConstExpr defaultVal = m.getDefaultReturnValue(); if (defaultVal!=null) { mbody.statements.add(new ReturnStmt(defaultVal)); } else { this.diagnosisReporter.report( Diagnosis.Kind.ERROR - , "Missing return statement in method:" + MethodUtil.toString(method) + , "Missing return statement in method:" + MethodUtil.toString(methodCtx.method) , m.offset ); } diff --git a/src/main/java/kalang/compiler/compile/MethodContext.java b/src/main/java/kalang/compiler/compile/MethodContext.java new file mode 100644 index 00000000..ac98a95f --- /dev/null +++ b/src/main/java/kalang/compiler/compile/MethodContext.java @@ -0,0 +1,37 @@ +package kalang.compiler.compile; + +import kalang.compiler.ast.ClassNode; +import kalang.compiler.ast.LocalVarNode; +import kalang.compiler.ast.MethodNode; +import kalang.compiler.ast.VarObject; +import kalang.compiler.core.Type; +import kalang.compiler.core.VarTable; + +public class MethodContext { + + public final ClassNode classNode; + + public final MethodNode method; + + public boolean returned = false; + + public VarTable varTables = new VarTable(); + + public VarTable overrideTypes = new VarTable(); + + public VarTable nullState = new VarTable(); + + public MethodContext(ClassNode classNode, MethodNode methodNode) { + this.classNode = classNode; + this.method = methodNode; + } + + public void newFrame(){ + this.varTables = varTables.newStack(); + } + + public void popFrame(){ + this.varTables = this.varTables.popStack(); + } + +}