package cd.backend.codegen; import cd.Config; import cd.backend.ExitCode; import cd.ir.Ast; import cd.ir.Ast.*; import cd.ir.CompileTimeEvaluator; import cd.ir.Symbol.VariableSymbol; import cd.transform.analysis.MaybeC; import java.util.Optional; import static cd.backend.codegen.AssemblyEmitter.constant; import static cd.backend.codegen.RegisterManager.STACK_REG; /** * Class with simple static checks mimicking the runtime system with the functions to check * possible errors at runtime. The purpose is to skip unnecessary calls to those functions */ public class CompileTimeChecks { protected final CfgCodeGenerator cfgCg; protected final AstCodeGeneratorRef cgRef; protected CompileTimeEvaluator cte; public CompileTimeChecks(AstCodeGeneratorRef cg, CfgCodeGenerator cfgCodeGenerator) { this.cfgCg = cfgCodeGenerator; this.cgRef = cg; cte = new CompileTimeEvaluator(); } /** * Convenience method that extracts the VariableSymbol from a Var or Field node. *
If the node is not one of those, the node must be a Index */ private VariableSymbol symbolOf(Expr expr) { if (expr instanceof Var) return ((Var) expr).sym; if (expr instanceof Field) return ((Field) expr).sym; // assert expr instanceof Index // only for local checks return null; } /** Returns whether a NewArray expression needs to check dynamically for the * validity of the index (>=0). In case that it is known but invalid, a call * to exit is done with the appropriate error code. */ public boolean checkArraySize(Ast.NewArray expr) { Optional arraySize = cte.calc(expr.arg()); if (!arraySize.isPresent()) { return true; } int arraySizeValue = arraySize.get(); if (arraySizeValue < 0) { cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_SIZE.value), 0, STACK_REG); cgRef.emit.emit("call", Config.EXIT); return false; } return false; } /** Returns whether a run-time check for the bounds of an array index is necessary. *
A check is only necessary if the index expression cannot be evaluated in * compile time or the size of the array is unknown. *
If the value is known and it is invalid, an unconditional call to exit is * performed with the appropriate exit code */ public boolean checkArrayBound(Index expr) { Optional index = cte.calc(expr.right()); if (!index.isPresent()) { return true; } VariableSymbol symbol = symbolOf(expr.left()); if (!cfgCg.arraySizeState.containsKey(symbol)) { return true; } int indexValue = index.get(); MaybeC bound = cfgCg.arraySizeState.get(symbol); if (bound.isNotConstant()) { return true; } int boundValue = bound.getValue(); if (indexValue < 0 || indexValue > (boundValue - 1)) { cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_BOUNDS.value), 0, STACK_REG); cgRef.emit.emit("call", Config.EXIT); } return false; } /** * Returns true only when it is impossible for the expression (Field or Var) to be null. *
Currently doesn't check indexes. */ public boolean isNotNull(Expr expr) { if (expr instanceof ThisRef) { return false; } else { VariableSymbol symbol = symbolOf(expr); MaybeC isNull = cfgCg.nullState.get(symbol); return isNull != null && isNull.isConstant() && !isNull.getValue(); } } }