107 lines
3.3 KiB
Java
Executable File
107 lines
3.3 KiB
Java
Executable File
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.
|
|
* <br/> 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<Integer> 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.
|
|
* <br/> A check is only necessary if the index expression cannot be evaluated in
|
|
* compile time or the size of the array is unknown.
|
|
* <br/> 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<Integer> 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<Integer> 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.
|
|
* <br/> Currently doesn't check indexes.
|
|
*/
|
|
public boolean isNotNull(Expr expr) {
|
|
if (expr instanceof ThisRef) {
|
|
return false;
|
|
} else {
|
|
VariableSymbol symbol = symbolOf(expr);
|
|
MaybeC<Boolean> isNull = cfgCg.nullState.get(symbol);
|
|
return isNull != null && isNull.isConstant() && !isNull.getValue();
|
|
}
|
|
}
|
|
}
|