Homework 4
This commit is contained in:
parent
0afc86ceeb
commit
72cc3206c4
125 changed files with 4200 additions and 1636 deletions
|
@ -2,10 +2,10 @@
|
|||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="lib" path="lib/antlr-4.4-complete.jar"/>
|
||||
<classpathentry kind="lib" path="lib/junit-4.12.jar"/>
|
||||
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/antlr-4.7.1-complete.jar"/>
|
||||
<classpathentry kind="lib" path="lib/javaliParserObf.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
2
.project
2
.project
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Javali-HW3</name>
|
||||
<name>Javali-HW4</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
39
Grade.txt
39
Grade.txt
|
@ -1,6 +1,39 @@
|
|||
Grade: 25/25
|
||||
nop90: 29/30 points
|
||||
|
||||
105/105 tests passed [20/20]
|
||||
Comments:
|
||||
I found that the following tasks were not implemented correctly or contained errors.
|
||||
|
||||
Submitted test cases: [5/5]
|
||||
* Null pointers check doesn't work for methods:
|
||||
"
|
||||
class Main {
|
||||
void main() {
|
||||
A a;
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
class A {
|
||||
void m() { }
|
||||
}
|
||||
"
|
||||
* Side effects
|
||||
If methods have side effects, then the execution order matters:
|
||||
"
|
||||
class Main {
|
||||
void main() {
|
||||
write(m1() + m2());
|
||||
writeln();
|
||||
}
|
||||
|
||||
int m1() {
|
||||
write(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int m2() {
|
||||
write(2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
"
|
||||
* Can't compile programs which use many registers
|
||||
|
||||
|
|
144
README.md
144
README.md
|
@ -1,55 +1,111 @@
|
|||
# Compiler design
|
||||
## Parser and syntactic analysis
|
||||
See repo containing [Homework #2](https://svn.inf.ethz.ch/svn/trg/cd_students/ss18/teams/nop90/HW2).
|
||||
# HW4: code generation
|
||||
Better viewed as markdown
|
||||
|
||||
## Semantic analysis
|
||||
This project checks a list class declaration as trees of Ast nodes for
|
||||
possible semantic errors.
|
||||
# VTables
|
||||
They are generated when each class is visited, except for those of Object,
|
||||
Object[], int[] and boolean[], which are generated in the bootstrapping.
|
||||
|
||||
### Design
|
||||
The semantic checks described [here](./SemanticChecks.md) are implemented in three
|
||||
phases.
|
||||
When the object vtable is generated, so is the array table, which just
|
||||
contains a reference to the supertype (in our case `Object`) and an unused
|
||||
pointer to the declared type of the element's vtable.
|
||||
|
||||
1. Circular inheritance check (just with the names, and checking for non-existent
|
||||
types) to avoid stack overflow problems in the main analysis. May throw a
|
||||
`NO_SUCH_TYPE` error if the parent of a given class doesn't exist.
|
||||
2. Main semantic analysis, performed by `StmtAnalyzer` and `ExprAnalyzer`,
|
||||
two visitors that traverse each `ClassDecl` and all of its contents looking
|
||||
for semantic errors. Some types and methods may not have been discovered
|
||||
when they are accessed, and to that end the functions `findType(String)`
|
||||
and `findMethod(String,ClassSymbol)` lookup both, searching unvisited class
|
||||
and method declarations in order to find the corresponding symbol.
|
||||
3. Lookup of start point (method `void main()` in `Main` class)
|
||||
supertype pointer (Object) <-- vtable pointer
|
||||
element vtable pointer (unused)
|
||||
|
||||
### Testing
|
||||
A set of test cases (Javali programs, valid and invalid) are provided. The ones
|
||||
designed specifically for this homework assignment are located in
|
||||
`javali_tests/HW3_nop90`, and they cover all the code in the `cd.frontend.semantic`
|
||||
and `cd.ir` packages. They cover all the kinds of errors that can arise
|
||||
from a semantic analysis of Javali programs. As the order of the errors
|
||||
is not determined, each test case only checks one failing condition, and
|
||||
therefore many more test cases are needed for this homework than for
|
||||
previous ones.
|
||||
Each type's vtable contains the supertype as the first element (trait shared
|
||||
with array type's vtables for easier cast type comparisons). Then they
|
||||
contain all the methods available to an object of that type, including
|
||||
all those inherited. As the dynamic type is not known, the number and
|
||||
position of the methods is not known when executing a method, therefore
|
||||
each method's pointer is prepended by the method's name hash (similar to
|
||||
fields in an object).
|
||||
|
||||
The test files are organized in folders, with names ending in Tests or Errors,
|
||||
the latter only contain test cases that result in semantic errors.
|
||||
supertype pointer <-- vtable pointer
|
||||
hash("method0")
|
||||
method0 pointer
|
||||
hash("method1")
|
||||
method1 pointer
|
||||
...
|
||||
hash("methodN")
|
||||
methodN pointer
|
||||
|
||||
To run the tests that exist in the `javali_tests` folder you can simply run
|
||||
# Method calls
|
||||
|
||||
$ ant clean clean-test compile test
|
||||
The basic scheme used for method calls and the structure of the stack
|
||||
frame is that described in the `__cdecl` used in GCC. In short, this is
|
||||
the execution of a method call:
|
||||
|
||||
### Development environment
|
||||
This project is designed for Eclipse and GNU/Linux, but it can be run
|
||||
in other environments, as long as they have a 32bit `IA 86x assembly` compiler
|
||||
and a `JVM`. Check the `Config` class for more information.
|
||||
1. The caller pushes the arguments in the stack, from right-to-left,
|
||||
leaving the 0th argument (the reference to `this`) at the top of the stack.
|
||||
2. The caller executes `call` (the instruction pointer gets stored in the stack)
|
||||
3. The callee now takes control, and its first action is to save the base
|
||||
pointer to the stack and set the base pointer to the current stack pointer.
|
||||
Therefore, the arguments and return address are available to the method.
|
||||
4. The local variables are reserved and zeroed in the stack.
|
||||
5. The CALLEE_SAVED registers are saved to the stack.
|
||||
6. The method executes its code
|
||||
7. The CALLEE_SAVED registers are restored to their original status.
|
||||
8. The callee restores the stack and base registers to its initial statuses.
|
||||
This is accomplished with a `leave` instruction. Then it returns using `ret`.
|
||||
This removes the instruction pointer from the stack leaving only the arguments.
|
||||
9. The caller finally removes all the arguments from the stack and obtains the
|
||||
return value from EAX.
|
||||
|
||||
### Documentation
|
||||
Available as [javadoc](javadoc/index.html). It is generated running the following command:
|
||||
Therefore, the stack frame follows the following structure (N arguments
|
||||
and M local variables):
|
||||
|
||||
$ ant clean-doc generate-doc
|
||||
top of stack <-- ESP
|
||||
...
|
||||
saved registers
|
||||
localM <-- EBP - 4 - M * 4
|
||||
...
|
||||
local1 <-- EBP - 8
|
||||
local0 <-- EBP - 4
|
||||
Old EBP <-- EBP
|
||||
Return EIP
|
||||
this <-- EBP + 8
|
||||
arg0 <-- EBP + 12
|
||||
...
|
||||
argN <-- EBP + 12 + N * 4
|
||||
|
||||
### Important files
|
||||
* `TODO.md`: contains tasks to complete
|
||||
* `SemanticChecks.md`: contains the semantic checks numbered for quick reference
|
||||
* [Javali Grammar](https://www.ethz.ch/content/dam/ethz/special-interest/infk/inst-cs/lst-dam/documents/Education/Classes/Spring2018/210_Compiler_Design/Homework/javali.pdf)
|
||||
* [HW3 statement](https://www.ethz.ch/content/dam/ethz/special-interest/infk/inst-cs/lst-dam/documents/Education/Classes/Spring2018/210_Compiler_Design/Homework/hw3.pdf)
|
||||
# Variable and object representation
|
||||
## Primitive variables
|
||||
They are represented by value, with both integers and booleans occupying
|
||||
4 bytes, and booleans being represented as TRUE = 0, FALSE = 1.
|
||||
|
||||
## Reference variables
|
||||
Stored in the heap, using calloc (to zero-initialize them).
|
||||
|
||||
### Arrays
|
||||
On top of storing the array elements, some overhead is stored with it,
|
||||
namely a pointer to the array vtable for casts, and the size of the
|
||||
array to check the bounds of the array on accesses or assignments.
|
||||
The pointer to the array points to the vtable pointer (the first
|
||||
element in the object structure):
|
||||
|
||||
array vtable pointer <-- var pointer
|
||||
array size
|
||||
element0
|
||||
element1
|
||||
...
|
||||
elementN
|
||||
|
||||
### Objects
|
||||
|
||||
Stores a pointer to the object's type vtable and all the fields. As the
|
||||
dynamic type of the object cannot be determined and therefore the fields
|
||||
not statically ordered, each field occupies 8 bytes, with the first 4 containing
|
||||
the hash for the VariableSymbol that represents it and the others the
|
||||
value or reference to the field.
|
||||
|
||||
The hash allows hiding variables, but introduces an overhead when accessing
|
||||
a field, to look for the position in which it is located.
|
||||
|
||||
vtable pointer <-- var pointer
|
||||
field0's hash
|
||||
field0
|
||||
field1's hash
|
||||
field1
|
||||
...
|
||||
fieldN's hash
|
||||
fieldN
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
# Class and method declaration errors
|
||||
1. All programs must have a method main() with void return type in the class Main (`INVALID_START_POINT`)
|
||||
2. The superclass of each class exists (`NO_SUCH_TYPE`)
|
||||
3. There is no circular inheritance (`CIRCULAR_INHERITANCE`)
|
||||
4. No class is declared with the name `Object` (`OBJECT_CLASS_DEFINED`)
|
||||
5. Unique names (`DOUBLE_DECLARATION`) for each group:
|
||||
1. Class names
|
||||
2. Field names in a single class (field shadowing is allowed)
|
||||
3. Method names in a single class (Override is allowed, overloading not)
|
||||
4. Method parameters and local variable names
|
||||
6. Overridden methods must: (`INVALID_OVERRIDE`)
|
||||
1. Have the same number of parameters
|
||||
2. The return types must match
|
||||
3. The type of the parameters must match (name can differ)
|
||||
|
||||
# Method body errors
|
||||
1. `write()` must take one argument of type `int` (`TYPE_ERROR`)
|
||||
2. `read()` and `writeln()` must take no arguments (`WRONG_NUMBER_OF_ARGUMENTS`)
|
||||
3. The condition of a `while` or `if` statement must be typed `booelan` (`TYPE_ERROR`)
|
||||
4. Assignments: the type of the right-hand side must be a suptype of the type of the left-hand side. (`TYPE_ERROR`)
|
||||
5. Operators (`TYPE_ERROR`):
|
||||
1. `*`, `/`, `%`, `+`, `-`, `<`, `>`, `<=`, `>=` take `int` arguments only.
|
||||
2. `!`, `&&`, `||` take `boolean` arguments only.
|
||||
3. `==` and `!=` takes any couple of arguments as long as one of the types is a subtype of the other.
|
||||
4. A cast is only possible if one of the types is a subtype of the other
|
||||
6. Method invocation, must match with declaration:
|
||||
1. The number of parameters (`WRONG_NUMBER_OF_ARGUMENTS`)
|
||||
2. The type of each of the parameters (`TYPE_ERROR`)
|
||||
7. Arrays:
|
||||
1. When indexing, the index expression must be an `int` (`TYPE_ERROR`)
|
||||
2. When creating one, the index expression must be an `int` (`TYPE_ERROR`)
|
||||
8. The constant `null` can be assigned to any reference type.
|
||||
9. The type name in a `new` statement must exist (`NO_SUCH_TYPE`)
|
||||
10. The left-hand side of an assignment can only be: (`NOT_ASSIGNABLE`)
|
||||
1. A variable
|
||||
2. A field
|
||||
3. Array dereferences (indexing)
|
||||
11. There must be no attempt to access the field or method of a
|
||||
non-object type, such as arrays or primitive types (`TYPE_ERROR`)
|
||||
12. A method with a return type must have a return statement at the end
|
||||
of all its paths (`MISSING_RETURN`). A while loop is not a valid return
|
||||
regardless of the condition. A ifElse must have a return on both the
|
||||
else and otherwise block. There may be some statements after a return.
|
75
TODO.md
75
TODO.md
|
@ -1,75 +0,0 @@
|
|||
## Possible code changes
|
||||
### General
|
||||
* DONE Use more specific messages when throwing a `SemanticFailure` exception. This has the caveat that the automated testing relies on those tests?
|
||||
|
||||
### StmtAnalyzer
|
||||
* DONE Propagate the change of class parameter from `ExprAnalyzer`.
|
||||
* DONE Complete checks for `MISSING_RETURN`, some ideas:
|
||||
* DONE Assume no instructions will appear after a return statement and analyze only the last `Stmt` using other visitor
|
||||
with different class parameters (for example passing the desired type and returning a `Optional<TypeSymbol>`).
|
||||
* DONE Decide the logic behind a return statement in a `while` loop, maybe treat it as an `if` with an empty `else` block.
|
||||
* A `while` as last instruction is not a valid return.
|
||||
* A `if` as last instruction must have a valid return a the end of its then and otherwise blocks.
|
||||
|
||||
### ExprAnalyzer
|
||||
* DONE Change the second class parameter to give method AND class information (needed by var and this nodes)
|
||||
* DISCARDED Add a ClassSymbol to MethodSymbol and use it as class parameter, the problem arises when visiting a method,
|
||||
should an empty method with the proper class be provided? Could also be solved by using `Symbol` as class parameter.
|
||||
* DONE Create a new class that includes a `ClassSymbol` and a `MethodSymbol` and use that. Keep `MethodSymbol == null`
|
||||
when appropriate.
|
||||
|
||||
## Tests
|
||||
This tests have been selected to increase to 100% the code coverage obtained by running the tests designed for the previous HW,
|
||||
which are in folder svn/HW2/javali_tests/HW2_nop90
|
||||
|
||||
As they are mostly related to errors that haven't been triggered, each test should be separate, in order to generate all
|
||||
of the errors.
|
||||
|
||||
### Remaining
|
||||
### Done
|
||||
* access var from class or superclass
|
||||
* test ! operator
|
||||
* type error in unaryOp
|
||||
* type error in array size
|
||||
* call method from superclass
|
||||
* call a method that doesn't exist
|
||||
* call a method with the wrong type of params
|
||||
* call a method with the wrong number of params
|
||||
* assign to all possible syntactically correct options
|
||||
* field
|
||||
* var
|
||||
* array index
|
||||
* method (error)
|
||||
* this (error)
|
||||
* indexing both types error
|
||||
* try to access a field of something that is not a class type
|
||||
* typing error in cast
|
||||
* AND and OR operations
|
||||
* equals operators (a case that is OK and another that's not)
|
||||
* binary operation with wrong type (any but == and !=)
|
||||
* search for a type that doesn't exist in a method body
|
||||
* don't have a Main class
|
||||
* don't have a main() method in the Main class (all possibilities)
|
||||
* wrong name
|
||||
* wrong return value
|
||||
* wrong parameters
|
||||
* inherited main method (it should work)
|
||||
* write something that is not an integer
|
||||
* declare a variable mutiple times (local variables)
|
||||
* declare a local variable with the same name as a parameter
|
||||
* check MISSING_RETURN
|
||||
* nested ifElse as end of method
|
||||
* while as end of method
|
||||
* normal returns at the end of the method
|
||||
* return with further statements after it
|
||||
* while with non-int condition
|
||||
* use a array type that hasn't been discovered yet
|
||||
* ```class A { B[] name; } class B {}```
|
||||
* name a class Object
|
||||
* name a class like another class
|
||||
* name a parameter like another
|
||||
* override methods and try to break that
|
||||
* same return type
|
||||
* same number of params
|
||||
* same type of params
|
||||
* name a variable like another (field level)
|
107
build.xml
107
build.xml
|
@ -15,31 +15,26 @@
|
|||
<property name="antlr.jar" value="${basedir}/lib/antlr-4.4-complete.jar"/>
|
||||
<property name="antlr.profile" value="false"/>
|
||||
<property name="antlr.report" value="false"/>
|
||||
<property name="jacocoant.jar" value="${basedir}/lib/jacocoant.jar"/>
|
||||
<property name="coverage.file" location="${build.dir}/jacoco.exec"/>
|
||||
<property name="min.coverage" value="0.5"/>
|
||||
<property name="coverage.check" value="cd.frontend.*:cd.backend.*"/>
|
||||
<property name="doc.dir" value="javadoc"/>
|
||||
|
||||
<!-- Cleans generated code, but NOT the parser source! -->
|
||||
<target name="clean">
|
||||
<delete dir="${build.dir}"/>
|
||||
</target>
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="${build.dir}"/>
|
||||
<target name="compile">
|
||||
<mkdir dir="${build.dir}"/>
|
||||
|
||||
<javac debug="true" destdir="${build.dir}" includeantruntime="false">
|
||||
<src path="${src.dir}"/>
|
||||
<src path="${test.dir}"/>
|
||||
<classpath>
|
||||
<pathelement location="${antlr.jar}"/>
|
||||
<pathelement location="${junit.jar}"/>
|
||||
<pathelement location="${hamcrest.jar}"/>
|
||||
<pathelement location="${parser.jar}"/>
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
<javac debug="true" destdir="${build.dir}" includeantruntime="false">
|
||||
<src path="${src.dir}"/>
|
||||
<src path="${test.dir}"/>
|
||||
<classpath>
|
||||
<pathelement location="${antlr.jar}"/>
|
||||
<pathelement location="${junit.jar}"/>
|
||||
<pathelement location="${hamcrest.jar}"/>
|
||||
<pathelement location="${parser.jar}"/>
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- Deletes all byproducts of running the tests -->
|
||||
<target name="clean-test">
|
||||
|
@ -49,70 +44,28 @@
|
|||
<fileset dir="${javali_tests.dir}" includes="**/*.bin"/>
|
||||
<fileset dir="${javali_tests.dir}" includes="**/*.dot"/>
|
||||
<fileset dir="${javali_tests.dir}" includes="**/*.exe"/>
|
||||
<fileset dir="${javali_tests.dir}" includes="**/*.ref"/>
|
||||
<fileset dir="${javali_tests.dir}" includes="**/*.ref"/>
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
|
||||
<classpath path="${jacocoant.jar}"/>
|
||||
</taskdef>
|
||||
|
||||
<!-- Runs the tests. Use the compile target first! -->
|
||||
<target name="test" depends="compile">
|
||||
<jacoco:coverage destfile="${coverage.file}">
|
||||
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<batchtest skipNonTests="true">
|
||||
<fileset dir="bin" includes="**/*.class" />
|
||||
</batchtest>
|
||||
<assertions enablesystemassertions="true" />
|
||||
<sysproperty key="cd.meta_hidden.Version" value="REFERENCE" />
|
||||
<classpath>
|
||||
<pathelement location="${build.dir}"/>
|
||||
<pathelement location="${junit.jar}"/>
|
||||
<pathelement location="${hamcrest.jar}"/>
|
||||
<pathelement location="${antlr.jar}"/>
|
||||
<pathelement location="${parser.jar}"/>
|
||||
</classpath>
|
||||
</junit>
|
||||
</jacoco:coverage>
|
||||
<fail if="tests-failed" />
|
||||
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<batchtest skipNonTests="true">
|
||||
<fileset dir="bin" includes="**/*.class" />
|
||||
</batchtest>
|
||||
<assertions enablesystemassertions="true" />
|
||||
<sysproperty key="cd.meta_hidden.Version" value="REFERENCE" />
|
||||
<classpath>
|
||||
<pathelement location="${build.dir}"/>
|
||||
<pathelement location="${junit.jar}"/>
|
||||
<pathelement location="${hamcrest.jar}"/>
|
||||
<pathelement location="${antlr.jar}"/>
|
||||
<pathelement location="${parser.jar}"/>
|
||||
</classpath>
|
||||
</junit>
|
||||
<fail if="tests-failed" />
|
||||
</target>
|
||||
|
||||
<target name="test-coverage-report">
|
||||
<jacoco:report>
|
||||
<executiondata>
|
||||
<file file="${coverage.file}"/>
|
||||
</executiondata>
|
||||
|
||||
<structure name="JaCoCo Ant Example">
|
||||
<classfiles>
|
||||
<fileset dir="${build.dir}"/>
|
||||
</classfiles>
|
||||
<sourcefiles encoding="UTF-8">
|
||||
<fileset dir="${src.dir}"/>
|
||||
</sourcefiles>
|
||||
</structure>
|
||||
<check>
|
||||
<rule element="PACKAGE" includes="${coverage.check}">
|
||||
<limit counter="LINE" value="COVEREDRATIO" minimum="${min.coverage}"/>
|
||||
</rule>
|
||||
</check>
|
||||
</jacoco:report>
|
||||
<echo message="Coverage above ${min.coverage} in ${coverage.check}: OK" level="info"/>
|
||||
</target>
|
||||
|
||||
<target name="clean-doc">
|
||||
<delete dir="javadoc"/>
|
||||
</target>
|
||||
|
||||
<target name="generate-doc">
|
||||
<javadoc sourcepath="${src.dir}" destdir="${doc.dir}"
|
||||
private="true" use="true" author="true">
|
||||
<package name="cd"/>
|
||||
<package name="cd.frontend.semantic"/>
|
||||
<package name="cd.ir"/>
|
||||
<arg value="-notimestamp"/>
|
||||
</javadoc>
|
||||
</target>
|
||||
</project>
|
||||
|
|
15
javali_tests/HW4/ErrDowncast.javali
Normal file
15
javali_tests/HW4/ErrDowncast.javali
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* Test illegal downcasts */
|
||||
|
||||
class A { }
|
||||
class B extends A { }
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A a;
|
||||
B b;
|
||||
a = new A();
|
||||
b = (B) a; /* Should fail at runtime */
|
||||
write(0);
|
||||
writeln();
|
||||
}
|
||||
}
|
23
javali_tests/HW4/FibonacciRecursive.javali
Normal file
23
javali_tests/HW4/FibonacciRecursive.javali
Normal file
|
@ -0,0 +1,23 @@
|
|||
class Main {
|
||||
void main() {
|
||||
int a;
|
||||
a = fib(20);
|
||||
write(a);
|
||||
writeln();
|
||||
}
|
||||
|
||||
int fib(int n) {
|
||||
|
||||
int fib;
|
||||
int fib2;
|
||||
|
||||
if (n <= 1) {
|
||||
fib = n;
|
||||
} else {
|
||||
fib = fib(n-1);
|
||||
fib2 = fib(n-2);
|
||||
fib = fib + fib2;
|
||||
}
|
||||
return fib;
|
||||
}
|
||||
}
|
14
javali_tests/HW4/OkArrayElementsMath.javali
Normal file
14
javali_tests/HW4/OkArrayElementsMath.javali
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* Test expressions using array elements as operands */
|
||||
class Main {
|
||||
int[] x;
|
||||
void main() {
|
||||
int i;
|
||||
x = new int[3];
|
||||
x[0] = 3;
|
||||
x[1] = 4;
|
||||
x[2] = 5;
|
||||
i = x[0] + x[1] + x[2];
|
||||
write(i);
|
||||
writeln();
|
||||
}
|
||||
}
|
20
javali_tests/HW4/OkCallWithParamField.javali
Normal file
20
javali_tests/HW4/OkCallWithParamField.javali
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* Test access to parameters and fields */
|
||||
class A {
|
||||
int i;
|
||||
void foo(int p) {
|
||||
write(p);
|
||||
write(i);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A a;
|
||||
a = new A();
|
||||
a.i = 10;
|
||||
a.foo(1);
|
||||
a.foo(2);
|
||||
a.foo(3);
|
||||
}
|
||||
}
|
28
javali_tests/HW4/OkDifferentReads.javali
Normal file
28
javali_tests/HW4/OkDifferentReads.javali
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* Test read() with different kinds of LHS values */
|
||||
class Main {
|
||||
int x;
|
||||
|
||||
void main() {
|
||||
int y;
|
||||
int[] arr;
|
||||
|
||||
write(1);
|
||||
writeln();
|
||||
|
||||
y = read();
|
||||
write(y + 1);
|
||||
writeln();
|
||||
|
||||
x = read();
|
||||
write(x + 1);
|
||||
writeln();
|
||||
|
||||
arr = new int[64];
|
||||
arr[x] = read();
|
||||
write(arr[x] + 1);
|
||||
writeln();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
4
javali_tests/HW4/OkDifferentReads.javali.in
Normal file
4
javali_tests/HW4/OkDifferentReads.javali.in
Normal file
|
@ -0,0 +1,4 @@
|
|||
9
|
||||
51
|
||||
12
|
||||
|
14
javali_tests/HW4/OkDowncast.javali
Normal file
14
javali_tests/HW4/OkDowncast.javali
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* Test legal downcasts */
|
||||
|
||||
class A { }
|
||||
class B extends A { }
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A a;
|
||||
B b;
|
||||
a = new B();
|
||||
b = (B) a; /* OK at runtime */
|
||||
write(0);
|
||||
}
|
||||
}
|
29
javali_tests/HW4/OkFieldInArray.javali
Normal file
29
javali_tests/HW4/OkFieldInArray.javali
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* Test access to fields from elements of arrays */
|
||||
class A {
|
||||
int field;
|
||||
void foo() {
|
||||
write(1);
|
||||
write(field);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
|
||||
class Main {
|
||||
A[] x;
|
||||
void main() {
|
||||
int i;
|
||||
x = new A[2];
|
||||
i = 1;
|
||||
write(i);
|
||||
writeln();
|
||||
|
||||
x[i] = new A();
|
||||
x[i].field = i + 1;
|
||||
|
||||
i = x[1].field;
|
||||
write(i);
|
||||
writeln();
|
||||
|
||||
x[1].foo();
|
||||
}
|
||||
}
|
50
javali_tests/HW4/OkVirtualMethod.javali
Normal file
50
javali_tests/HW4/OkVirtualMethod.javali
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* Test virtual method calls */
|
||||
class A {
|
||||
void override() {
|
||||
write(0);
|
||||
writeln();
|
||||
}
|
||||
void base() {
|
||||
write(1);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
void override() {
|
||||
write(2);
|
||||
writeln();
|
||||
}
|
||||
void sub() {
|
||||
write(3);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A a;
|
||||
B b;
|
||||
|
||||
a = null;
|
||||
b = null;
|
||||
|
||||
a = new A();
|
||||
a.base();
|
||||
a.override();
|
||||
|
||||
b = new B();
|
||||
b.base();
|
||||
b.override();
|
||||
b.sub();
|
||||
|
||||
a = b;
|
||||
a.base();
|
||||
a.override();
|
||||
|
||||
b.base();
|
||||
b.override();
|
||||
b.sub();
|
||||
|
||||
}
|
||||
}
|
81
javali_tests/HW4/Quicksort.javali
Normal file
81
javali_tests/HW4/Quicksort.javali
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* Overall test of arrays, loops, etc. that does a simple quicksort */
|
||||
|
||||
class Record {
|
||||
int a;
|
||||
void print() {
|
||||
write(a);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
|
||||
class Main {
|
||||
Record [] a;
|
||||
int i;
|
||||
|
||||
void swap(Record r1, Record r2) {
|
||||
int temp;
|
||||
|
||||
temp = r1.a;
|
||||
r1.a = r2.a;
|
||||
r2.a = temp;
|
||||
}
|
||||
|
||||
void sort(int left, int right) {
|
||||
int i,j;
|
||||
int m;
|
||||
|
||||
m = (a[left].a + a[right].a) / 2;
|
||||
i = left;
|
||||
j = right;
|
||||
while (i <= j) {
|
||||
while (a[i].a < m) { i = i+1; }
|
||||
while (a[j].a > m) { j = j-1; }
|
||||
if (i <= j) {
|
||||
swap(a[i], a[j]);
|
||||
i = i + 1;
|
||||
j = j - 1;
|
||||
}
|
||||
}
|
||||
if (left < j) { sort(left, j); }
|
||||
if (i < right) { sort(i, right); }
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
int SIZE;
|
||||
int j;
|
||||
|
||||
SIZE = 5;
|
||||
|
||||
a = new Record[SIZE];
|
||||
j = 0;
|
||||
while (j < SIZE) {
|
||||
a[j] = new Record();
|
||||
j = j + 1;
|
||||
}
|
||||
a[0].a = 5;
|
||||
a[1].a = 3;
|
||||
a[2].a = 1;
|
||||
a[3].a = 4;
|
||||
a[4].a = 2;
|
||||
|
||||
/* Numbers before sorting */
|
||||
j = 0;
|
||||
while (j < SIZE) {
|
||||
a[j].print();
|
||||
j = j + 1;
|
||||
}
|
||||
writeln();
|
||||
|
||||
sort(0, 4);
|
||||
|
||||
/* Numbers after sorting */
|
||||
j = 0;
|
||||
while (j < SIZE) {
|
||||
a[j].print();
|
||||
j = j + 1;
|
||||
}
|
||||
writeln();
|
||||
|
||||
}
|
||||
}
|
12
javali_tests/HW4_nop90/Array/ErrArrayIndex.javali
Normal file
12
javali_tests/HW4_nop90/Array/ErrArrayIndex.javali
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* Test accessing arrays with invalid index*/
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
x = new int[5];
|
||||
x[0] = 3;
|
||||
x[1] = 4;
|
||||
x[2] = 5;
|
||||
|
||||
x[5] = 5; //this should fail
|
||||
}
|
||||
}
|
9
javali_tests/HW4_nop90/Array/ErrArrayIndex2.javali
Normal file
9
javali_tests/HW4_nop90/Array/ErrArrayIndex2.javali
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* Test accessing arrays with invalid index*/
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
x = new int[5];
|
||||
|
||||
x[8] = 5; //this should fail
|
||||
}
|
||||
}
|
9
javali_tests/HW4_nop90/Array/ErrArrayIndex3.javali
Normal file
9
javali_tests/HW4_nop90/Array/ErrArrayIndex3.javali
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* Test accessing arrays with invalid index*/
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
x = new int[5];
|
||||
|
||||
x[-1] = 5; //this should fail
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/* Test access an array on a null pointer */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
x = null;
|
||||
|
||||
x[1] = 5; //this should throw null pointer exception
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/* Test access an array on a null pointer */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
|
||||
x[1] = 5; //this should throw null pointer exception
|
||||
}
|
||||
}
|
10
javali_tests/HW4_nop90/Array/ErrArraySize.javali
Normal file
10
javali_tests/HW4_nop90/Array/ErrArraySize.javali
Normal file
|
@ -0,0 +1,10 @@
|
|||
/* Test creating an array with negative length */
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
x = new int[-3];
|
||||
x[0] = 3;
|
||||
x[1] = 4;
|
||||
x[2] = 5;
|
||||
}
|
||||
}
|
16
javali_tests/HW4_nop90/Array/OkArrayAccess.javali
Normal file
16
javali_tests/HW4_nop90/Array/OkArrayAccess.javali
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* Test accessing arrays */
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
int i;
|
||||
x = new int[3];
|
||||
x[0] = 3;
|
||||
x[1] = 4;
|
||||
x[2] = 5;
|
||||
i = x[0] + x[1] + x[2];
|
||||
x[2]=55;
|
||||
write(i);
|
||||
writeln();
|
||||
}
|
||||
}
|
||||
|
15
javali_tests/HW4_nop90/Array/OkArrayAccess2.javali
Normal file
15
javali_tests/HW4_nop90/Array/OkArrayAccess2.javali
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* Test creating arrays of objects and accessing a null element*/
|
||||
|
||||
class A{}
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A[] x;
|
||||
A a;
|
||||
x = new A[3];
|
||||
x[1] = new A();
|
||||
x[2] = a;
|
||||
|
||||
x[2] = new A();
|
||||
}
|
||||
}
|
8
javali_tests/HW4_nop90/Array/OkArraySizeIsZero.javali
Normal file
8
javali_tests/HW4_nop90/Array/OkArraySizeIsZero.javali
Normal file
|
@ -0,0 +1,8 @@
|
|||
/* Test array size=0 */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
x = new int[0];
|
||||
}
|
||||
}
|
12
javali_tests/HW4_nop90/Array/OkFieldArray.javali
Normal file
12
javali_tests/HW4_nop90/Array/OkFieldArray.javali
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* Test Arrays as Fields */
|
||||
|
||||
class Main {
|
||||
int[] x;
|
||||
void main() {
|
||||
int i;
|
||||
x = new int[3];
|
||||
x[0] = 3;
|
||||
x[1] = 4;
|
||||
x[2] = 5;
|
||||
}
|
||||
}
|
19
javali_tests/HW4_nop90/Array/OkInheritedArray.javali
Normal file
19
javali_tests/HW4_nop90/Array/OkInheritedArray.javali
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* Test inherited Array */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C1 c1;
|
||||
C2 c2;
|
||||
c1 = new C1();
|
||||
c2 = new C2();
|
||||
c1.x = new int[3];
|
||||
c2.x = new int[4];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class C1{
|
||||
int[] x;
|
||||
}
|
||||
|
||||
class C2 extends C1 {}
|
11
javali_tests/HW4_nop90/Array/OkObjectArray.javali
Normal file
11
javali_tests/HW4_nop90/Array/OkObjectArray.javali
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* Test creating arrays of objects */
|
||||
|
||||
class A{}
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A[] x;
|
||||
x = new A[2];
|
||||
x[0] = new A();
|
||||
}
|
||||
}
|
14
javali_tests/HW4_nop90/Assignments/OkArraySubtypes.javali
Normal file
14
javali_tests/HW4_nop90/Assignments/OkArraySubtypes.javali
Normal file
|
@ -0,0 +1,14 @@
|
|||
// any array is a subtype of Object
|
||||
// assignment and 'is equal' with an object of type 'Object' is fine
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
boolean y;
|
||||
Object x;
|
||||
int[] testArray;
|
||||
testArray = new int[10];
|
||||
x = testArray;
|
||||
testArray = null;
|
||||
x = null;
|
||||
}
|
||||
}
|
21
javali_tests/HW4_nop90/Assignments/OkSubtypes.javali
Normal file
21
javali_tests/HW4_nop90/Assignments/OkSubtypes.javali
Normal file
|
@ -0,0 +1,21 @@
|
|||
// assignment of subtypes
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A a1, a2;
|
||||
B b1, b2;
|
||||
C c;
|
||||
a1 = new A();
|
||||
b2 = new B();
|
||||
c = new C();
|
||||
a2 = a1;
|
||||
b1 = c;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class A {}
|
||||
|
||||
class B extends A {}
|
||||
|
||||
class C extends B {}
|
8
javali_tests/HW4_nop90/Booleans/OkBooleanAndOr.javali
Normal file
8
javali_tests/HW4_nop90/Booleans/OkBooleanAndOr.javali
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Test boolean && and || operators
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
boolean a, b, c;
|
||||
a = b && c || a;
|
||||
}
|
||||
}
|
31
javali_tests/HW4_nop90/Booleans/OkEquals.javali
Normal file
31
javali_tests/HW4_nop90/Booleans/OkEquals.javali
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Test the equal/not equal operations, one of the types must be a subtype of the other
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C1 c1;
|
||||
C2 c2;
|
||||
C3 c3;
|
||||
Object o1, o2;
|
||||
boolean s;
|
||||
|
||||
o1 = new Object();
|
||||
o2 = new Object();
|
||||
c1 = new C1();
|
||||
c2 = new C2();
|
||||
c3 = new C3();
|
||||
|
||||
s = o1 == o2;
|
||||
s = c1 != c2;
|
||||
s = c3 == c1;
|
||||
s = o2 != o1;
|
||||
s = null == c2;
|
||||
s = o1 == c2;
|
||||
//s = null == o;
|
||||
}
|
||||
}
|
||||
|
||||
class C1 {}
|
||||
|
||||
class C2 extends C1{}
|
||||
|
||||
class C3 extends C2{}
|
15
javali_tests/HW4_nop90/Booleans/OkEquals2.javali
Executable file
15
javali_tests/HW4_nop90/Booleans/OkEquals2.javali
Executable file
|
@ -0,0 +1,15 @@
|
|||
// Test the equal/not equal operations, one of the types must be a subtype of the other
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a,b;
|
||||
boolean c;
|
||||
a = 8;
|
||||
b = 6;
|
||||
c = a==b;
|
||||
|
||||
if (!c){
|
||||
write(5);}
|
||||
}
|
||||
}
|
||||
|
14
javali_tests/HW4_nop90/Booleans/OkEquals3.javali
Executable file
14
javali_tests/HW4_nop90/Booleans/OkEquals3.javali
Executable file
|
@ -0,0 +1,14 @@
|
|||
// Test the equal/not equal operations, one of the types must be a subtype of the other
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
boolean a,b,c;
|
||||
a = true;
|
||||
b = 6<10;
|
||||
c = a==b;
|
||||
|
||||
if (c){
|
||||
write(5);}
|
||||
}
|
||||
}
|
||||
|
36
javali_tests/HW4_nop90/Booleans/OkNot.javali
Normal file
36
javali_tests/HW4_nop90/Booleans/OkNot.javali
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Test '!' operator
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
boolean a,b,c,d;
|
||||
int x,y;
|
||||
|
||||
a = true;
|
||||
c = !false;
|
||||
b = !a;
|
||||
x = 100;
|
||||
y = 5;
|
||||
|
||||
if (a) {
|
||||
write(1);}
|
||||
writeln();
|
||||
|
||||
if (!b){
|
||||
write(2);
|
||||
}
|
||||
writeln();
|
||||
|
||||
while(c){
|
||||
write(3);
|
||||
c = false;
|
||||
}
|
||||
writeln();
|
||||
|
||||
// !x < 2 * y --> !x type error
|
||||
// !(x < 2 * y) --> correct syntax
|
||||
while(!(x<2*y)){
|
||||
write(y);
|
||||
y = y*2;
|
||||
}
|
||||
}
|
||||
}
|
21
javali_tests/HW4_nop90/Casts/ErrCastUnrelatedType.javali
Executable file
21
javali_tests/HW4_nop90/Casts/ErrCastUnrelatedType.javali
Executable file
|
@ -0,0 +1,21 @@
|
|||
// Test types in a Cast (cannot cast to unrelated type)
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C1 a, b;
|
||||
C2 c, d;
|
||||
Object o;
|
||||
c = new C2();
|
||||
a = new C1();
|
||||
|
||||
b = (C1) c;
|
||||
d = (C2) a;
|
||||
o = (Object) a;
|
||||
o = (Object) c;
|
||||
o = (Main) c;
|
||||
}
|
||||
}
|
||||
|
||||
class C1 {}
|
||||
|
||||
class C2 extends C1{}
|
14
javali_tests/HW4_nop90/Casts/ErrObjectToArrayCast.javali
Executable file
14
javali_tests/HW4_nop90/Casts/ErrObjectToArrayCast.javali
Executable file
|
@ -0,0 +1,14 @@
|
|||
// Test downcast from Object to array
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A[] a;
|
||||
Object o;
|
||||
|
||||
o = new Object();
|
||||
|
||||
a = (A[]) o;
|
||||
}
|
||||
}
|
||||
|
||||
class A{}
|
12
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast.javali
Executable file
12
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast.javali
Executable file
|
@ -0,0 +1,12 @@
|
|||
// Test types in a Cast
|
||||
// cannot cast an int to an boolean
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a;
|
||||
boolean b;
|
||||
a = 1;
|
||||
|
||||
b = (boolean) a;
|
||||
}
|
||||
}
|
11
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast2.javali
Executable file
11
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast2.javali
Executable file
|
@ -0,0 +1,11 @@
|
|||
// Test types in a Cast (cannot cast to unrelated type)
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a;
|
||||
boolean b;
|
||||
//b = false;
|
||||
|
||||
a = (int) b;
|
||||
}
|
||||
}
|
12
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast.javali
Executable file
12
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast.javali
Executable file
|
@ -0,0 +1,12 @@
|
|||
// Test downcast from array to Object
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int[] x;
|
||||
Object o;
|
||||
|
||||
x = new int[5];
|
||||
|
||||
o = (Object) x;
|
||||
}
|
||||
}
|
14
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast2.javali
Executable file
14
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast2.javali
Executable file
|
@ -0,0 +1,14 @@
|
|||
// Test downcast from array to Object
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
A[] a;
|
||||
Object o;
|
||||
|
||||
a = new A[5];
|
||||
|
||||
o = (Object) a;
|
||||
}
|
||||
}
|
||||
|
||||
class A{}
|
15
javali_tests/HW4_nop90/Casts/OkSubtype.javali
Executable file
15
javali_tests/HW4_nop90/Casts/OkSubtype.javali
Executable file
|
@ -0,0 +1,15 @@
|
|||
// Test cast from a type to the same type
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C c,d;
|
||||
Object o,g;
|
||||
c = new C();
|
||||
g = new Object();
|
||||
|
||||
d = (C) c;
|
||||
o = (Object) g;
|
||||
}
|
||||
}
|
||||
|
||||
class C {}
|
26
javali_tests/HW4_nop90/Casts/OkTypeCast.javali
Executable file
26
javali_tests/HW4_nop90/Casts/OkTypeCast.javali
Executable file
|
@ -0,0 +1,26 @@
|
|||
// Test valid casts
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C1 a, x;
|
||||
C2 c,d;
|
||||
C4 b,f;
|
||||
Object o;
|
||||
c = new C2();
|
||||
a = new C1();
|
||||
b = new C4();
|
||||
f = new C4();
|
||||
|
||||
x = (C1) b;
|
||||
o = (Object) f;
|
||||
}
|
||||
}
|
||||
|
||||
class C1 {}
|
||||
|
||||
class C2 extends C1{}
|
||||
|
||||
class C3 extends C2{}
|
||||
|
||||
class C4 extends C3{}
|
||||
|
23
javali_tests/HW4_nop90/Casts/OkTypeToObjectCast.javali
Executable file
23
javali_tests/HW4_nop90/Casts/OkTypeToObjectCast.javali
Executable file
|
@ -0,0 +1,23 @@
|
|||
// Test casts from type to object
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C1 a;
|
||||
Object o,f,g;
|
||||
int x;
|
||||
boolean y;
|
||||
a = new C1();
|
||||
x = 2;
|
||||
y = true;
|
||||
|
||||
|
||||
o = (Object) a;
|
||||
//f = (Object) x;
|
||||
//g = (Object) y;
|
||||
}
|
||||
}
|
||||
|
||||
class C1 {}
|
||||
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ class Main {
|
|||
i6 = 6;
|
||||
i7 = 7;
|
||||
|
||||
write(i0 + (i1 + ( i2 + ( i3 + ( i4 + (i5 + (i6 + i7))))))); writeln();
|
||||
write(i0 + (i1 + ( i2 + ( i3 + ( i4 + (i5 + (i6 + (i7 + i0)))))))); writeln();
|
||||
write(((((((i0 + i1) + i2) + i3) + i4) + i5) + i6) + i7); writeln();
|
||||
write(((i0 + i1) + (i2 + i3)) + ((i4 + i5) + (i6 + i7))); writeln();
|
||||
}
|
10
javali_tests/HW4_nop90/ErrDivisionByZero.javali
Executable file
10
javali_tests/HW4_nop90/ErrDivisionByZero.javali
Executable file
|
@ -0,0 +1,10 @@
|
|||
// test division by zero
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a;
|
||||
int b;
|
||||
a = 10;
|
||||
b = a / 0;
|
||||
}
|
||||
}
|
27
javali_tests/HW4_nop90/Fields/OkInheritedFieldAccess.javali
Normal file
27
javali_tests/HW4_nop90/Fields/OkInheritedFieldAccess.javali
Normal file
|
@ -0,0 +1,27 @@
|
|||
// access a field from a class and a superclass (also includes hidden fields)
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C1 c1;
|
||||
C2 c2;
|
||||
C4 c4;
|
||||
c1 = new C1();
|
||||
c2 = new C2();
|
||||
c4 = new C4();
|
||||
c1.a = 5;
|
||||
c2.a = 6;
|
||||
c4.a = false;
|
||||
}
|
||||
}
|
||||
|
||||
class C1{
|
||||
int a;
|
||||
}
|
||||
|
||||
class C2 extends C1 {}
|
||||
|
||||
class C3 extends C2 {}
|
||||
|
||||
class C4 extends C3 {
|
||||
boolean a;
|
||||
}
|
19
javali_tests/HW4_nop90/Fields/OkObjectFields.javali
Executable file
19
javali_tests/HW4_nop90/Fields/OkObjectFields.javali
Executable file
|
@ -0,0 +1,19 @@
|
|||
// test an Array of Objects and access their field
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
D[] x;
|
||||
x = new D[50];
|
||||
x[22].c.a = 2;
|
||||
}
|
||||
}
|
||||
|
||||
class C{
|
||||
int a;
|
||||
}
|
||||
|
||||
|
||||
class D {
|
||||
C c;
|
||||
}
|
||||
|
5
javali_tests/HW4_nop90/ManyRegisters.javali
Normal file
5
javali_tests/HW4_nop90/ManyRegisters.javali
Normal file
|
@ -0,0 +1,5 @@
|
|||
class Main {
|
||||
void main() {
|
||||
write(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15);
|
||||
}
|
||||
}
|
30
javali_tests/HW4_nop90/OkAccessField.javali
Normal file
30
javali_tests/HW4_nop90/OkAccessField.javali
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Access inherited and hidden fields (general check)
|
||||
|
||||
class Other {
|
||||
int a;
|
||||
int b;
|
||||
Object c;
|
||||
Other o;
|
||||
}
|
||||
|
||||
class Main extends Other {
|
||||
int a, b;
|
||||
|
||||
void main() {
|
||||
Other o;
|
||||
o = (Other) this;
|
||||
a = 1;
|
||||
b = 2;
|
||||
o.a = -1;
|
||||
o.b = -2;
|
||||
write(a);
|
||||
write(b);
|
||||
write(o.a);
|
||||
write(o.b);
|
||||
if (c != null) {
|
||||
if (o.o != null) {
|
||||
write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
javali_tests/HW4_nop90/OkAssignments.javali
Normal file
34
javali_tests/HW4_nop90/OkAssignments.javali
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* testing assign statements*/
|
||||
class Main {
|
||||
int methodCall() { return 0; }
|
||||
int methodCall2(int a, int b) {
|
||||
if (a >= b) {
|
||||
return 0;
|
||||
} else {
|
||||
if (b <= a) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return a * b;
|
||||
}
|
||||
|
||||
void main() {
|
||||
int a, b, c, e;
|
||||
Ast d;
|
||||
int[] g;
|
||||
Object f;
|
||||
a = read();
|
||||
b = methodCall();
|
||||
c = methodCall2(a, b);
|
||||
d = new Ast();
|
||||
e = d.field;
|
||||
g = new int[a];
|
||||
f = new Object[a];
|
||||
f = g;
|
||||
g = (int[]) f;
|
||||
}
|
||||
}
|
||||
|
||||
class Ast {
|
||||
int field;
|
||||
}
|
1
javali_tests/HW4_nop90/OkAssignments.javali.in
Normal file
1
javali_tests/HW4_nop90/OkAssignments.javali.in
Normal file
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -8,11 +8,11 @@ class Main {
|
|||
d = 40;
|
||||
e = 1;
|
||||
|
||||
while ( b > c ) {
|
||||
while ( b >= c ) {
|
||||
write(b);
|
||||
b = b-1;
|
||||
}
|
||||
a = c < (b+4-5/2);
|
||||
a = c <= (b+4-5/2);
|
||||
if (a){
|
||||
write(13);}
|
||||
if (100/2>d*e){
|
17
javali_tests/HW4_nop90/OkCallInheritedMethod.javali
Normal file
17
javali_tests/HW4_nop90/OkCallInheritedMethod.javali
Normal file
|
@ -0,0 +1,17 @@
|
|||
// call an method from a superclass
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C2 c;
|
||||
c = new C2();
|
||||
c.aux();
|
||||
}
|
||||
}
|
||||
|
||||
class C1{
|
||||
void aux(){
|
||||
write(2);
|
||||
}
|
||||
}
|
||||
|
||||
class C2 extends C1 {}
|
10
javali_tests/HW4_nop90/OkEmptyBlocks.javali
Normal file
10
javali_tests/HW4_nop90/OkEmptyBlocks.javali
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Main {
|
||||
void main() {
|
||||
while(false) {}
|
||||
if(false) {
|
||||
return;
|
||||
}
|
||||
if (false) {} else {}
|
||||
if (false) {} else {return;}
|
||||
}
|
||||
}
|
5
javali_tests/HW4_nop90/OkEmptyWhile.javali
Normal file
5
javali_tests/HW4_nop90/OkEmptyWhile.javali
Normal file
|
@ -0,0 +1,5 @@
|
|||
class Main {
|
||||
void main() {
|
||||
while(false) {}
|
||||
}
|
||||
}
|
16
javali_tests/HW4_nop90/OkHideField.javali
Normal file
16
javali_tests/HW4_nop90/OkHideField.javali
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Hide a field from a superclass (type doesn't need to match)
|
||||
|
||||
class Parent {
|
||||
int a;
|
||||
}
|
||||
class Main extends Parent {
|
||||
Object a;
|
||||
Main b;
|
||||
void main() {
|
||||
Parent c;
|
||||
b = new Main();
|
||||
b.a = new Object();
|
||||
c = new Parent();
|
||||
c.a = 10;
|
||||
}
|
||||
}
|
|
@ -7,14 +7,10 @@ class Main {
|
|||
int a;
|
||||
boolean b;
|
||||
Object c;
|
||||
|
||||
a = null;
|
||||
b = null;
|
||||
|
||||
c = null;
|
||||
|
||||
write(a);
|
||||
writeln();
|
||||
//write(null)
|
||||
|
||||
}
|
||||
}
|
14
javali_tests/HW4_nop90/OkOverwriteMainMethod.javali
Normal file
14
javali_tests/HW4_nop90/OkOverwriteMainMethod.javali
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* test overwritten main method */
|
||||
|
||||
|
||||
class OtherMain {
|
||||
void main () {
|
||||
int a;
|
||||
}
|
||||
}
|
||||
|
||||
class Main extends OtherMain {
|
||||
void main () {
|
||||
int b;
|
||||
}
|
||||
}
|
32
javali_tests/HW4_nop90/OkRegisterUse.javali
Normal file
32
javali_tests/HW4_nop90/OkRegisterUse.javali
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* this test forces the compiler to push registers to the stack */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int u,v,x,y,z;
|
||||
boolean e,f,g,h,i,j;
|
||||
B b1,b2;
|
||||
e = true;
|
||||
f = true;
|
||||
g = true;
|
||||
z = 0;
|
||||
while (z < 30) {
|
||||
if (f){
|
||||
if (g){
|
||||
u = 5;
|
||||
v = 8;
|
||||
x = u/v;
|
||||
b1 = new B();
|
||||
e = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
j = true;
|
||||
}
|
||||
z = z + 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class B {}
|
29
javali_tests/HW4_nop90/OkVariables.javali
Executable file
29
javali_tests/HW4_nop90/OkVariables.javali
Executable file
|
@ -0,0 +1,29 @@
|
|||
/* Test expression evaluation with more than 8 variables
|
||||
Test Register use
|
||||
*/
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int r1, r2, r3;
|
||||
int i0, i1, i2, i3, i4, i5, i6, i7;
|
||||
int a,b,c,d,e;
|
||||
|
||||
i0 = 0;
|
||||
i1 = 1;
|
||||
i2 = 2;
|
||||
i3 = 3;
|
||||
i4 = 4;
|
||||
i5 = 5;
|
||||
i6 = 6;
|
||||
i7 = 7;
|
||||
a = 5;
|
||||
b = 5;
|
||||
c = 5;
|
||||
d = 5;
|
||||
e = 5;
|
||||
|
||||
write(i0 + (i1 + ( i2 + ( i3 + (a+b+c+d+e) + ( i4 + (i5 + (i6 + (i7 + i0)))))))); writeln();
|
||||
write(((((((i0 + i1) + i2) + i3) + i4 + (a*b*c*d*e)) + i5) + i6) + i7); writeln();
|
||||
write(((i0 + i1) + (i2 + i3)) + ((i4 + i5) + (i6 + i7))); writeln();
|
||||
}
|
||||
}
|
9
javali_tests/HW4_nop90/OkZeroInitialized.javali
Executable file
9
javali_tests/HW4_nop90/OkZeroInitialized.javali
Executable file
|
@ -0,0 +1,9 @@
|
|||
/* Test that variables are zero initialized */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a;
|
||||
write(a);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// A if/else block is valid return statment if all possible branches return some value
|
||||
// Also serves to check subtyping
|
||||
// The return logic leads to nonsensical programs with statements after the return, but that's ok
|
||||
|
||||
class Main {
|
||||
// All returns must be empty, but we don't care for their existence
|
||||
void main() {
|
||||
int a, b, c;
|
||||
if (a == b) {
|
||||
return;
|
||||
} else {
|
||||
while (a > b) {
|
||||
return;
|
||||
}
|
||||
if (c > b) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All returns must be null, Main (all subtypes of Main), and there must be an unconditional return
|
||||
Main action1() {
|
||||
Main a;
|
||||
Object b;
|
||||
boolean x, y;
|
||||
if (x) {
|
||||
if (y) {
|
||||
if (x || y) {
|
||||
if (x == y && x != y) {
|
||||
return null;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
} else {
|
||||
if (b == null) {
|
||||
return a;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (b == a || a == null) {
|
||||
return null;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// All returns must be null, Main[] (all subtypes of Main[]), and there must be an unconditional return
|
||||
Main[] action2() {
|
||||
Main[] a, b;
|
||||
if (a == b) {
|
||||
return a;
|
||||
} else {
|
||||
if (b != null) {
|
||||
return b;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All returns must be int (no subtypes), and there must be an unconditional return
|
||||
int action3() {
|
||||
int a, b, c, d;
|
||||
if (a > b) {
|
||||
return c - d;
|
||||
while (a != a) {
|
||||
return b;
|
||||
}
|
||||
} else {
|
||||
return c * d / a + b % c - a;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
class Main {
|
||||
int n() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
n();
|
||||
}
|
||||
}
|
21
javali_tests/HW4_nop90/ReturnTests/OkMiddleReturn.javali
Normal file
21
javali_tests/HW4_nop90/ReturnTests/OkMiddleReturn.javali
Normal file
|
@ -0,0 +1,21 @@
|
|||
// The return statement can be placed in the middle of the method, and no warning will be thrown about unexecuted
|
||||
// statements
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
write(things());
|
||||
writeln();
|
||||
}
|
||||
|
||||
int things() {
|
||||
int a;
|
||||
a = 10;
|
||||
return a;
|
||||
|
||||
// These statements won't be reached
|
||||
// but there is a complete return statement
|
||||
a = 20;
|
||||
writeln();
|
||||
writeln();
|
||||
}
|
||||
}
|
22
javali_tests/HW4_nop90/ReturnTests/OkReturnObject.javali
Executable file
22
javali_tests/HW4_nop90/ReturnTests/OkReturnObject.javali
Executable file
|
@ -0,0 +1,22 @@
|
|||
/* test method that returns an object */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int x;
|
||||
B b;
|
||||
b = aux();
|
||||
x = b.a;
|
||||
write(x);
|
||||
}
|
||||
|
||||
B aux(){
|
||||
B b;
|
||||
b = new B();
|
||||
b.a = 10;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
int a;
|
||||
}
|
24
javali_tests/HW4_nop90/ReturnTests/OkSimpleIfElse.javali
Normal file
24
javali_tests/HW4_nop90/ReturnTests/OkSimpleIfElse.javali
Normal file
|
@ -0,0 +1,24 @@
|
|||
// A if/else with a return in both branches is valid
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
write(things());
|
||||
writeln();
|
||||
}
|
||||
|
||||
int things() {
|
||||
int a;
|
||||
int b;
|
||||
Object c, d;
|
||||
a = 10;
|
||||
b = 100;
|
||||
c = new Object();
|
||||
d = new Object();
|
||||
|
||||
if (c == d) {
|
||||
return 10;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
22
javali_tests/HW4_nop90/method invocation/OkCallByValue.javali
Executable file
22
javali_tests/HW4_nop90/method invocation/OkCallByValue.javali
Executable file
|
@ -0,0 +1,22 @@
|
|||
// test that call-by-value is used
|
||||
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int x;
|
||||
A a;
|
||||
a = new A();
|
||||
a.a = 50;
|
||||
aux(a.a);
|
||||
x = a.a;
|
||||
write(x);
|
||||
}
|
||||
|
||||
void aux (int arg){
|
||||
arg = arg + 1;
|
||||
}
|
||||
}
|
||||
|
||||
class A {
|
||||
int a;
|
||||
}
|
18
javali_tests/HW4_nop90/method invocation/OkCallByValue2.javali
Executable file
18
javali_tests/HW4_nop90/method invocation/OkCallByValue2.javali
Executable file
|
@ -0,0 +1,18 @@
|
|||
// test that call-by-value is used
|
||||
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int[] y;
|
||||
int x;
|
||||
y = new int[5];
|
||||
y[2] = 50;
|
||||
aux(y[2]);
|
||||
x = y[2];
|
||||
write(x);
|
||||
}
|
||||
|
||||
void aux (int arg){
|
||||
arg = arg + 1;
|
||||
}
|
||||
}
|
18
javali_tests/HW4_nop90/method invocation/OkCallByValue3.javali
Executable file
18
javali_tests/HW4_nop90/method invocation/OkCallByValue3.javali
Executable file
|
@ -0,0 +1,18 @@
|
|||
// test that call-by-value is used
|
||||
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int[] y;
|
||||
int x;
|
||||
y = new int[5];
|
||||
y[2] = 50;
|
||||
aux(y);
|
||||
x = y[2];
|
||||
write(x);
|
||||
}
|
||||
|
||||
void aux (int[] arg){
|
||||
arg[2] = 100;
|
||||
}
|
||||
}
|
17
javali_tests/HW4_nop90/method invocation/OkCallInheritedMethod.javali
Executable file
17
javali_tests/HW4_nop90/method invocation/OkCallInheritedMethod.javali
Executable file
|
@ -0,0 +1,17 @@
|
|||
// call an method from a superclass
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
C2 c;
|
||||
c = new C2();
|
||||
c.aux();
|
||||
}
|
||||
}
|
||||
|
||||
class C1{
|
||||
void aux(){
|
||||
write(2);
|
||||
}
|
||||
}
|
||||
|
||||
class C2 extends C1 {}
|
19
javali_tests/HW4_nop90/method invocation/OkMethod.javali
Executable file
19
javali_tests/HW4_nop90/method invocation/OkMethod.javali
Executable file
|
@ -0,0 +1,19 @@
|
|||
// call a simple method and use its return value
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a,b;
|
||||
C c;
|
||||
c = new C();
|
||||
a = 5;
|
||||
b = c.aux(a);
|
||||
}
|
||||
}
|
||||
|
||||
class C{
|
||||
int aux(int arg){
|
||||
return arg*2;
|
||||
}
|
||||
}
|
||||
|
||||
|
35
javali_tests/HW4_nop90/method invocation/OkMethod2.javali
Executable file
35
javali_tests/HW4_nop90/method invocation/OkMethod2.javali
Executable file
|
@ -0,0 +1,35 @@
|
|||
// call a method with many parameters and use its return value
|
||||
// also Test Register use, by allocating memory
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int a,b,c,d,e,f,g,h,x;
|
||||
a = 5;
|
||||
b = 5;
|
||||
c = 5;
|
||||
d = 5;
|
||||
e = 5;
|
||||
f = 5;
|
||||
g = 5;
|
||||
h = 5;
|
||||
|
||||
x = aux(a,b,c,d,e,f,g,h);
|
||||
|
||||
}
|
||||
|
||||
int aux(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8){
|
||||
int[] x;
|
||||
int i;
|
||||
D d;
|
||||
x = new int[20];
|
||||
i = arg1 + arg2 + arg3 + arg4 - arg5 - arg6 + arg7 - arg8;
|
||||
d = new D();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
class D{
|
||||
int[] x;
|
||||
}
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/* testing arrays with primitive types as well as new objects
|
||||
the if/else statements shows that the array is boolean array is initialized with false
|
||||
*/
|
||||
class Main {
|
||||
void main() {
|
||||
int [] testArray;
|
||||
boolean [] boolarray;
|
||||
|
||||
boolarray = new boolean [3];
|
||||
testArray = new int [10];
|
||||
|
||||
testArray[5] = 3;
|
||||
boolarray[1] = true;
|
||||
|
||||
if (boolarray[0]){
|
||||
write(1);}
|
||||
else{
|
||||
write(5);}
|
||||
writeln();
|
||||
if (boolarray[1]){
|
||||
write(1);}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* testing assign statements*/
|
||||
class Main {
|
||||
void main() {
|
||||
a = read();
|
||||
b = methodCall();
|
||||
c = methodCall(param1, param2);
|
||||
d = object.access;
|
||||
e = new Ast();
|
||||
d = new int[size];
|
||||
f = new Object[size];
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
class Main {
|
||||
void main() {
|
||||
int a, b, c, d, e;
|
||||
|
||||
a = 10;
|
||||
b = -1;
|
||||
c = 0;
|
||||
d = 100;
|
||||
e = 2;
|
||||
|
||||
write(a / b); writeln();
|
||||
write(d / e); writeln();
|
||||
write(c / d); writeln();
|
||||
write(b / a + c); writeln();
|
||||
write(d / e * a / b * c); writeln();
|
||||
write(d / e * a / b); writeln();
|
||||
write(d / e + a / b); writeln();
|
||||
write(d / e * a * b * c); writeln();
|
||||
write(d / e * a - b + c); writeln();
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
/* testing basic inheritance*/
|
||||
class C1 {}
|
||||
class C2 extends C1 {}
|
||||
class C3 extends C2 {}
|
||||
class C4 extends C2 {}
|
||||
class C5 extends C3 {}
|
||||
class Main {
|
||||
void main() {}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/* testing different expressions
|
||||
compiler should recognize Type error: Return statement of method with void return type should not have arguments
|
||||
*/
|
||||
class Main {
|
||||
void main() {
|
||||
return;
|
||||
return true;
|
||||
return false;
|
||||
return 0x10;
|
||||
return 10;
|
||||
return variable;
|
||||
return array[index];
|
||||
return methodAccess();
|
||||
return object.field;
|
||||
return object.call();
|
||||
return op + op2;
|
||||
return op / asd * asd && a == true;
|
||||
return this.run();
|
||||
return this;
|
||||
return this.field;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
class Main {
|
||||
void main() {
|
||||
int r1;
|
||||
int i0, i1;
|
||||
int x,y,z;
|
||||
|
||||
i0 = 5;
|
||||
i1 = 2;
|
||||
|
||||
r1 = i1 * 3;
|
||||
write(r1); writeln();
|
||||
|
||||
r1 = i0 * i1;
|
||||
write(r1); writeln();
|
||||
|
||||
r1 = r1 * i0 * i1 * 3;
|
||||
write(r1); writeln();
|
||||
|
||||
y = 5;
|
||||
z = 10;
|
||||
x = (-y * z);
|
||||
write(x); writeln();
|
||||
|
||||
y = 0;
|
||||
z = - 10;
|
||||
x = (y * z);
|
||||
write(x); writeln();
|
||||
write(y * z); writeln();
|
||||
write(0 * -10); writeln();
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
/*Check the order of the declarations in the generated parser
|
||||
* Do the variables come always first or in their place? */
|
||||
class ClassName {
|
||||
void a() {}
|
||||
int a;
|
||||
void a() {}
|
||||
void tests(boolean d, nulle a) {}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/* Test what happens for Integers that are to big for 32bits
|
||||
2147483647 (=0x7FFFFFFF) is the biggest integer in 32bits (IntMAX)
|
||||
*/
|
||||
class Main {
|
||||
void main() {
|
||||
int x,y,z;
|
||||
x = 2147483647;
|
||||
write( x ); writeln();
|
||||
/* add 1 to IntMax and output it */
|
||||
write( x + 1); writeln();
|
||||
|
||||
/* read an int bigger than IntMAX */
|
||||
x = read();
|
||||
write(x); writeln();
|
||||
|
||||
/* performe some operation that should generate an int overflow */
|
||||
x = 21474836400;
|
||||
y = 60000;
|
||||
write( x + y); writeln();
|
||||
z = 20000000;
|
||||
write( y * z); writeln();
|
||||
write( (y * z) ); writeln();
|
||||
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
2147483647
|
|
@ -1,15 +0,0 @@
|
|||
/*Testing all related to methods (declaration and execution)*/
|
||||
class Main {
|
||||
void main() {
|
||||
callWithParams(a, b, c, 0, false);
|
||||
object.call(a, b, d);
|
||||
}
|
||||
|
||||
int method(int a, String b, int[] c) {
|
||||
|
||||
}
|
||||
|
||||
int[] method2() {}
|
||||
Object method3() {}
|
||||
Model[] method4() {}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/* Test read/write native functions */
|
||||
class Main {
|
||||
void main() {
|
||||
int r1, r2, readvar, a1;
|
||||
|
||||
r1 = 6;
|
||||
/* r2 = 22 */
|
||||
r2 = read();
|
||||
|
||||
write(r1); writeln(); // 6
|
||||
|
||||
/* test expressions inside write() */
|
||||
write(r1 - 3); writeln(); // 3
|
||||
write(r1 - 6); writeln(); // 0
|
||||
write(r1 - 7); writeln(); // -1
|
||||
/* should output 111 */
|
||||
readvar = read(); // 1
|
||||
write( (r1 + (r2 * 5)) + readvar); // 117
|
||||
write(- r1); // -6
|
||||
writeln();
|
||||
/* should output 15 */
|
||||
a1 = read(); // -15
|
||||
write(- a1); // 15
|
||||
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
22
|
||||
|
||||
|
||||
1
|
||||
|
||||
-15
|
|
@ -1,34 +0,0 @@
|
|||
/* testing different statements
|
||||
'condition' is not initialized
|
||||
*/
|
||||
class Main {
|
||||
void main() {
|
||||
if (condition) {
|
||||
instructions();
|
||||
asd.b = c;
|
||||
if (cond2) {
|
||||
|
||||
} else {
|
||||
nonEmptyBlock = a;
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
// Whiles
|
||||
while (condition) {
|
||||
while (anotherLoop == false) {
|
||||
nestedLoops();
|
||||
}
|
||||
}
|
||||
while (false) {} // emptyloop
|
||||
// Returns
|
||||
return; // empty
|
||||
return expr; // with expressions (expressions already tested)
|
||||
return array[index];
|
||||
// Writes
|
||||
write(a);
|
||||
write(9 + 10);
|
||||
writeln();
|
||||
write(call());
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* testing invalid casts */
|
||||
class Main
|
||||
{
|
||||
void main()
|
||||
{
|
||||
int a;
|
||||
Object d;
|
||||
d = new Object();
|
||||
a = (Object) d;
|
||||
a = (boolean[]) a;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
class Main {
|
||||
void main() {
|
||||
boolean b;
|
||||
int a,c,n;
|
||||
a = 10;
|
||||
//b = true;
|
||||
|
||||
while ( a>0 ) {
|
||||
a = a-1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/* test what happens if there are no parentheses */
|
||||
|
||||
class Main {
|
||||
void main() {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
|
||||
x = 5;
|
||||
y = 10;
|
||||
z = 100;
|
||||
|
||||
write( x + y + z); writeln();
|
||||
/* */
|
||||
write( - x + y - z); writeln();
|
||||
|
||||
/* should output 205 */
|
||||
write( x + 2 * z); writeln();
|
||||
|
||||
write( x + 2 * z / x + 1); writeln();
|
||||
write(+x); writeln();
|
||||
|
||||
}
|
||||
}
|
|
@ -10,11 +10,6 @@ public class Config {
|
|||
MACOSX
|
||||
}
|
||||
|
||||
/**
|
||||
* What kind of system we are on
|
||||
*/
|
||||
public static final SystemKind systemKind;
|
||||
|
||||
/**
|
||||
* Defines the extension used for assembler files on this platform.
|
||||
* Currently always {@code .s}.
|
||||
|
@ -84,11 +79,9 @@ public class Config {
|
|||
public static final String JAVA_EXE;
|
||||
|
||||
static {
|
||||
|
||||
final String os = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if(os.contains("windows") || os.contains("nt")) {
|
||||
systemKind = SystemKind.WINDOWS;
|
||||
BINARYEXT = ".exe";
|
||||
MAIN = "_main";
|
||||
PRINTF = "_printf";
|
||||
|
@ -108,7 +101,6 @@ public class Config {
|
|||
COMMENT_SEP = "#";
|
||||
}
|
||||
else if(os.contains("mac os x") || os.contains("darwin")) {
|
||||
systemKind = SystemKind.MACOSX;
|
||||
BINARYEXT = ".bin";
|
||||
MAIN = "_main";
|
||||
PRINTF = "_printf";
|
||||
|
@ -126,7 +118,6 @@ public class Config {
|
|||
COMMENT_SEP = "#";
|
||||
}
|
||||
else {
|
||||
systemKind = SystemKind.LINUX;
|
||||
BINARYEXT = ".bin";
|
||||
MAIN = "main";
|
||||
PRINTF = "printf";
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
package cd;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
|
||||
import cd.backend.codegen.AstCodeGenerator;
|
||||
import cd.frontend.parser.JavaliAstVisitor;
|
||||
import cd.frontend.parser.JavaliLexer;
|
||||
import cd.frontend.parser.JavaliParser;
|
||||
|
@ -24,6 +11,14 @@ import cd.ir.Ast.ClassDecl;
|
|||
import cd.ir.Symbol;
|
||||
import cd.ir.Symbol.TypeSymbol;
|
||||
import cd.util.debug.AstDump;
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The main entrypoint for the compiler. Consists of a series
|
||||
|
@ -37,7 +32,7 @@ public class Main {
|
|||
// Set to non-null to write debug info out
|
||||
public Writer debug = null;
|
||||
|
||||
// Set to non-null to write dump of control flow graph (Advanced Compiler Design)
|
||||
// Set to non-null to write dump of control flow graph
|
||||
public File cfgdumpbase;
|
||||
|
||||
/** Symbol for the Main type */
|
||||
|
@ -74,6 +69,12 @@ public class Main {
|
|||
|
||||
// Run the semantic check:
|
||||
m.semanticCheck(astRoots);
|
||||
|
||||
// Generate code:
|
||||
String sFile = arg + Config.ASMEXT;
|
||||
try (FileWriter fout = new FileWriter(sFile)) {
|
||||
m.generateCode(astRoots, fout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +112,13 @@ public class Main {
|
|||
new SemanticAnalyzer(this).check(astRoots);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateCode(List<ClassDecl> astRoots, Writer out) {
|
||||
{
|
||||
AstCodeGenerator cg = AstCodeGenerator.createCodeGenerator(this, out);
|
||||
cg.go(astRoots);
|
||||
}
|
||||
}
|
||||
|
||||
/** Dumps the AST to the debug stream */
|
||||
private void dumpAst(List<ClassDecl> astRoots) throws IOException {
|
||||
|
|
19
src/cd/backend/ExitCode.java
Normal file
19
src/cd/backend/ExitCode.java
Normal file
|
@ -0,0 +1,19 @@
|
|||
package cd.backend;
|
||||
|
||||
public enum ExitCode {
|
||||
OK(0),
|
||||
INVALID_DOWNCAST(1),
|
||||
INVALID_ARRAY_STORE(2),
|
||||
INVALID_ARRAY_BOUNDS(3),
|
||||
NULL_POINTER(4),
|
||||
INVALID_ARRAY_SIZE(5),
|
||||
INFINITE_LOOP(6),
|
||||
DIVISION_BY_ZERO(7),
|
||||
INTERNAL_ERROR(22);
|
||||
|
||||
public final int value;
|
||||
|
||||
private ExitCode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
176
src/cd/backend/codegen/AssemblyEmitter.java
Normal file
176
src/cd/backend/codegen/AssemblyEmitter.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.Config;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class AssemblyEmitter {
|
||||
public Writer out;
|
||||
public StringBuilder indent = new StringBuilder();
|
||||
public int counter = 0;
|
||||
|
||||
public AssemblyEmitter(Writer out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/** Creates an constant operand. */
|
||||
static String constant(int i) {
|
||||
return "$" + i;
|
||||
}
|
||||
|
||||
/** Creates an constant operand with the address of a label. */
|
||||
static String labelAddress(String lbl) {
|
||||
return "$" + lbl;
|
||||
}
|
||||
|
||||
/** Creates an operand relative to another operand. */
|
||||
static String registerOffset(int offset, Register reg) {
|
||||
return String.format("%d(%s)", offset, reg);
|
||||
}
|
||||
|
||||
/** Creates an operand addressing an item in an array */
|
||||
static String arrayAddress(Register arrReg, Register idxReg) {
|
||||
final int offset = Config.SIZEOF_PTR * 2; // one word each in front for
|
||||
// vptr and length
|
||||
final int mul = Config.SIZEOF_PTR; // assume all arrays of 4-byte elem
|
||||
return String.format("%d(%s,%s,%d)", offset, arrReg, idxReg, mul);
|
||||
}
|
||||
|
||||
void increaseIndent(String comment) {
|
||||
indent.append(" ");
|
||||
if (comment != null)
|
||||
emitComment(comment);
|
||||
}
|
||||
|
||||
void decreaseIndent() {
|
||||
indent.setLength(indent.length() - 2);
|
||||
}
|
||||
|
||||
void emitCommentSection(String name) {
|
||||
int indentLen = indent.length();
|
||||
int breakLen = 68 - indentLen - name.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(Config.COMMENT_SEP).append(" ");
|
||||
for (int i = 0; i < indentLen; i++)
|
||||
sb.append("_");
|
||||
sb.append(name);
|
||||
for (int i = 0; i < breakLen; i++)
|
||||
sb.append("_");
|
||||
|
||||
try {
|
||||
out.write(sb.toString());
|
||||
out.write("\n");
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
void emitComment(String comment) {
|
||||
emitRaw(Config.COMMENT_SEP + " " + comment);
|
||||
}
|
||||
|
||||
void emit(String op, Register src, String dest) {
|
||||
emit(op, src.repr, dest);
|
||||
}
|
||||
|
||||
void emit(String op, String src, Register dest) {
|
||||
emit(op, src, dest.repr);
|
||||
}
|
||||
|
||||
void emit(String op, Register src, Register dest) {
|
||||
emit(op, src.repr, dest.repr);
|
||||
}
|
||||
|
||||
void emit(String op, String src, String dest) {
|
||||
emitRaw(String.format("%s %s, %s", op, src, dest));
|
||||
}
|
||||
|
||||
void emit(String op, int src, Register dest) {
|
||||
emit(op, constant(src), dest);
|
||||
}
|
||||
|
||||
void emit(String op, int src, String dest) {
|
||||
emit(op, constant(src), dest);
|
||||
}
|
||||
|
||||
void emit(String op, String dest) {
|
||||
emitRaw(op + " " + dest);
|
||||
}
|
||||
|
||||
void emit(String op, Register reg) {
|
||||
emit(op, reg.repr);
|
||||
}
|
||||
|
||||
void emit(String op, int dest) {
|
||||
emit(op, constant(dest));
|
||||
}
|
||||
|
||||
void emit(String op, int src, int dest) {
|
||||
emit(op, constant(src), constant(dest));
|
||||
}
|
||||
|
||||
void emitMove(Register src, String dest) {
|
||||
emitMove(src.repr, dest);
|
||||
}
|
||||
|
||||
void emitMove(Register src, Register dest) {
|
||||
emitMove(src.repr, dest.repr);
|
||||
}
|
||||
|
||||
void emitMove(String src, Register dest) {
|
||||
emitMove(src, dest.repr);
|
||||
}
|
||||
|
||||
void emitMove(String src, String dest) {
|
||||
if (!src.equals(dest))
|
||||
emit("movl", src, dest);
|
||||
}
|
||||
|
||||
void emitMove(int src, Register dest) {
|
||||
emitMove(constant(src), dest);
|
||||
}
|
||||
|
||||
void emitLoad(int srcOffset, Register src, Register dest) {
|
||||
emitMove(registerOffset(srcOffset, src), dest.repr);
|
||||
}
|
||||
|
||||
void emitStore(Register src, int destOffset, Register dest) {
|
||||
emitStore(src.repr, destOffset, dest);
|
||||
}
|
||||
|
||||
void emitStore(String src, int destOffset, Register dest) {
|
||||
emitMove(src, registerOffset(destOffset, dest));
|
||||
}
|
||||
|
||||
void emitStore(int src, int destOffset, Register dest) {
|
||||
emitStore(constant(src), destOffset, dest);
|
||||
}
|
||||
|
||||
void emitConstantData(String data) {
|
||||
emitRaw(String.format("%s %s", Config.DOT_INT, data));
|
||||
}
|
||||
|
||||
String uniqueLabel() {
|
||||
String labelName = "label" + counter++;
|
||||
return labelName;
|
||||
}
|
||||
|
||||
void emitLabel(String label) {
|
||||
try {
|
||||
out.write(label + ":" + "\n");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
void emitRaw(String op) {
|
||||
try {
|
||||
out.write(indent.toString());
|
||||
out.write(op);
|
||||
out.write("\n");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
14
src/cd/backend/codegen/AssemblyFailedException.java
Normal file
14
src/cd/backend/codegen/AssemblyFailedException.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
public class AssemblyFailedException extends RuntimeException {
|
||||
private static final long serialVersionUID = -5658502514441032016L;
|
||||
|
||||
public final String assemblerOutput;
|
||||
public AssemblyFailedException(
|
||||
String assemblerOutput) {
|
||||
super("Executing assembler failed.\n"
|
||||
+ "Output:\n"
|
||||
+ assemblerOutput);
|
||||
this.assemblerOutput = assemblerOutput;
|
||||
}
|
||||
}
|
172
src/cd/backend/codegen/AstCodeGenerator.java
Normal file
172
src/cd/backend/codegen/AstCodeGenerator.java
Normal file
|
@ -0,0 +1,172 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.Config;
|
||||
import cd.Main;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.Ast.ClassDecl;
|
||||
import cd.ir.Symbol.*;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
|
||||
import static cd.Config.MAIN;
|
||||
import static cd.backend.codegen.RegisterManager.BASE_REG;
|
||||
import static cd.backend.codegen.RegisterManager.STACK_REG;
|
||||
|
||||
public class AstCodeGenerator {
|
||||
/** Constant representing the boolean TRUE as integer */
|
||||
static final int TRUE = 1;
|
||||
/** Constant representing the boolean FALSE as integer */
|
||||
static final int FALSE = 0;
|
||||
/** Size of any variable in assembly
|
||||
* Primitive variables take up 4 bytes (booleans are integers)
|
||||
* Reference variables are a 4 byte pointer
|
||||
*/
|
||||
static final int VAR_SIZE = 4;
|
||||
|
||||
RegsNeededVisitor rnv;
|
||||
|
||||
ExprGenerator eg;
|
||||
StmtGenerator sg;
|
||||
|
||||
protected final Main main;
|
||||
|
||||
final AssemblyEmitter emit;
|
||||
final RegisterManager rm = new RegisterManager();
|
||||
|
||||
AstCodeGenerator(Main main, Writer out) {
|
||||
initMethodData();
|
||||
main.allTypeSymbols = new ArrayList<>();
|
||||
|
||||
this.emit = new AssemblyEmitter(out);
|
||||
this.main = main;
|
||||
this.rnv = new RegsNeededVisitor();
|
||||
|
||||
this.eg = new ExprGenerator(this);
|
||||
this.sg = new StmtGenerator(this);
|
||||
}
|
||||
|
||||
protected void debug(String format, Object... args) {
|
||||
this.main.debug(format, args);
|
||||
}
|
||||
|
||||
public static AstCodeGenerator createCodeGenerator(Main main, Writer out) {
|
||||
return new AstCodeGenerator(main, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method. Causes us to emit x86 assembly corresponding to {@code ast}
|
||||
* into {@code file}. Throws a {@link RuntimeException} should any I/O error
|
||||
* occur.
|
||||
*
|
||||
* <p>
|
||||
* The generated file will be divided into two sections:
|
||||
* <ol>
|
||||
* <li>Prologue: Generated by {@link #emitPrefix()}. This contains any
|
||||
* introductory declarations and the like.
|
||||
* <li>Body: Generated by {@link ExprGenerator}. This contains the main
|
||||
* method definitions.
|
||||
* </ol>
|
||||
*/
|
||||
public void go(List<? extends ClassDecl> astRoots) {
|
||||
// Find main type and assign to main.mainType
|
||||
// Add array types to the types list to lookup later
|
||||
for (ClassDecl decl : astRoots) {
|
||||
if (decl.name.equals("Main")) {
|
||||
main.mainType = decl.sym;
|
||||
}
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(decl.sym));
|
||||
}
|
||||
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(PrimitiveTypeSymbol.intType));
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(PrimitiveTypeSymbol.booleanType));
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(ClassSymbol.objectType));
|
||||
|
||||
emitPrefix();
|
||||
for (ClassDecl ast : astRoots) {
|
||||
sg.gen(ast);
|
||||
}
|
||||
}
|
||||
|
||||
private void emitPrefix() {
|
||||
// Emit some useful string constants (copied from old HW1 method declaration)
|
||||
emit.emitRaw(Config.DATA_STR_SECTION);
|
||||
emit.emitLabel("STR_NL");
|
||||
emit.emitRaw(Config.DOT_STRING + " \"\\n\"");
|
||||
emit.emitLabel("STR_D");
|
||||
emit.emitRaw(Config.DOT_STRING + " \"%d\"");
|
||||
|
||||
// Define Object, Object[], int, int[], boolean and boolean[]
|
||||
List<TypeSymbol> elementTypes = new ArrayList<>();
|
||||
elementTypes.add(ClassSymbol.objectType);
|
||||
elementTypes.add(PrimitiveTypeSymbol.intType);
|
||||
elementTypes.add(PrimitiveTypeSymbol.booleanType);
|
||||
emit.emitRaw(Config.DATA_INT_SECTION);
|
||||
for (TypeSymbol type : elementTypes) {
|
||||
// type vtable
|
||||
emit.emitLabel(Label.type(type));
|
||||
emit.emitConstantData("0"); // Supertype (null)
|
||||
// array vtable
|
||||
emit.emitLabel(Label.type(new ArrayTypeSymbol(type)));
|
||||
emit.emitConstantData(Label.type(ClassSymbol.objectType)); // Supertype
|
||||
emit.emitConstantData(Label.type(type)); // Element type
|
||||
}
|
||||
|
||||
// Emit the new Main().main() code to start the program:
|
||||
|
||||
// 1. Enter TEXT and start the program
|
||||
emit.emitRaw(Config.TEXT_SECTION);
|
||||
emit.emit(".globl", MAIN);
|
||||
emit.emitLabel(MAIN);
|
||||
// 1.1. Prepare first frame
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
|
||||
// 2. Create main variable
|
||||
Ast.NewObject newMain = new Ast.NewObject("Main");
|
||||
newMain.type = main.mainType;
|
||||
Register mainLocation = eg.visit(newMain, null);
|
||||
|
||||
// 3. Call main()
|
||||
emit.emit("push", mainLocation);
|
||||
for (ClassSymbol sym = main.mainType; sym != ClassSymbol.objectType; sym = sym.superClass)
|
||||
if (sym.methods.getOrDefault("main", null) != null) {
|
||||
emit.emit("call", Label.method(sym, sym.methods.get("main")));
|
||||
break;
|
||||
}
|
||||
emitMethodSuffix(true);
|
||||
}
|
||||
|
||||
void initMethodData() {
|
||||
rm.initRegisters();
|
||||
}
|
||||
|
||||
|
||||
void emitMethodSuffix(boolean returnNull) {
|
||||
if (returnNull)
|
||||
emit.emit("movl", 0, Register.EAX);
|
||||
emit.emitRaw("leave");
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
static class Label {
|
||||
static String type(TypeSymbol symbol) {
|
||||
if (symbol instanceof ClassSymbol)
|
||||
return String.format("type_%s", symbol);
|
||||
else if (symbol instanceof ArrayTypeSymbol)
|
||||
return String.format("array_%s", ((ArrayTypeSymbol) symbol).elementType);
|
||||
else if (symbol instanceof PrimitiveTypeSymbol)
|
||||
return String.format("primive_%s", symbol);
|
||||
throw new RuntimeException("Unimplemented type symbol");
|
||||
}
|
||||
|
||||
static String method(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
|
||||
return String.format("method_%s_%s", classSymbol.name, methodSymbol.name);
|
||||
}
|
||||
|
||||
static String returnMethod(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
|
||||
return String.format("return_%s", method(classSymbol, methodSymbol));
|
||||
}
|
||||
}
|
||||
}
|
613
src/cd/backend/codegen/ExprGenerator.java
Normal file
613
src/cd/backend/codegen/ExprGenerator.java
Normal file
|
@ -0,0 +1,613 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.backend.ExitCode;
|
||||
import cd.backend.codegen.AstCodeGenerator.*;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
import cd.ir.Ast.*;
|
||||
import cd.ir.ExprVisitor;
|
||||
import cd.ir.Symbol.ClassSymbol;
|
||||
import cd.ir.Symbol.MethodSymbol;
|
||||
import cd.ir.Symbol.PrimitiveTypeSymbol;
|
||||
import cd.ir.Symbol.VariableSymbol;
|
||||
import cd.util.debug.AstOneLine;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cd.Config.SCANF;
|
||||
import static cd.backend.codegen.AstCodeGenerator.*;
|
||||
import static cd.backend.codegen.RegisterManager.BASE_REG;
|
||||
import static cd.backend.codegen.RegisterManager.CALLER_SAVE;
|
||||
import static cd.backend.codegen.RegisterManager.STACK_REG;
|
||||
|
||||
/**
|
||||
* Generates code to evaluate expressions. After emitting the code, returns a
|
||||
* String which indicates the register where the result can be found.
|
||||
*/
|
||||
class ExprGenerator extends ExprVisitor<Register,Location> {
|
||||
private final AstCodeGenerator cg;
|
||||
|
||||
ExprGenerator(AstCodeGenerator astCodeGenerator) {
|
||||
cg = astCodeGenerator;
|
||||
}
|
||||
|
||||
public Register gen(Expr ast) {
|
||||
return visit(ast, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register visit(Expr ast, Location arg) {
|
||||
try {
|
||||
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
|
||||
if (cg.rnv.calc(ast) > cg.rm.availableRegisters()) {
|
||||
Deque<Register> pushed = new ArrayDeque<>();
|
||||
for (Register r : RegisterManager.GPR) {
|
||||
if (cg.rm.isInUse(r)) {
|
||||
cg.emit.emit("push", r);
|
||||
pushed.push(r);
|
||||
cg.rm.releaseRegister(r);
|
||||
}
|
||||
}
|
||||
Register result = super.visit(ast, arg);
|
||||
for (Register r : pushed)
|
||||
cg.rm.useRegister(r);
|
||||
Register finalResult = cg.rm.getRegister();
|
||||
cg.emit.emitMove(result, finalResult);
|
||||
for (Register r = pushed.pop(); !pushed.isEmpty(); r = pushed.pop())
|
||||
cg.emit.emit("pop", r);
|
||||
return finalResult;
|
||||
} else return super.visit(ast, arg);
|
||||
} finally {
|
||||
cg.emit.decreaseIndent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register binaryOp(BinaryOp ast, Location arg) {
|
||||
// Simplistic HW1 implementation that does
|
||||
// not care if it runs out of registers
|
||||
|
||||
int leftRN = cg.rnv.calc(ast.left());
|
||||
int rightRN = cg.rnv.calc(ast.right());
|
||||
|
||||
Register leftReg, rightReg;
|
||||
if (leftRN > rightRN) {
|
||||
leftReg = visit(ast.left(), arg);
|
||||
rightReg = visit(ast.right(), arg);
|
||||
} else {
|
||||
rightReg = visit(ast.right(), arg);
|
||||
leftReg = visit(ast.left(), arg);
|
||||
}
|
||||
|
||||
cg.debug("Binary Op: %s (%s,%s)", ast, leftReg, rightReg);
|
||||
|
||||
switch (ast.operator) {
|
||||
case B_TIMES:
|
||||
cg.emit.emit("imul", rightReg, leftReg);
|
||||
break;
|
||||
case B_PLUS:
|
||||
cg.emit.emit("add", rightReg, leftReg);
|
||||
break;
|
||||
case B_MINUS:
|
||||
cg.emit.emit("sub", rightReg, leftReg);
|
||||
break;
|
||||
case B_DIV:
|
||||
case B_MOD:
|
||||
// Check division by 0
|
||||
String beginDivision = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("cmp", 0, rightReg);
|
||||
cg.emit.emit("jne", beginDivision);
|
||||
Interrupts.exit(cg, ExitCode.DIVISION_BY_ZERO);
|
||||
cg.emit.emitLabel(beginDivision);
|
||||
|
||||
// Save EAX, EBX, and EDX to the stack if they are not used
|
||||
// in this subtree (but are used elsewhere). We will be
|
||||
// changing them.
|
||||
List<Register> dontBother = Arrays.asList(rightReg, leftReg);
|
||||
Register[] affected = { Register.EAX, Register.EBX, Register.EDX };
|
||||
|
||||
for (Register s : affected)
|
||||
if (!dontBother.contains(s) && cg.rm.isInUse(s))
|
||||
cg.emit.emit("pushl", s);
|
||||
|
||||
// Move the LHS (numerator) into eax
|
||||
// Move the RHS (denominator) into ebx
|
||||
cg.emit.emit("pushl", rightReg);
|
||||
cg.emit.emit("pushl", leftReg);
|
||||
cg.emit.emit("popl", Register.EAX);
|
||||
cg.emit.emit("popl", Register.EBX);
|
||||
cg.emit.emitRaw("cltd"); // sign-extend %eax into %edx
|
||||
cg.emit.emit("idivl", Register.EBX); // division, result into edx:eax
|
||||
|
||||
// Move the result into the LHS, and pop off anything we saved
|
||||
if (ast.operator == BinaryOp.BOp.B_DIV)
|
||||
cg.emit.emitMove(Register.EAX, leftReg);
|
||||
else
|
||||
cg.emit.emitMove(Register.EDX, leftReg);
|
||||
for (int i = affected.length - 1; i >= 0; i--) {
|
||||
Register s = affected[i];
|
||||
if (!dontBother.contains(s) && cg.rm.isInUse(s))
|
||||
cg.emit.emit("popl", s);
|
||||
}
|
||||
break;
|
||||
case B_AND:
|
||||
cg.emit.emit("and", rightReg, leftReg);
|
||||
break;
|
||||
case B_OR:
|
||||
cg.emit.emit("or", rightReg, leftReg);
|
||||
break;
|
||||
case B_EQUAL: // a == b <--> ! (a != b)
|
||||
cg.emit.emit("xor", rightReg, leftReg);
|
||||
// if 'leftReg'==0, set leftReg to '1'
|
||||
String equal = cg.emit.uniqueLabel();
|
||||
String end = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("cmp", 0, leftReg);
|
||||
cg.emit.emit("je", equal);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(0), leftReg);
|
||||
cg.emit.emit("jmp", end);
|
||||
cg.emit.emitLabel(equal);
|
||||
cg.emit.emitMove(TRUE, leftReg);
|
||||
cg.emit.emitLabel(end);
|
||||
break;
|
||||
case B_NOT_EQUAL:
|
||||
String skipTrue = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("xor", rightReg, leftReg);
|
||||
cg.emit.emit("cmp", 0, leftReg);
|
||||
cg.emit.emit("je", skipTrue);
|
||||
cg.emit.emitMove(TRUE, leftReg);
|
||||
cg.emit.emitLabel(skipTrue);
|
||||
break;
|
||||
default: // Comparison operations
|
||||
String endLabel = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("cmp", rightReg, leftReg);
|
||||
// leftReg - rightReg
|
||||
cg.emit.emitMove(TRUE, leftReg);
|
||||
switch (ast.operator) {
|
||||
case B_LESS_THAN:
|
||||
cg.emit.emit("jl", endLabel);
|
||||
break;
|
||||
case B_LESS_OR_EQUAL:
|
||||
cg.emit.emit("jle", endLabel);
|
||||
break;
|
||||
case B_GREATER_THAN:
|
||||
cg.emit.emit("jg", endLabel);
|
||||
break;
|
||||
case B_GREATER_OR_EQUAL:
|
||||
cg.emit.emit("jge", endLabel);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("A binary operation wasn't implemented");
|
||||
}
|
||||
cg.emit.emitMove(FALSE, leftReg);
|
||||
cg.emit.emitLabel(endLabel);
|
||||
}
|
||||
|
||||
cg.rm.releaseRegister(rightReg);
|
||||
|
||||
return leftReg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register booleanConst(BooleanConst ast, Location arg) {
|
||||
Register reg = cg.rm.getRegister();
|
||||
cg.emit.emitMove(ast.value ? TRUE : FALSE, reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register builtInRead(BuiltInRead ast, Location arg) {
|
||||
Register reg = cg.rm.getRegister();
|
||||
cg.emit.emit("sub", 16, STACK_REG);
|
||||
cg.emit.emit("leal", AssemblyEmitter.registerOffset(8, STACK_REG), reg);
|
||||
cg.emit.emitStore(reg, 4, STACK_REG);
|
||||
cg.emit.emitStore("$STR_D", 0, STACK_REG);
|
||||
cg.emit.emit("call", SCANF);
|
||||
cg.emit.emitLoad(8, STACK_REG, reg);
|
||||
cg.emit.emit("add", 16, STACK_REG);
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Cast from one type to another: {@code (typeName)arg}
|
||||
*/
|
||||
@Override
|
||||
public Register cast(Cast ast, Location arg) {
|
||||
// 1. Obtain a register with the desired type's address
|
||||
Register desiredType = cg.rm.getRegister();
|
||||
cg.emit.emit("lea", Label.type(ast.type), desiredType); // lea copies the label's address to the reg
|
||||
|
||||
// 2. Get a reference to the object to be casted, and push a copy for later
|
||||
Register runtimeType = visit(ast.arg(), arg);
|
||||
cg.emit.emit("push", runtimeType);
|
||||
|
||||
// 3. Go to the type's vtable
|
||||
cg.emit.emitLoad(0, runtimeType, runtimeType);
|
||||
|
||||
// 4. Runtime type check: recursively go to superType until
|
||||
String matchLabel = cg.emit.uniqueLabel();
|
||||
String checkSuperTypeLabel = cg.emit.uniqueLabel();
|
||||
cg.emit.emitLabel(checkSuperTypeLabel);
|
||||
cg.emit.emit("cmpl", desiredType, runtimeType);
|
||||
cg.emit.emit("je", matchLabel); // 4.1 It matches: ok
|
||||
cg.emit.emitLoad(0, runtimeType, runtimeType); // Go to superclass
|
||||
|
||||
cg.emit.emit("cmp", 0, runtimeType); // 4.2 null is reached (super type of Object): error
|
||||
cg.emit.emit("jne", checkSuperTypeLabel);
|
||||
Interrupts.exit(cg, ExitCode.INVALID_DOWNCAST);
|
||||
cg.emit.emitLabel(matchLabel);
|
||||
|
||||
cg.rm.releaseRegister(desiredType);
|
||||
|
||||
// 5. Recover pointer to object and return it
|
||||
cg.emit.emit("pop", runtimeType);
|
||||
return runtimeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register index(Index ast, Location arg) {
|
||||
boolean obtainReference = arg.isObtainReference();
|
||||
String invalidBoundLabel = cg.emit.uniqueLabel();
|
||||
String validBoundLabel = cg.emit.uniqueLabel();
|
||||
String checkBoundsLabel = cg.emit.uniqueLabel();
|
||||
|
||||
// Obtain pointer to array and index to be accessed
|
||||
Register pointer = visit(ast.left(), arg);
|
||||
Register index = visit(ast.right(), arg);
|
||||
|
||||
// check for null pointer
|
||||
cg.emit.emit("cmp", 0, pointer);
|
||||
cg.emit.emit("jne", checkBoundsLabel); // 0 != array
|
||||
Interrupts.exit(cg, ExitCode.NULL_POINTER);
|
||||
|
||||
// check if index>=0 and index<arraySize
|
||||
Register arraySizeReg = cg.rm.getRegister();
|
||||
cg.emit.emitLabel(checkBoundsLabel);
|
||||
cg.emit.emitLoad(1 * VAR_SIZE, pointer, arraySizeReg);
|
||||
cg.emit.emit("cmp", 0, index);
|
||||
cg.emit.emit("jl", invalidBoundLabel); // 0 > index
|
||||
cg.emit.emit("cmp", index, arraySizeReg);
|
||||
cg.emit.emit("jg", validBoundLabel); // index < array.length
|
||||
cg.emit.emitLabel(invalidBoundLabel);
|
||||
Interrupts.exit(cg, ExitCode.INVALID_ARRAY_BOUNDS);
|
||||
cg.rm.releaseRegister(arraySizeReg);
|
||||
|
||||
// return array element (base + 2 * VAR_SIZE + index * VAR_SIZE)
|
||||
cg.emit.emitLabel(validBoundLabel);
|
||||
cg.emit.emit("imul", VAR_SIZE, index);
|
||||
cg.emit.emit("add", pointer, index);
|
||||
cg.rm.releaseRegister(pointer);
|
||||
if (!obtainReference)
|
||||
cg.emit.emitLoad(2 * VAR_SIZE, index, index);
|
||||
else
|
||||
cg.emit.emit("add", 2 * VAR_SIZE, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register intConst(IntConst ast, Location arg) {
|
||||
Register reg = cg.rm.getRegister();
|
||||
cg.emit.emitMove(ast.value, reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register field(Field ast, Location arg) {
|
||||
boolean obtainReference = arg.isObtainReference();
|
||||
String accessFieldLabel = cg.emit.uniqueLabel();
|
||||
Register pointer = visit(ast.arg(), arg);
|
||||
cg.emit.emit("cmp", 0, pointer);
|
||||
cg.emit.emit("jne", accessFieldLabel);
|
||||
Interrupts.exit(cg, ExitCode.NULL_POINTER);
|
||||
|
||||
cg.emit.emitLabel(accessFieldLabel);
|
||||
String foundFieldLabel = cg.emit.uniqueLabel();
|
||||
String lookForFieldLabel = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("add", VAR_SIZE, pointer);
|
||||
cg.emit.emitLabel(lookForFieldLabel);
|
||||
cg.emit.emit("cmpl", ast.sym.hashCode(), AssemblyEmitter.registerOffset(0, pointer));
|
||||
cg.emit.emit("je", foundFieldLabel);
|
||||
cg.emit.emit("add", 2 * VAR_SIZE, pointer);
|
||||
cg.emit.emit("jmp", lookForFieldLabel);
|
||||
cg.emit.emitLabel(foundFieldLabel);
|
||||
if (obtainReference)
|
||||
cg.emit.emit("add", VAR_SIZE, pointer);
|
||||
else
|
||||
cg.emit.emitLoad(VAR_SIZE, pointer, pointer);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array structure:
|
||||
* <ul>
|
||||
* <li>Type of array (for casts, assignments and equalities)</li>
|
||||
* <li>Size of array (N)</li>
|
||||
* <li>element 0 or pointer to element 0</li>
|
||||
* <li>...</li>
|
||||
* <li>element N or pointer to element N</li>
|
||||
* </ul>
|
||||
* The type of the elements is stored in the vtable for the array (in order to
|
||||
* save space). <br/>
|
||||
*
|
||||
* The pointer that references the array points to the type of the array.
|
||||
* The reason for this is so that all reference types have at pointer + 0
|
||||
* the type, for dynamic type comparisons for assignments, casts and ==/!= operators
|
||||
*/
|
||||
@Override
|
||||
public Register newArray(NewArray ast, Location arg) {
|
||||
String validArraySizeLabel = cg.emit.uniqueLabel();
|
||||
|
||||
// Check size of array is positive
|
||||
Register arraySizeReg = visit(ast.arg(), arg);
|
||||
cg.emit.emit("cmp", 0, arraySizeReg);
|
||||
cg.emit.emit("jns", validArraySizeLabel); // size >= 0
|
||||
Interrupts.exit(cg, ExitCode.INVALID_ARRAY_SIZE);
|
||||
|
||||
// Reserve for length + 2 variables
|
||||
cg.emit.emitLabel(validArraySizeLabel);
|
||||
cg.emit.emit("push", arraySizeReg);
|
||||
cg.emit.emit("add", 2, arraySizeReg);
|
||||
Register arrayPointerReg = calloc(arraySizeReg);
|
||||
|
||||
// Store overhead information
|
||||
// Type reference
|
||||
Register arrayTypeReg = cg.rm.getRegister();
|
||||
cg.emit.emit("lea", Label.type(ast.type), arrayTypeReg);
|
||||
cg.emit.emitStore(arrayTypeReg, 0 * VAR_SIZE, arrayPointerReg);
|
||||
cg.rm.releaseRegister(arrayTypeReg);
|
||||
// Number of elements
|
||||
Register numElemReg = cg.rm.getRegister();
|
||||
cg.emit.emit("pop", numElemReg);
|
||||
cg.emit.emitStore(numElemReg, 1 * VAR_SIZE, arrayPointerReg);
|
||||
cg.rm.releaseRegister(numElemReg);
|
||||
|
||||
return arrayPointerReg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Structure of reference type objects (except arrays)
|
||||
* <ul>
|
||||
* <li>Pointer to type vtable</li>
|
||||
* <li>hashCode0, field0</li>
|
||||
* <li>...</li>
|
||||
* <li>hashCodeN, fieldN</li>
|
||||
* </ul>
|
||||
* The object pointer points to the first element, and every element
|
||||
* is of VAR_SIZE
|
||||
*/
|
||||
@Override
|
||||
public Register newObject(NewObject ast, Location arg) {
|
||||
ClassSymbol sym = (ClassSymbol) ast.type;
|
||||
|
||||
// Obtain from type size of allocated object and allocate memory
|
||||
int size = 1;
|
||||
ClassSymbol auxSym = sym;
|
||||
while (auxSym != ClassSymbol.objectType) {
|
||||
size += auxSym.fields.size() * 2;
|
||||
auxSym = auxSym.superClass;
|
||||
}
|
||||
Register sizeReg = cg.rm.getRegister();
|
||||
cg.emit.emitMove(size, sizeReg);
|
||||
|
||||
Register pointer = calloc(sizeReg);
|
||||
|
||||
// Store the pointer to the type vtable
|
||||
Register auxReg = cg.rm.getRegister();
|
||||
cg.emit.emit("lea", Label.type(sym), auxReg);
|
||||
cg.emit.emitStore(auxReg, 0, pointer);
|
||||
cg.emit.emit("push", pointer); // Save the pointer to the beginning to return later
|
||||
cg.rm.releaseRegister(auxReg);
|
||||
|
||||
// Store the hashes for the fields' variable symbols
|
||||
cg.emit.emit("add", VAR_SIZE, pointer);
|
||||
auxSym = sym;
|
||||
while (auxSym != ClassSymbol.objectType) {
|
||||
for (VariableSymbol field : auxSym.fields.values()) {
|
||||
cg.emit.emitStore(field.hashCode(), 0, pointer);
|
||||
cg.emit.emit("add", 2 * VAR_SIZE, pointer);
|
||||
}
|
||||
auxSym = auxSym.superClass;
|
||||
}
|
||||
|
||||
// Recover the initial address and return
|
||||
cg.emit.emit("pop", pointer);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register nullConst(NullConst ast, Location arg) {
|
||||
Register reg = cg.rm.getRegister();
|
||||
cg.emit.emitMove(0, reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public Register thisRef(ThisRef ast, Location arg) {
|
||||
Register register = cg.rm.getRegister();
|
||||
cg.emit.emitLoad(2 * VAR_SIZE, BASE_REG, register);
|
||||
return register;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* implementation of x86 calling convention according to:
|
||||
* http://unixwiz.net/techtips/win32-callconv-asm.html
|
||||
* following the __cdecl calling convention
|
||||
*/
|
||||
@Override
|
||||
public Register methodCall(MethodCallExpr ast, Location arg) {
|
||||
// 0. Save registers to stack
|
||||
List<Register> callerSaved = new ArrayList<>();
|
||||
for (Register reg : CALLER_SAVE) {
|
||||
if (cg.rm.isInUse(reg)) {
|
||||
callerSaved.add(0, reg);
|
||||
cg.emit.emit("push", Register.EAX);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Evaluate all the arguments left-to-right (according to Java's spec)
|
||||
// and push them to the stack in right-to-left order. We will push them ltr
|
||||
// and then swap them so that the order is reversed
|
||||
for (Expr allArgument : ast.allArguments()) {
|
||||
Register argumentRegister = visit(allArgument, arg);
|
||||
cg.emit.emit("push", argumentRegister);
|
||||
cg.rm.releaseRegister(argumentRegister);
|
||||
}
|
||||
for (int i = 0; i < ast.allArguments().size() / 2; i++) {
|
||||
int offset1 = i * VAR_SIZE;
|
||||
int offset2 = (ast.allArguments().size() - 1 - i) * VAR_SIZE;
|
||||
Register aux1 = cg.rm.getRegister();
|
||||
Register aux2 = cg.rm.getRegister();
|
||||
cg.emit.emitLoad(offset1, STACK_REG, aux1);
|
||||
cg.emit.emitLoad(offset2, STACK_REG, aux2);
|
||||
cg.emit.emitStore(aux1, offset2, STACK_REG);
|
||||
cg.emit.emitStore(aux2, offset1, STACK_REG);
|
||||
cg.rm.releaseRegister(aux1);
|
||||
cg.rm.releaseRegister(aux2);
|
||||
}
|
||||
|
||||
// 2. Call function
|
||||
// 2.1. Search for the method pointer using the hashCode
|
||||
Register thisRef = cg.rm.getRegister();
|
||||
cg.emit.emitLoad(0, STACK_REG, thisRef);
|
||||
int hashCode = ast.methodName.hashCode();
|
||||
String lookForMethod = cg.emit.uniqueLabel();
|
||||
String methodFound = cg.emit.uniqueLabel();
|
||||
cg.emit.emitLoad(0, thisRef, thisRef); // Go to type vtable
|
||||
cg.emit.emit("add", 1 * VAR_SIZE, thisRef); // Skip the reference to superType
|
||||
cg.emit.emitLabel(lookForMethod);
|
||||
cg.emit.emit("cmpl", hashCode, AssemblyEmitter.registerOffset(0, thisRef));
|
||||
cg.emit.emit("je", methodFound); // hashCode == methodName.hashCode
|
||||
cg.emit.emit("add", 2 * VAR_SIZE, thisRef); // Go to next hashCode
|
||||
cg.emit.emit("jmp", lookForMethod);
|
||||
cg.emit.emitLabel(methodFound);
|
||||
// 2.2. Call the function
|
||||
cg.emit.emit("call", AssemblyEmitter.registerOffset(VAR_SIZE, thisRef));
|
||||
cg.rm.releaseRegister(thisRef);
|
||||
|
||||
// 3. Pop arguments from stack
|
||||
cg.emit.emit("add", VAR_SIZE * ast.allArguments().size(), STACK_REG);
|
||||
|
||||
// 4. Return result to caller
|
||||
Register result = null;
|
||||
if (ast.sym.returnType != PrimitiveTypeSymbol.voidType) {
|
||||
result = cg.rm.getRegister();
|
||||
cg.emit.emitMove(Register.EAX, result);
|
||||
}
|
||||
|
||||
// 5. Restore registers
|
||||
for (Register reg : callerSaved) {
|
||||
if (cg.rm.isInUse(reg))
|
||||
cg.emit.emit("pop", Register.EAX);
|
||||
else
|
||||
cg.emit.emit("add", 4, Register.ESP); // Don't overwrite
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register unaryOp(UnaryOp ast, Location arg) {
|
||||
Register argReg = visit(ast.arg(), arg);
|
||||
switch (ast.operator) {
|
||||
case U_PLUS:
|
||||
break;
|
||||
|
||||
case U_MINUS:
|
||||
cg.emit.emit("negl", argReg);
|
||||
break;
|
||||
|
||||
case U_BOOL_NOT:
|
||||
cg.emit.emit("negl", argReg);
|
||||
cg.emit.emit("incl", argReg);
|
||||
break;
|
||||
}
|
||||
return argReg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the value or reference to a variable described by a Var node
|
||||
* and a VariableSymbol.
|
||||
*/
|
||||
@Override
|
||||
public Register var(Var ast, Location arg) {
|
||||
boolean obtainReference = arg.isObtainReference();
|
||||
Register register = cg.rm.getRegister();
|
||||
cg.emit.emitMove(BASE_REG, register);
|
||||
int offset;
|
||||
switch (ast.sym.kind) {
|
||||
case PARAM:
|
||||
offset = 3 + arg.methodSym().parameters.indexOf(ast.sym);
|
||||
break;
|
||||
case LOCAL:
|
||||
offset = -(1 + positionOfLocal(arg.methodSym(), ast.name));
|
||||
break;
|
||||
case FIELD:
|
||||
String foundFieldLabel = cg.emit.uniqueLabel();
|
||||
String lookForFieldLabel = cg.emit.uniqueLabel();
|
||||
cg.emit.emitLoad(2 * VAR_SIZE, register, register);
|
||||
cg.emit.emit("add", VAR_SIZE, register);
|
||||
cg.emit.emitLabel(lookForFieldLabel);
|
||||
cg.emit.emit("cmpl", ast.sym.hashCode(), AssemblyEmitter.registerOffset(0, register));
|
||||
cg.emit.emit("je", foundFieldLabel);
|
||||
cg.emit.emit("add", 2 * VAR_SIZE, register);
|
||||
cg.emit.emit("jmp", lookForFieldLabel);
|
||||
cg.emit.emitLabel(foundFieldLabel);
|
||||
offset = 1; // The next element will be the reference we want
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("VariableSymbol Kind option not implemented");
|
||||
}
|
||||
if (obtainReference)
|
||||
cg.emit.emit("add", offset * VAR_SIZE, register);
|
||||
else
|
||||
cg.emit.emitLoad(offset * VAR_SIZE, register, register);
|
||||
return register;
|
||||
}
|
||||
|
||||
private int positionOfLocal(MethodSymbol sym, String name) {
|
||||
List<String> locals = new ArrayList<>(sym.locals.keySet());
|
||||
locals.sort(Comparator.comparing(o -> o));
|
||||
for (int i = 0; i < locals.size(); i++)
|
||||
if (locals.get(i).equals(name))
|
||||
return i;
|
||||
throw new RuntimeException("The variable could not be found, and should be there");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that calls calloc to obtain memory for reference type variables
|
||||
* @param numberOfElements Size in bytes, stored in a register
|
||||
* @return A register with the first address of the memory allocated
|
||||
*/
|
||||
private Register calloc(Register numberOfElements) {
|
||||
cg.emit.emitComment("calloc begin arg=" + numberOfElements);
|
||||
String callocOk = cg.emit.uniqueLabel();
|
||||
|
||||
boolean eaxInUse = cg.rm.isInUse(Register.EAX);
|
||||
if (eaxInUse && Register.EAX != numberOfElements)
|
||||
cg.emit.emit("push", Register.EAX);
|
||||
|
||||
// push arguments to the stack, then call calloc
|
||||
// the size of allocated elements is always four bytes!
|
||||
cg.emit.emit("push", AssemblyEmitter.constant(VAR_SIZE));
|
||||
cg.emit.emit("push", numberOfElements);
|
||||
cg.rm.releaseRegister(numberOfElements);
|
||||
cg.emit.emit("call", "calloc");
|
||||
cg.emit.emit("add", 8, STACK_REG);
|
||||
|
||||
// Check for null pointer (if calloc fails)
|
||||
cg.emit.emit("cmp", 0, Register.EAX);
|
||||
cg.emit.emit("jne", callocOk);
|
||||
Interrupts.exit(cg, ExitCode.INTERNAL_ERROR);
|
||||
|
||||
cg.emit.emitLabel(callocOk);
|
||||
Register result = cg.rm.getRegister();
|
||||
cg.emit.emitMove(Register.EAX, result);
|
||||
|
||||
// pop EAX if needed
|
||||
if (eaxInUse && Register.EAX != numberOfElements)
|
||||
cg.emit.emit("pop", Register.EAX);
|
||||
|
||||
cg.emit.emitComment("calloc end");
|
||||
return result;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue