Homework B (benchmarks)
This commit is contained in:
parent
72cc3206c4
commit
76fbabdf53
141 changed files with 7540 additions and 2032 deletions
|
@ -61,6 +61,9 @@ abstract public class AbstractTestAgainstFrozenReference {
|
|||
{
|
||||
if (passedSemanticAnalysis) {
|
||||
testCodeGenerator(astRoots);
|
||||
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +111,7 @@ abstract public class AbstractTestAgainstFrozenReference {
|
|||
|
||||
private static String referenceVersion() {
|
||||
{
|
||||
return "CD_HW_CODEGEN_FULL_SOL";
|
||||
return "BENCH";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +197,7 @@ abstract public class AbstractTestAgainstFrozenReference {
|
|||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run the code generator, assemble the resulting .s file, and (if the output
|
||||
* is well-defined) compare against the expected output.
|
||||
|
|
345
test/cd/BenchmarksRunner.java
Normal file
345
test/cd/BenchmarksRunner.java
Normal file
|
@ -0,0 +1,345 @@
|
|||
package cd;
|
||||
|
||||
import static java.nio.file.Files.lines;
|
||||
import static java.nio.file.Files.write;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import cd.backend.codegen.CfgCodeGenerator;
|
||||
import cd.backend.interpreter.DynamicError;
|
||||
import cd.backend.interpreter.Interpreter;
|
||||
import cd.backend.interpreter.StaticError;
|
||||
import cd.frontend.parser.ParseFailure;
|
||||
import cd.frontend.semantic.SemanticFailure;
|
||||
import cd.ir.Ast.ClassDecl;
|
||||
import cd.util.FileUtil;
|
||||
import cd.util.Pair;
|
||||
|
||||
public class BenchmarksRunner {
|
||||
|
||||
// MAIN_OVERHEAD is an estimate of instructions executed by an empty 'main' in Javali.
|
||||
private static final long MAIN_OVERHEAD = 150000;
|
||||
|
||||
public static final File BENCH_DIR = new File("benchmarks");
|
||||
//public static final File BENCH_DIR = new File("javali_tests");
|
||||
|
||||
public static void main(String[] args) {
|
||||
BenchmarksRunner runner = new BenchmarksRunner();
|
||||
runner.runBenchmarks();
|
||||
}
|
||||
|
||||
private final Collection<File> benchmarks;
|
||||
private File sFile;
|
||||
private File binFile;
|
||||
private File inFile;
|
||||
private Main main;
|
||||
private File optRefFile;
|
||||
private File execRefFile;
|
||||
private File sRefFile;
|
||||
private File binRefFile;
|
||||
|
||||
public BenchmarksRunner() {
|
||||
benchmarks = collectBenchs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all Javali-files in a directory (recursively)
|
||||
*/
|
||||
private static Collection<File> collectBenchs() {
|
||||
List<File> result = new ArrayList<>();
|
||||
for (File file : FileUtil.findJavaliFiles(BENCH_DIR))
|
||||
result.add(file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all benchmarks found in <code>BENCH_DIR</code> and compare with the reference compiler.
|
||||
*/
|
||||
public void runBenchmarks() {
|
||||
System.out.println("Benchmark\tAST reduction\tValgrind reduction");
|
||||
for (File file : benchmarks) {
|
||||
this.sFile = new File(file.getPath() + Config.ASMEXT);
|
||||
this.binFile = new File(file.getPath() + Config.BINARYEXT);
|
||||
this.inFile = new File(file.getPath() + ".in");
|
||||
this.execRefFile = new File(file.getPath() + ".exec.ref");
|
||||
this.optRefFile = new File(file.getPath() + ".opt.ref");
|
||||
this.sRefFile = new File(file.getPath() + ".ref" + Config.ASMEXT);
|
||||
this.binRefFile = new File(file.getPath() + ".ref" + Config.BINARYEXT);
|
||||
|
||||
System.out.print(file.getName() + "\t");
|
||||
try {
|
||||
Pair<Double> improvement = runOneBench(file);
|
||||
System.out.printf("%.1f%%\t%.1f%%\n",
|
||||
improvement.a * 100, improvement.b * 100);
|
||||
} catch (BenchmarkError e) {
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Throwable t) {
|
||||
System.out.println("ERROR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a Javali-program on the reference compiler
|
||||
*/
|
||||
private void runReference(File file) throws IOException, InterruptedException {
|
||||
String slash = File.separator;
|
||||
String colon = File.pathSeparator;
|
||||
String javaExe = System.getProperty("java.home") + slash + "bin" + slash + Config.JAVA_EXE;
|
||||
|
||||
String benchV = "BENCH2";
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(javaExe,
|
||||
"-Dcd.meta_hidden.Version="+benchV, "-cp",
|
||||
"lib/frozenReferenceObf.jar" + colon + " lib/junit-4.12.jar" + colon
|
||||
+ "lib/antlr-4.7.1-complete.jar",
|
||||
"cd.FrozenReferenceMain", file.getAbsolutePath());
|
||||
|
||||
pb.redirectOutput(Redirect.INHERIT);
|
||||
pb.redirectError(Redirect.INHERIT);
|
||||
|
||||
Process proc = pb.start();
|
||||
proc.waitFor();
|
||||
try (InputStream err = proc.getErrorStream()) {
|
||||
if (err.available() > 0) {
|
||||
byte b[] = new byte[err.available()];
|
||||
err.read(b, 0, b.length);
|
||||
System.err.println(new String(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run and compare one javali-benchmark. Returns two "improvement" numbers that
|
||||
* represent the reduction in instructions executed. (once on the interpreter, once
|
||||
* as compiled code using valgrind)
|
||||
*/
|
||||
private Pair<Double> runOneBench(File file) throws BenchmarkError {
|
||||
// Delete intermediate files from previous runs:
|
||||
if (sFile.exists())
|
||||
sFile.delete();
|
||||
if (binFile.exists())
|
||||
binFile.delete();
|
||||
|
||||
// Read test-input file
|
||||
String input = "";
|
||||
try {
|
||||
if (inFile.exists())
|
||||
input = FileUtil.read(inFile);
|
||||
} catch (IOException e1) {
|
||||
throw new BenchmarkError("Error while reading .in file");
|
||||
}
|
||||
|
||||
// Run test on reference implementation
|
||||
String execRef, optRef;
|
||||
try {
|
||||
runReference(file);
|
||||
execRef = FileUtil.read(execRefFile);
|
||||
optRef = FileUtil.read(optRefFile);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("Error while running on reference");
|
||||
}
|
||||
|
||||
// Call frontend of compiler-under-test (CUT)
|
||||
List<ClassDecl> astRoots;
|
||||
PrintStream oldStdOut = System.out;
|
||||
try {
|
||||
System.setOut(new PrintStream(new File("stdout-log.txt")));
|
||||
|
||||
this.main = new Main();
|
||||
this.main.debug = new StringWriter();
|
||||
|
||||
try {
|
||||
astRoots = main.parse(new FileReader(file));
|
||||
} catch (ParseFailure | IOException pf) {
|
||||
System.err.println(pf);
|
||||
throw new BenchmarkError("ParseFailure");
|
||||
}
|
||||
|
||||
try {
|
||||
main.semanticCheck(astRoots);
|
||||
} catch (SemanticFailure sf) {
|
||||
System.err.println(sf);
|
||||
throw new BenchmarkError("SemanticFailure");
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("Failed to redirect stdout");
|
||||
} finally {
|
||||
System.setOut(oldStdOut);
|
||||
}
|
||||
|
||||
// Run IR of compiled test with interpreter to count
|
||||
// instructions
|
||||
final StringWriter outputWriter = new StringWriter();
|
||||
final Reader inputReader = new StringReader(input);
|
||||
final Interpreter interp = new Interpreter(astRoots, inputReader, outputWriter);
|
||||
|
||||
try {
|
||||
interp.execute();
|
||||
} catch (StaticError | DynamicError err) {
|
||||
System.err.println(err);
|
||||
throw new BenchmarkError("Error while running the interpreter");
|
||||
}
|
||||
|
||||
// If output is the same, calculate the reduction in
|
||||
// instructions by the CUT compared to the reference
|
||||
// compiler
|
||||
String execOut = outputWriter.toString();
|
||||
if (!execOut.equals(execRef))
|
||||
throw new BenchmarkError("Output is incorrect");
|
||||
|
||||
String optOut = interp.operationSummary();
|
||||
double refCount = getTotalOps(optRef);
|
||||
double outCount = getTotalOps(optOut);
|
||||
|
||||
double interpReduction = 0;
|
||||
if (refCount != 0 || outCount != 0)
|
||||
interpReduction = 1 - outCount / refCount;
|
||||
|
||||
double valgrindReduction = 0;
|
||||
// on BENCH2, we also compare the generated assembly using valgrind
|
||||
valgrindReduction = compareWithValgrind(astRoots, input);
|
||||
|
||||
return new Pair<>(interpReduction, valgrindReduction);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate assembly, assemble, and run the executables of the reference
|
||||
* and the CUT on valgrind to get a deterministic instruction count. Return
|
||||
* the reduction of instructions compared to the reference on BENCH2.
|
||||
*/
|
||||
private double compareWithValgrind(List<ClassDecl> astRoots, String input) throws BenchmarkError {
|
||||
// generate ASM
|
||||
try (FileWriter fout = new FileWriter(sFile)) {
|
||||
CfgCodeGenerator cg = new CfgCodeGenerator(main, fout);
|
||||
cg.go(astRoots);
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("IOException when generating ASM");
|
||||
}
|
||||
|
||||
// assemble the generated and the reference's assembly file
|
||||
try {
|
||||
FileUtil.runCommand(
|
||||
Config.ASM_DIR, Config.ASM,
|
||||
new String[] { binFile.getAbsolutePath(), sFile.getAbsolutePath() },
|
||||
null, false);
|
||||
if (!binFile.exists())
|
||||
throw new BenchmarkError("Error while assembling ASM");
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("Error while assembling ASM");
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtil.runCommand(
|
||||
Config.ASM_DIR, Config.ASM,
|
||||
new String[] { binRefFile.getAbsolutePath(), sRefFile.getAbsolutePath() },
|
||||
null, false);
|
||||
if (!binRefFile.exists())
|
||||
throw new BenchmarkError("Error while assembling ASM from reference");
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("Error while assembling ASM from reference");
|
||||
}
|
||||
|
||||
// run both binaries on valgrind
|
||||
String execOut;
|
||||
try {
|
||||
String[] cmd = {"valgrind", "--tool=callgrind", binFile.getAbsolutePath()};
|
||||
execOut = FileUtil.runCommand(new File("."),
|
||||
cmd, new String[] {},
|
||||
input, true);
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("Error while running binary");
|
||||
}
|
||||
|
||||
String execRefOut;
|
||||
try {
|
||||
String[] cmd = {"valgrind", "--tool=callgrind", binRefFile.getAbsolutePath()};
|
||||
execRefOut = FileUtil.runCommand(new File("."),
|
||||
cmd, new String[] {},
|
||||
input, true);
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
throw new BenchmarkError("Error while running binary from reference");
|
||||
}
|
||||
|
||||
// parse output of valgrind and return reduction in instructions
|
||||
double instrCount = parseInstrCount(execOut) - MAIN_OVERHEAD;
|
||||
double instrRefCount = parseInstrCount(execRefOut) - MAIN_OVERHEAD;
|
||||
|
||||
double valgrindReduction = 0;
|
||||
if (instrCount != 0 || instrRefCount != 0)
|
||||
valgrindReduction = 1 - instrCount / instrRefCount;
|
||||
|
||||
return valgrindReduction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse output of valgrind and return total instruction count
|
||||
*/
|
||||
private long parseInstrCount(String execOut_) throws BenchmarkError {
|
||||
String execOut = execOut_.replace("\r\n", "\n");
|
||||
for (String line : execOut.split("\n")) {
|
||||
if (line.contains(" I refs:")) {
|
||||
String[] args = line.split(": ");
|
||||
String number = args[1].trim();
|
||||
number = number.replace(",", "");
|
||||
return Long.valueOf(number);
|
||||
}
|
||||
}
|
||||
|
||||
throw new BenchmarkError("Error while parsing valgrind output");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse operation summary of interpreter and get the total count of instructions executed
|
||||
* (Binary+Unary ops)
|
||||
*/
|
||||
private int getTotalOps(String optCount_) {
|
||||
String optCount = optCount_.replace("\r\n", "\n");
|
||||
int sum = 0;
|
||||
for (String line : optCount.split("\n")) {
|
||||
if (!line.equals("")) {
|
||||
String[] args = line.split(": ");
|
||||
sum += Integer.valueOf(args[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
class BenchmarkError extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public BenchmarkError(String arg) {
|
||||
super(arg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,8 @@ public class TestSamplePrograms extends AbstractTestAgainstFrozenReference {
|
|||
this.main = new Main();
|
||||
this.main.debug = new StringWriter();
|
||||
|
||||
this.main.cfgdumpbase = file;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
32
test/cd/backend/interpreter/DynamicError.java
Normal file
32
test/cd/backend/interpreter/DynamicError.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package cd.backend.interpreter;
|
||||
|
||||
import cd.backend.ExitCode;
|
||||
import cd.util.FileUtil;
|
||||
|
||||
/** Thrown in cases that are not ruled out by static analysis: */
|
||||
@SuppressWarnings("serial")
|
||||
public class DynamicError extends RuntimeException {
|
||||
private ExitCode code;
|
||||
|
||||
public DynamicError(String message, ExitCode code) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public DynamicError(Throwable thr, ExitCode code) {
|
||||
super(thr);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public ExitCode getExitCode() {
|
||||
return this.code;
|
||||
}
|
||||
/**
|
||||
* Returns a string exactly like the one that
|
||||
* {@link FileUtil#runCommand(java.io.File, String[], String[], String, boolean)}
|
||||
* returns when a command results in an error. */
|
||||
public String format() {
|
||||
return "Error: " + code.value + "\n";
|
||||
}
|
||||
|
||||
}
|
585
test/cd/backend/interpreter/Interpreter.java
Normal file
585
test/cd/backend/interpreter/Interpreter.java
Normal file
|
@ -0,0 +1,585 @@
|
|||
package cd.backend.interpreter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.InputMismatchException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Scanner;
|
||||
|
||||
import cd.backend.ExitCode;
|
||||
import cd.backend.interpreter.Interpreter.MethodInterp.EarlyReturnException;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.Ast.Assign;
|
||||
import cd.ir.Ast.BinaryOp;
|
||||
import cd.ir.Ast.BooleanConst;
|
||||
import cd.ir.Ast.BuiltInRead;
|
||||
import cd.ir.Ast.BuiltInWrite;
|
||||
import cd.ir.Ast.BuiltInWriteln;
|
||||
import cd.ir.Ast.Cast;
|
||||
import cd.ir.Ast.ClassDecl;
|
||||
import cd.ir.Ast.Expr;
|
||||
import cd.ir.Ast.Field;
|
||||
import cd.ir.Ast.IfElse;
|
||||
import cd.ir.Ast.Index;
|
||||
import cd.ir.Ast.IntConst;
|
||||
import cd.ir.Ast.MethodCall;
|
||||
import cd.ir.Ast.MethodCallExpr;
|
||||
import cd.ir.Ast.MethodDecl;
|
||||
import cd.ir.Ast.NewArray;
|
||||
import cd.ir.Ast.NewObject;
|
||||
import cd.ir.Ast.NullConst;
|
||||
import cd.ir.Ast.ReturnStmt;
|
||||
import cd.ir.Ast.Stmt;
|
||||
import cd.ir.Ast.ThisRef;
|
||||
import cd.ir.Ast.UnaryOp;
|
||||
import cd.ir.Ast.Var;
|
||||
import cd.ir.Ast.WhileLoop;
|
||||
import cd.ir.AstVisitor;
|
||||
import cd.ir.BasicBlock;
|
||||
import cd.ir.ControlFlowGraph;
|
||||
import cd.ir.Symbol.ArrayTypeSymbol;
|
||||
import cd.ir.Symbol.ClassSymbol;
|
||||
import cd.ir.Symbol.TypeSymbol;
|
||||
import cd.ir.Symbol.VariableSymbol;
|
||||
|
||||
/**
|
||||
* An interpreter for the Javali IR. It requires that the IR be fully
|
||||
* semantically analyzed -- in particular, that symbols be assigned to Var and
|
||||
* Field nodes.
|
||||
*
|
||||
* It can interpret either the AST used in CD1 or the CFG from CD2. It detects
|
||||
* infinite loops and also tracks how many operations of each kind were
|
||||
* performed.
|
||||
*/
|
||||
public class Interpreter {
|
||||
|
||||
private static final long MAX_STEPS = 100000000;
|
||||
|
||||
private long steps = 0;
|
||||
private final JlNull nullPointer = new JlNull();
|
||||
|
||||
private final List<ClassDecl> classDecls;
|
||||
private final Writer output;
|
||||
private final Scanner input;
|
||||
|
||||
public Interpreter(List<ClassDecl> classDecls, Reader in, Writer out) {
|
||||
this.classDecls = classDecls;
|
||||
this.input = new Scanner(in);
|
||||
this.output = out;
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
ClassSymbol mainType = findMainClass();
|
||||
invokeMethod(mainType, "main", new JlObject(mainType),
|
||||
Collections.<JlValue> emptyList());
|
||||
}
|
||||
|
||||
// Optimization detection:
|
||||
//
|
||||
// We count the number of binary and unary operations that
|
||||
// occurred during execution and compare this to a fully-optimized version.
|
||||
// The key to this hashtable is either a BinaryOp.BOp or UnaryOp.UOp.
|
||||
private final Map<Object, Integer> opCounts = new HashMap<Object, Integer>();
|
||||
|
||||
private void increment(Object operator) {
|
||||
Integer current = opCounts.get(operator);
|
||||
if (current == null)
|
||||
opCounts.put(operator, 1);
|
||||
else
|
||||
opCounts.put(operator, current + 1);
|
||||
}
|
||||
|
||||
public String operationSummary() {
|
||||
|
||||
List<String> operationSummaries = new ArrayList<String>();
|
||||
|
||||
for (Object operation : opCounts.keySet()) {
|
||||
operationSummaries.add(String.format("%s: %s\n", operation,
|
||||
opCounts.get(operation)));
|
||||
}
|
||||
|
||||
Collections.sort(operationSummaries);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String summary : operationSummaries) {
|
||||
sb.append(summary);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void step() {
|
||||
|
||||
// Stop after taking too many evaluation steps!
|
||||
if (++steps > MAX_STEPS) {
|
||||
throw new DynamicError("Infinite Loop!",
|
||||
ExitCode.INFINITE_LOOP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The interpreter proper:
|
||||
class ExprInterp extends AstVisitor<JlValue, StackFrame> {
|
||||
|
||||
private JlValue v(int value) {
|
||||
return new JlInt(value);
|
||||
}
|
||||
|
||||
private JlValue v(boolean b) {
|
||||
return new JlBoolean(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue visit(Ast ast, StackFrame arg) {
|
||||
step();
|
||||
return super.visit(ast, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue binaryOp(BinaryOp ast, StackFrame arg) {
|
||||
|
||||
try {
|
||||
|
||||
final JlValue left = visit(ast.left(), arg);
|
||||
final JlValue right = visit(ast.right(), arg);
|
||||
|
||||
// TODO Only increment this operator for integers
|
||||
increment(ast.operator);
|
||||
switch (ast.operator) {
|
||||
case B_TIMES :
|
||||
return left.times(right);
|
||||
case B_DIV :
|
||||
return left.div(right);
|
||||
case B_MOD :
|
||||
return left.mod(right);
|
||||
case B_PLUS :
|
||||
return left.add(right);
|
||||
case B_MINUS :
|
||||
return left.subtract(right);
|
||||
case B_AND :
|
||||
return left.and(right);
|
||||
case B_OR :
|
||||
return left.or(right);
|
||||
case B_EQUAL :
|
||||
return v(left.equals(right));
|
||||
case B_NOT_EQUAL :
|
||||
return v(!left.equals(right));
|
||||
case B_LESS_THAN :
|
||||
return left.less(right);
|
||||
case B_LESS_OR_EQUAL :
|
||||
return left.lessOrEqual(right);
|
||||
case B_GREATER_THAN :
|
||||
return left.greater(right);
|
||||
case B_GREATER_OR_EQUAL :
|
||||
return left.greaterOrEqual(right);
|
||||
}
|
||||
|
||||
throw new DynamicError("Unhandled binary operator",
|
||||
ExitCode.INTERNAL_ERROR);
|
||||
|
||||
} catch (ArithmeticException e) {
|
||||
throw new DynamicError("Division by zero",
|
||||
ExitCode.DIVISION_BY_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue booleanConst(BooleanConst ast, StackFrame arg) {
|
||||
return v(ast.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue builtInRead(BuiltInRead ast, StackFrame arg) {
|
||||
try {
|
||||
return v(input.nextInt());
|
||||
} catch (InputMismatchException e) {
|
||||
throw new DynamicError("Your .javali.in file is malformed.",
|
||||
ExitCode.INTERNAL_ERROR);
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new DynamicError("Your .javali.in does not contain enough numbers"
|
||||
+ " or may not exist at all.\nMake sure that test cases that contain "
|
||||
+ "read() expressions provide a [testcasename].javali.in file.",
|
||||
ExitCode.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue cast(Cast ast, StackFrame arg) {
|
||||
|
||||
JlReference ref = visit(ast.arg(), arg).asRef();
|
||||
|
||||
if (ref.canBeCastTo(ast.typeName)) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
throw new DynamicError("Cast failure: cannot cast " + ref.typeSym
|
||||
+ " to " + ast.typeName, ExitCode.INVALID_DOWNCAST);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue field(Field ast, StackFrame arg) {
|
||||
JlValue lhs = visit(ast.arg(), arg);
|
||||
return lhs.asRef().field(ast.sym);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue index(Index ast, StackFrame arg) {
|
||||
JlValue lhs = visit(ast.left(), arg);
|
||||
JlValue idx = visit(ast.right(), arg);
|
||||
return lhs.asRef().deref(idx.asInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue intConst(IntConst ast, StackFrame arg) {
|
||||
return v(ast.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue newArray(NewArray ast, StackFrame arg) {
|
||||
JlValue size = visit(ast.arg(), arg);
|
||||
return new JlArray((ArrayTypeSymbol) ast.type, size.asInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue newObject(NewObject ast, StackFrame arg) {
|
||||
return new JlObject((ClassSymbol) ast.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue nullConst(NullConst ast, StackFrame arg) {
|
||||
return nullPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue thisRef(ThisRef ast, StackFrame arg) {
|
||||
return arg.getThisPointer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue methodCall(MethodCallExpr ast, StackFrame frame) {
|
||||
|
||||
JlObject rcvr = expr(ast.receiver(), frame).asObject();
|
||||
|
||||
List<JlValue> arguments = new ArrayList<JlValue>();
|
||||
for (Ast arg : ast.argumentsWithoutReceiver()) {
|
||||
arguments.add(expr(arg, frame));
|
||||
}
|
||||
|
||||
return invokeMethod((ClassSymbol) rcvr.typeSym, ast.methodName,
|
||||
rcvr, arguments);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue unaryOp(UnaryOp ast, StackFrame arg) {
|
||||
|
||||
JlValue val = visit(ast.arg(), arg);
|
||||
|
||||
// TODO Increment this only when is an int
|
||||
increment(ast.operator);
|
||||
|
||||
switch (ast.operator) {
|
||||
case U_PLUS :
|
||||
return val.plus();
|
||||
case U_MINUS :
|
||||
return val.minus();
|
||||
case U_BOOL_NOT :
|
||||
return val.not();
|
||||
}
|
||||
|
||||
throw new DynamicError("Unhandled unary operator " + ast.operator,
|
||||
ExitCode.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue var(Var ast, StackFrame arg) {
|
||||
|
||||
if (ast.sym == null) {
|
||||
throw new DynamicError("Var node with null symbol",
|
||||
ExitCode.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
switch (ast.sym.kind) {
|
||||
|
||||
case LOCAL :
|
||||
case PARAM :
|
||||
return arg.var(ast.sym);
|
||||
case FIELD :
|
||||
return arg.getThisPointer().field(ast.sym);
|
||||
}
|
||||
|
||||
throw new DynamicError("Unhandled VariableSymbol kind: "
|
||||
+ ast.sym.kind, ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public JlValue expr(Ast ast, StackFrame frame) {
|
||||
return new ExprInterp().visit(ast, frame);
|
||||
}
|
||||
|
||||
private ClassSymbol findMainClass() {
|
||||
for (ClassDecl classDecl : classDecls) {
|
||||
if (classDecl.name.equals("Main"))
|
||||
return classDecl.sym;
|
||||
}
|
||||
throw new StaticError("No Main class found");
|
||||
}
|
||||
|
||||
public ClassDecl findClassDecl(TypeSymbol typeSym) {
|
||||
for (ClassDecl cd : classDecls) {
|
||||
if (cd.sym == typeSym)
|
||||
return cd;
|
||||
}
|
||||
|
||||
throw new StaticError("No such type " + typeSym.name);
|
||||
}
|
||||
|
||||
class MethodInterp extends cd.ir.AstVisitor<JlValue, StackFrame> {
|
||||
|
||||
public boolean earlyReturn = false;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class EarlyReturnException extends RuntimeException {
|
||||
public JlValue value;
|
||||
public EarlyReturnException(final JlValue value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue assign(Assign ast, final StackFrame frame) {
|
||||
|
||||
new AstVisitor<Void, Expr>() {
|
||||
|
||||
@Override
|
||||
public Void field(Field ast, Expr right) {
|
||||
JlValue obj = expr(ast.arg(), frame);
|
||||
assert obj != null && obj.asRef() != null;
|
||||
final JlValue val = expr(right, frame);
|
||||
assert val != null;
|
||||
obj.asRef().setField(ast.sym, val);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void index(Index ast, Expr right) {
|
||||
JlValue obj = expr(ast.left(), frame);
|
||||
JlValue idx = expr(ast.right(), frame);
|
||||
final JlValue val = expr(right, frame);
|
||||
assert val != null;
|
||||
obj.asRef().setDeref(idx.asInt(), val);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void var(Var ast, Expr right) {
|
||||
final JlValue val = expr(right, frame);
|
||||
assert val != null;
|
||||
frame.setVar(ast.sym, val);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void dflt(Ast ast, Expr arg) {
|
||||
throw new StaticError("Malformed l-value in AST");
|
||||
}
|
||||
|
||||
}.visit(ast.left(), ast.right());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue builtInWrite(BuiltInWrite ast, StackFrame frame) {
|
||||
|
||||
JlValue val = expr(ast.arg(), frame);
|
||||
|
||||
try {
|
||||
output.write(Integer.toString(val.asInt()));
|
||||
} catch (IOException e) {
|
||||
throw new DynamicError(e, ExitCode.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue builtInWriteln(BuiltInWriteln ast, StackFrame arg) {
|
||||
|
||||
try {
|
||||
output.write("\n");
|
||||
} catch (IOException e) {
|
||||
throw new DynamicError(e, ExitCode.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue ifElse(IfElse ast, StackFrame frame) {
|
||||
|
||||
JlValue cond = expr(ast.condition(), frame);
|
||||
|
||||
if (cond.asBoolean()) {
|
||||
return visit(ast.then(), frame);
|
||||
} else {
|
||||
return visit(ast.otherwise(), frame);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue methodCall(MethodCall ast, final StackFrame frame) {
|
||||
return expr(ast.getMethodCallExpr(), frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue whileLoop(WhileLoop ast, StackFrame frame) {
|
||||
|
||||
while (true) {
|
||||
|
||||
JlValue cond = expr(ast.condition(), frame);
|
||||
|
||||
if (!cond.asBoolean()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
visit(ast.body(), frame);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue returnStmt(ReturnStmt ast, StackFrame frame) {
|
||||
|
||||
JlValue ret = null;
|
||||
|
||||
if (ast.arg() != null) {
|
||||
ret = expr(ast.arg(), frame);
|
||||
} else {
|
||||
ret = new JlNull();
|
||||
}
|
||||
|
||||
if (!earlyReturn) {
|
||||
return ret;
|
||||
} else {
|
||||
throw new EarlyReturnException(ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public MethodDecl findMethodDecl(ClassSymbol typeSym,
|
||||
final String methodName) {
|
||||
ClassSymbol currSym = typeSym;
|
||||
while (currSym != ClassSymbol.objectType) {
|
||||
ClassDecl cd = findClassDecl(currSym);
|
||||
final List<MethodDecl> result = new ArrayList<MethodDecl>();
|
||||
|
||||
for (Ast mem : cd.members()) {
|
||||
AstVisitor<Void, Void> vis = new AstVisitor<Void, Void>() {
|
||||
@Override
|
||||
public Void methodDecl(MethodDecl ast, Void arg) {
|
||||
|
||||
if (!ast.name.equals(methodName)) {
|
||||
return null;
|
||||
}
|
||||
result.add(ast);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
vis.visit(mem, null);
|
||||
}
|
||||
|
||||
if (result.size() == 1) {
|
||||
return result.get(0);
|
||||
}
|
||||
|
||||
if (result.size() > 1) {
|
||||
throw new StaticError(result.size()
|
||||
+ " implementations of method " + methodName
|
||||
+ " found in type " + currSym.name);
|
||||
}
|
||||
|
||||
currSym = currSym.superClass;
|
||||
}
|
||||
|
||||
throw new StaticError("No method " + methodName + " in type " + typeSym);
|
||||
}
|
||||
|
||||
// Note: does not interpret phis!
|
||||
public JlValue interpretCfg(ControlFlowGraph cfg, final StackFrame frame) {
|
||||
|
||||
BasicBlock current = cfg.start;
|
||||
MethodInterp minterp = new MethodInterp();
|
||||
JlValue res = null;
|
||||
|
||||
while (true) {
|
||||
step();
|
||||
|
||||
for(Stmt stmt : current.stmts) {
|
||||
res = minterp.visit(stmt, frame);
|
||||
if(stmt instanceof ReturnStmt)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (current == cfg.end) {
|
||||
return res;
|
||||
} else if (current.condition == null) {
|
||||
current = current.successors.get(0);
|
||||
} else {
|
||||
|
||||
JlValue cond = expr(current.condition, frame);
|
||||
|
||||
if (cond.asBoolean()) {
|
||||
current = current.trueSuccessor();
|
||||
} else {
|
||||
current = current.falseSuccessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public JlValue invokeMethod(final ClassSymbol typeSym,
|
||||
final String methodName, final JlObject rcvr,
|
||||
final List<JlValue> arguments) {
|
||||
MethodDecl mdecl = findMethodDecl(typeSym, methodName);
|
||||
StackFrame newFrame = new StackFrame(rcvr);
|
||||
int idx = 0;
|
||||
|
||||
for (VariableSymbol sym : mdecl.sym.parameters) {
|
||||
newFrame.setVar(sym, arguments.get(idx++));
|
||||
}
|
||||
|
||||
if (mdecl.cfg != null) {
|
||||
return interpretCfg(mdecl.cfg, newFrame);
|
||||
} else {
|
||||
final MethodInterp mthInterp = new MethodInterp();
|
||||
mthInterp.earlyReturn = true;
|
||||
try {
|
||||
return mthInterp.visit(mdecl.body(), newFrame);
|
||||
} catch (final EarlyReturnException ex) {
|
||||
return ex.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
544
test/cd/backend/interpreter/JlValue.java
Normal file
544
test/cd/backend/interpreter/JlValue.java
Normal file
|
@ -0,0 +1,544 @@
|
|||
package cd.backend.interpreter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import cd.backend.ExitCode;
|
||||
import cd.ir.Symbol.ArrayTypeSymbol;
|
||||
import cd.ir.Symbol.ClassSymbol;
|
||||
import cd.ir.Symbol.PrimitiveTypeSymbol;
|
||||
import cd.ir.Symbol.TypeSymbol;
|
||||
import cd.ir.Symbol.VariableSymbol;
|
||||
|
||||
// Values:
|
||||
abstract class JlValue {
|
||||
public final TypeSymbol typeSym;
|
||||
|
||||
static public JlValue getDefault(TypeSymbol type) {
|
||||
JlValue result = null;
|
||||
if (type.isReferenceType())
|
||||
result = new JlNull();
|
||||
else if (type == PrimitiveTypeSymbol.booleanType)
|
||||
result = new JlBoolean(false);
|
||||
else if (type == PrimitiveTypeSymbol.intType)
|
||||
result = new JlInt(0);
|
||||
assert result != null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public JlValue(TypeSymbol s) {
|
||||
typeSym = s;
|
||||
}
|
||||
|
||||
protected void defaultConversion() {
|
||||
throw new StaticError("Type conversion between incompatible types.");
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
defaultConversion();
|
||||
return false;
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
defaultConversion();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public JlReference asRef() {
|
||||
defaultConversion();
|
||||
return null;
|
||||
}
|
||||
|
||||
public JlObject asObject() {
|
||||
defaultConversion();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Binary Operations
|
||||
// Arithmetic
|
||||
protected JlValue defaultOperation() {
|
||||
throw new StaticError("Operation not supported on " + this.getClass());
|
||||
}
|
||||
|
||||
public JlValue times(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue div(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue add(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue subtract(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue mod(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
// Boolean
|
||||
public JlValue or(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue and(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
// Comparison
|
||||
public JlValue less(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue lessOrEqual(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue greater(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue greaterOrEqual(JlValue value) {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
// Unary Operation
|
||||
public JlValue plus() {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue minus() {
|
||||
return defaultOperation();
|
||||
}
|
||||
|
||||
public JlValue not() {
|
||||
return defaultOperation();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class JlReference extends JlValue {
|
||||
|
||||
public JlReference(TypeSymbol s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlReference asRef() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract JlValue field(VariableSymbol name);
|
||||
|
||||
public abstract void setField(VariableSymbol name, JlValue val);
|
||||
|
||||
public abstract JlValue deref(int index);
|
||||
|
||||
public abstract void setDeref(int index, JlValue val);
|
||||
|
||||
public abstract boolean canBeCastTo(String typeName);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s(%x)", typeSym, System.identityHashCode(this));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JlObject extends JlReference {
|
||||
|
||||
protected Map<VariableSymbol, JlValue> fields = new HashMap<VariableSymbol, JlValue>();
|
||||
|
||||
public JlObject(ClassSymbol s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlObject asObject() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue field(VariableSymbol name) {
|
||||
if (fields.containsKey(name)) {
|
||||
return fields.get(name);
|
||||
}
|
||||
|
||||
JlValue dflt = JlValue.getDefault(name.type);
|
||||
setField(name, dflt);
|
||||
return dflt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(VariableSymbol name, JlValue val) {
|
||||
fields.put(name, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue deref(int index) {
|
||||
throw new StaticError("Not an array");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeref(int index, JlValue val) {
|
||||
throw new StaticError("Not an array");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastTo(String typeName) {
|
||||
// Can always cast to Object:
|
||||
if (typeName.equals("Object"))
|
||||
return true;
|
||||
|
||||
// Make up a set of acceptable types. Check for circular loops!
|
||||
Set<String> superTypes = new HashSet<String>();
|
||||
ClassSymbol currentType = (ClassSymbol)typeSym;
|
||||
|
||||
while (!currentType.name.equals("Object")) {
|
||||
if (superTypes.contains(currentType.name)) {
|
||||
throw new StaticError("Circular inheritance: " + currentType.name);
|
||||
}
|
||||
|
||||
superTypes.add(currentType.name);
|
||||
currentType = currentType.superClass;
|
||||
}
|
||||
|
||||
return superTypes.contains(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
class JlArray extends JlReference {
|
||||
|
||||
private JlValue contents[];
|
||||
|
||||
public JlArray(ArrayTypeSymbol s, int size) {
|
||||
super(s);
|
||||
|
||||
if (size < 0) {
|
||||
throw new DynamicError("Invalid array size: " + size,
|
||||
ExitCode.INVALID_ARRAY_SIZE);
|
||||
}
|
||||
|
||||
this.contents = new JlValue[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
this.contents[i] = JlValue.getDefault(s.elementType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlReference asRef() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JlArray asArray() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue deref(int idx) {
|
||||
|
||||
try {
|
||||
return contents[idx];
|
||||
} catch (final ArrayIndexOutOfBoundsException ex) {
|
||||
throw new DynamicError("Array index out of bounds " + idx,
|
||||
ExitCode.INVALID_ARRAY_BOUNDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeref(int idx, JlValue value) {
|
||||
|
||||
try {
|
||||
contents[idx] = value;
|
||||
} catch (final ArrayIndexOutOfBoundsException ex) {
|
||||
throw new DynamicError("Array index out of bounds " + idx,
|
||||
ExitCode.INVALID_ARRAY_BOUNDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue field(VariableSymbol name) {
|
||||
throw new StaticError("Not an object");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(VariableSymbol name, JlValue value) {
|
||||
throw new StaticError("Not an object");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastTo(String typeName) {
|
||||
return this.typeSym.name.equals(typeName) || typeName.equals("Object");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JlNull extends JlReference {
|
||||
|
||||
public JlNull() {
|
||||
super(ClassSymbol.nullType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (o instanceof JlNull) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlReference asRef() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlObject asObject() {
|
||||
throw new DynamicError("Null pointer dereferenced",
|
||||
ExitCode.NULL_POINTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue field(VariableSymbol name) {
|
||||
throw new DynamicError("Null pointer dereferenced",
|
||||
ExitCode.NULL_POINTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(VariableSymbol name, JlValue value) {
|
||||
throw new DynamicError("Null pointer dereferenced",
|
||||
ExitCode.NULL_POINTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue deref(int idx) {
|
||||
throw new DynamicError("Null pointer dereferenced",
|
||||
ExitCode.NULL_POINTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeref(int idx, JlValue value) {
|
||||
throw new DynamicError("Null pointer dereferenced",
|
||||
ExitCode.NULL_POINTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastTo(String typeName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JlInt extends JlValue {
|
||||
|
||||
private final int value;
|
||||
|
||||
JlInt(int value) {
|
||||
super(PrimitiveTypeSymbol.intType);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (o instanceof JlInt) {
|
||||
return value == ((JlInt) o).value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue times(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlInt(this.value * ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue div(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlInt(this.value / ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue mod(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlInt(this.value % ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue add(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlInt(this.value + ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue subtract(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlInt(this.value - ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue less(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlBoolean(this.value < ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue lessOrEqual(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlBoolean(this.value <= ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue greater(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlBoolean(this.value > ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue greaterOrEqual(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlInt) {
|
||||
return new JlBoolean(this.value >= ((JlInt) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue plus() {
|
||||
return new JlInt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue minus() {
|
||||
return new JlInt(-value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int asInt() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JlBoolean extends JlValue {
|
||||
|
||||
private final boolean value;
|
||||
|
||||
JlBoolean(boolean value) {
|
||||
super(PrimitiveTypeSymbol.booleanType);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (o instanceof JlBoolean) {
|
||||
return value == ((JlBoolean) o).value;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue not() {
|
||||
return new JlBoolean(!value);
|
||||
};
|
||||
|
||||
@Override
|
||||
public JlValue or(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlBoolean) {
|
||||
return new JlBoolean(this.value || ((JlBoolean) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public JlValue and(JlValue obj) {
|
||||
|
||||
if (obj instanceof JlBoolean) {
|
||||
return new JlBoolean(this.value && ((JlBoolean) obj).value);
|
||||
}
|
||||
|
||||
throw new DynamicError("Invalid type!", ExitCode.INTERNAL_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean asBoolean() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Boolean.toString(value);
|
||||
}
|
||||
|
||||
}
|
41
test/cd/backend/interpreter/StackFrame.java
Normal file
41
test/cd/backend/interpreter/StackFrame.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package cd.backend.interpreter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cd.ir.Symbol.VariableSymbol;
|
||||
|
||||
// The stack:
|
||||
class StackFrame {
|
||||
|
||||
private final JlObject thisPointer;
|
||||
private final Map<VariableSymbol, JlValue> variables;
|
||||
|
||||
public StackFrame(JlObject thisPointer) {
|
||||
this.thisPointer = thisPointer;
|
||||
this.variables = new HashMap<VariableSymbol, JlValue>();
|
||||
}
|
||||
|
||||
public JlValue var(VariableSymbol name) {
|
||||
if (variables.containsKey(name)) {
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
JlValue dflt = JlValue.getDefault(name.type);
|
||||
setVar(name, dflt);
|
||||
return dflt;
|
||||
}
|
||||
|
||||
public void setVar(VariableSymbol name, JlValue val) {
|
||||
variables.put(name, val);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("StackFrame(%s) {%s}", System.identityHashCode(this), variables.toString());
|
||||
}
|
||||
|
||||
public JlObject getThisPointer() {
|
||||
return thisPointer;
|
||||
}
|
||||
|
||||
}
|
10
test/cd/backend/interpreter/StaticError.java
Normal file
10
test/cd/backend/interpreter/StaticError.java
Normal file
|
@ -0,0 +1,10 @@
|
|||
package cd.backend.interpreter;
|
||||
|
||||
/** Thrown in cases that should be ruled out by static analysis: */
|
||||
@SuppressWarnings("serial")
|
||||
public
|
||||
class StaticError extends RuntimeException {
|
||||
public StaticError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue