import java.util.*; import minijava.analysis.*; import minijava.node.*; import minijava.symbol.*; public class TypeCheck extends DepthFirstAdapter { private Table symtable; private minijava.symbol.Class currClass; private minijava.symbol.Method currMethod; private Hashtable initcheck; private boolean [] ok; public TypeCheck(Table table, boolean[] ok) { this.symtable = table; this.ok = ok; currClass = null; currMethod = null; } private boolean isIntegerType(PType type) { return (type instanceof AIntegerType); } private boolean isBooleanType(PType type) { return (type instanceof ABooleanType); } private PType findVarType(String id) { Variable v = symtable.getVar(currMethod, currClass, id); if (v == null) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Variable " + id + "inside function " + currMethod.getId() + " of class " + currClass.getId() + " has not been declared"); ok[0] = false; System.exit( -1); } return v.getType(); } private String createSignature(Method mymeth) { StringBuffer signature = new StringBuffer(); ; signature.append("("); for (int i = 0; i < mymeth.ParamSize(); i++) { signature.append(extractType(mymeth.getParamAt(i).getType())); } signature.append(")"); signature.append(extractType(mymeth.getType())); return signature.toString(); } private String extractType(PType type) { if (type instanceof ABooleanType) { return "Z"; } else if (type instanceof AIntArrayType) { return "[I"; } else if (type instanceof AIntegerType) { return "I"; } else if (type instanceof AIdentifierType) { return "L" + ( (AIdentifierType) type).getIdLiteral().toString().trim() + ";"; //reexamine } return null; } private boolean assignpossible(PType ltyp, PType rtyp) { if (ltyp.getClass() != rtyp.getClass()) { return false; } if (ltyp instanceof AIdentifierType) { // subclass variable shoud be assignable to superclass variable minijava.symbol.Class supercl, subcl; supercl = symtable.getClass( ( (AIdentifierType) ltyp).getIdLiteral(). toString().trim()); subcl = symtable.getClass( ( (AIdentifierType) rtyp).getIdLiteral(). toString().trim()); return subclass(subcl, supercl); } return true; // of same type } public boolean subclass(minijava.symbol.Class supercl, minijava.symbol.Class subcl) { if (subcl == null) { return false; } if (subcl.getId().equals(supercl.getId())) { return true; } if (subcl.parent() == null) { return false; // root reached } return subclass(supercl, symtable.getClass(subcl.parent())); } public void inAMainClass(AMainClass node) { String ClassName = node.getClname().toString().trim(); currClass = symtable.getClass(ClassName); currMethod = currClass.getMethod("main"); initcheck = new java.util.Hashtable(); } public void outAMainClass(AMainClass node) { currClass = null; currMethod = null; } public void inASimpleClassDecl(ASimpleClassDecl node) { String ClassName = node.getIdentifier().toString().trim(); currClass = symtable.getClass(ClassName); } public void outASimpleClassDecl(ASimpleClassDecl node) { currClass = null; } public void inAExtendsClassDecl(AExtendsClassDecl node) { String ClassName = node.getNewcl().toString().trim(); String ParentClass = node.getOldcl().toString().trim(); currClass = symtable.getClass(ClassName); } public void outAExtendsClassDecl(AExtendsClassDecl node) { currClass = null; } public void inAMethodDecl(AMethodDecl node) { PType t = node.getType(); String id = node.getIdentifier().toString(); currMethod = currClass.getMethod(id); initcheck = new Hashtable(); int line = ( (AIdentifier) node.getIdentifier()).getIdLiteral().getLine(); // check if method override happens and ensures it's correct if there is one. String parentstr = currClass.parent(); if (parentstr != null) { minijava.symbol.Class parentclass = symtable.getClass(parentstr); if (parentclass != null) { Method m = symtable.getMethod(id, parentstr); if (m != null) { String currsign = createSignature(currMethod); String prevsign = createSignature(m); if (!currsign.equals(prevsign)) { System.out.println(currClass.getId() + "." + currMethod.getId() + " (Line " + line + "):" + "Attempt to override function " + id + " incorrect. Signatures do not match."); ok[0] = false; } } } } } public void outAMethodDecl(AMethodDecl node) { PType t = (PType) getOut(node.getExp()); PType returntype = currMethod.getType(); int line = ( (AIdentifier) node.getIdentifier()).getIdLiteral().getLine(); // if (!assignpossible(returntype, t)) { if (!assignpossible(t, returntype)) { System.out.println(currClass.getId() + "." + currMethod.getId() + " (Line " + line + "):" + "Return type does not match with function declaration"); ok[0] = false; } currMethod = null; } public void outAVarDecl(AVarDecl node) { String id = node.getIdentifier().toString(); // PType type = findVarType(id); if (currMethod != null) { //local function variables initcheck.put(id, new Integer(0)); } } public void outAIntegerLiteralExp(AIntegerLiteralExp node) { setOut(node, new AIntegerType()); } public void outATrueExp(ATrueExp node) { setOut(node, new ABooleanType()); } public void outAFalseExp(AFalseExp node) { setOut(node, new ABooleanType()); } public void outAIdentifierExp(AIdentifierExp node) { String id = node.getIdLiteral().toString(); PType t = findVarType(id); int line = node.getIdLiteral().getLine(); setOut(node, t); if (currMethod.containsVar(id)) { //local vars only if ( ( (Integer) initcheck.get(id)).intValue() == 0) { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Variable " + id + " might not have been" + " initialized"); ok[0] = false; } } } public void outAArrayLookupExp(AArrayLookupExp node) { PType id = (PType) getOut(node.getExp()); PType index = (PType) getOut(node.getIndex()); if (! (id instanceof AIntArrayType)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Variable " + node.getExp().toString() + " must be of type IntArray"); ok[0] = false; } if (!isIntegerType(index)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Index for IntArray variable " + node.getExp().toString() + " must be of type Integer"); ok[0] = false; } setOut(node, new AIntegerType()); } public void outAArrayLengthExp(AArrayLengthExp node) { PType id = (PType) getOut(node.getExp()); if (! (id instanceof AIntArrayType)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Variable " + node.getExp().toString() + " must be of type IntArray"); ok[0] = false; } setOut(node, new AIntegerType()); } public void outANewArrayExp(ANewArrayExp node) { PType index = (PType) getOut(node.getExp()); if (!isIntegerType(index)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Index for New IntArray declaration " + " must be of type Integer"); ok[0] = false; } setOut(node, new AIntArrayType()); } public void outAPlusExp(APlusExp node) { PType leftType = (PType) getOut(node.getLeft()); PType rightType = (PType) getOut(node.getRight()); if (isIntegerType(leftType) && isIntegerType(rightType)) { setOut(node, new AIntegerType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Both sides of addition must be Integer"); ok[0] = false; } } public void outAMinusExp(AMinusExp node) { PType leftType = (PType) getOut(node.getLeft()); PType rightType = (PType) getOut(node.getRight()); if (isIntegerType(leftType) && isIntegerType(rightType)) { setOut(node, new AIntegerType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Both sides of substraction must be Integer"); ok[0] = false; } } public void outATimesExp(ATimesExp node) { PType leftType = (PType) getOut(node.getLeft()); PType rightType = (PType) getOut(node.getRight()); if (isIntegerType(leftType) && isIntegerType(rightType)) { setOut(node, new AIntegerType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Both sides of multiplication must be Integer"); ok[0] = false; } } public void outALessThanExp(ALessThanExp node) { PType leftType = (PType) getOut(node.getLeft()); PType rightType = (PType) getOut(node.getRight()); if (isIntegerType(leftType) && isIntegerType(rightType)) { setOut(node, new ABooleanType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Both sides of comparison < must be Integer"); ok[0] = false; } } public void outAAndExp(AAndExp node) { PType leftType = (PType) getOut(node.getLeft()); PType rightType = (PType) getOut(node.getRight()); if (isBooleanType(leftType) && isBooleanType(rightType)) { setOut(node, new ABooleanType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Both sides of \"&&\" must be Boolean"); ok[0] = false; } } public void outANotExp(ANotExp node) { PType t = (PType) getOut(node.getExp()); if (isBooleanType(t)) { setOut(node, new ABooleanType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "\"Not\" operand should be used with a " + "Boolean type expression"); ok[0] = false; } } public void outAThisExp(AThisExp node) { setOut(node, new AIdentifierType(new TIdLiteral(currClass.getId()))); } public void outANewObjectExp(ANewObjectExp node) { String object = node.getIdentifier().toString(); int line = ( (AIdentifier) node.getIdentifier()).getIdLiteral().getLine(); if (symtable.containsClass(object.trim())) { setOut(node, new AIdentifierType(new TIdLiteral(object))); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Class " + object + ", declared in " + "New Object expression, does not exist"); ok[0] = false; System.exit( -1); } } public void outACallExp(ACallExp node) { PType object = (PType) getOut(node.getExp()); String methodname = node.getIdentifier().toString(); String classname = ( (AIdentifierType) object).getIdLiteral().toString(). trim(); int line = ( (AIdentifier) node.getIdentifier()).getIdLiteral().getLine(); minijava.symbol.Method invocmethod = symtable.getMethod(methodname, classname); if (invocmethod != null) { for (int i = 0; i < node.getArgs().size(); i++) { PType reference = null, current; current = (PType) getOut( (Node) node.getArgs().get(i)); if (invocmethod.getParamAt(i) != null) { reference = (PType) invocmethod.getParamAt(i).getType(); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Number of parameters inside method " + classname + "." + methodname + " lower than what is declared here."); ok[0] = false; } if (!assignpossible(current, reference)) { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Type mismatch in arguments passed to " + classname + "." + methodname); ok[0] = false; } } if (invocmethod.ParamSize() > node.getArgs().size()) { System.out.println(currClass.getId() + "." + currMethod.getId() + ":" + "(Line " + line + "): " + "Number of parameters inside method " + classname + "." + methodname + " higher than what is declared here."); ok[0] = false; } setOut(node, invocmethod.getType()); } else { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Class \"" + classname + "\" does not contain " + "method \"" + methodname + "\""); ok[0] = false; System.exit(-1); } } public void outAPrintStatement(APrintStatement node) { PType output = (PType) getOut(node.getExp()); if (!isIntegerType(output)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "Arguments of \"System.out.println\" must be" + " of type Integer"); ok[0] = false; } } public void outAIfStatement(AIfStatement node) { PType condition = (PType) getOut(node.getExp()); if (!isBooleanType(condition)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + " \"If\" statement condition must be of type" + " Boolean"); ok[0] = false; } } public void caseAIfStatement(AIfStatement node) { inAIfStatement(node); if (node.getExp() != null) { node.getExp().apply(this); } Hashtable ifcheck = new Hashtable(); Hashtable elsecheck = new Hashtable(); ifcheck.putAll(initcheck); elsecheck.putAll(initcheck); initcheck = ifcheck; if (node.getIfpart() != null) { node.getIfpart().apply(this); } setIn(node.getIfpart(), initcheck); initcheck = elsecheck; if (node.getElsepart() != null) { node.getElsepart().apply(this); } setIn(node.getElsepart(), initcheck); Hashtable doneifcheck = (Hashtable) getIn(node.getIfpart()); Hashtable doneelsecheck = (Hashtable) getIn(node.getElsepart()); initcheck = executeAnd(doneifcheck, doneelsecheck); outAIfStatement(node); } public Hashtable executeAnd(Hashtable ifpart, Hashtable elsepart) { Hashtable output = new Hashtable(); int paramsnumber = currMethod.ParamSize(); int varsnumber = currMethod.VarSize(); for (int i = 0; i < varsnumber; i++) { Variable v = currMethod.getVarAt(i + paramsnumber + 1); String varname = v.getId(); int ifint = ( (Integer) ifpart.get(varname)).intValue(); int elseint = ( (Integer) elsepart.get(varname)).intValue(); output.put(varname, new Integer(ifint & elseint)); } return output; } public void caseAWhileStatement(AWhileStatement node) { inAWhileStatement(node); Hashtable whilecheck = new Hashtable(); Hashtable originitcheck = new Hashtable(); whilecheck.putAll(initcheck); originitcheck.putAll(initcheck); initcheck = whilecheck; if (node.getExp() != null) { node.getExp().apply(this); } if (node.getStatement() != null) { node.getStatement().apply(this); } initcheck = originitcheck; outAWhileStatement(node); } public void outAWhileStatement(AWhileStatement node) { PType condition = (PType) getOut(node.getExp()); if (!isBooleanType(condition)) { System.out.println(currClass.getId() + "." + currMethod.getId() + ": " + "\"While\" statement condition must be of type" + " Boolean"); ok[0] = false; } } public void outAAssignStatement(AAssignStatement node) { String id = node.getIdentifier().toString(); PType lhs = findVarType(id); PType rhs = (PType) getOut(node.getExp()); int line = ( (AIdentifier) node.getIdentifier()).getIdLiteral().getLine(); if (!assignpossible(lhs, rhs)) { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Types do not match in assignment"); ok[0] = false; } if (currMethod.containsVar(id)) { //local vars only initcheck.put(id, new Integer(1)); } } public void outAArrayAssignStatement(AArrayAssignStatement node) { String id = node.getIdentifier().toString(); PType lhs = findVarType(id); PType index = (PType) getOut(node.getIndex()); PType rhs = (PType) getOut(node.getRhs()); int line = ( (AIdentifier) node.getIdentifier()).getIdLiteral().getLine(); if (! (lhs instanceof AIntArrayType)) { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Left hand side variable must be an IntArray"); ok[0] = false; } if (!isIntegerType(index)) { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Index inside IntArray variable must be of Integer type"); ok[0] = false; } if (!isIntegerType(rhs)) { System.out.println(currClass.getId() + "." + currMethod.getId() + "(Line " + line + "): " + "Rhs of an IntArray assignment must be of Integer type"); ok[0] = false; } } }