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>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="test"/>
|
<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/junit-4.12.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.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="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"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
2
.project
2
.project
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<projectDescription>
|
<projectDescription>
|
||||||
<name>Javali-HW3</name>
|
<name>Javali-HW4</name>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
<projects>
|
<projects>
|
||||||
</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
|
# HW4: code generation
|
||||||
## Parser and syntactic analysis
|
Better viewed as markdown
|
||||||
See repo containing [Homework #2](https://svn.inf.ethz.ch/svn/trg/cd_students/ss18/teams/nop90/HW2).
|
|
||||||
|
|
||||||
## Semantic analysis
|
# VTables
|
||||||
This project checks a list class declaration as trees of Ast nodes for
|
They are generated when each class is visited, except for those of Object,
|
||||||
possible semantic errors.
|
Object[], int[] and boolean[], which are generated in the bootstrapping.
|
||||||
|
|
||||||
### Design
|
When the object vtable is generated, so is the array table, which just
|
||||||
The semantic checks described [here](./SemanticChecks.md) are implemented in three
|
contains a reference to the supertype (in our case `Object`) and an unused
|
||||||
phases.
|
pointer to the declared type of the element's vtable.
|
||||||
|
|
||||||
1. Circular inheritance check (just with the names, and checking for non-existent
|
supertype pointer (Object) <-- vtable pointer
|
||||||
types) to avoid stack overflow problems in the main analysis. May throw a
|
element vtable pointer (unused)
|
||||||
`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)
|
|
||||||
|
|
||||||
### Testing
|
Each type's vtable contains the supertype as the first element (trait shared
|
||||||
A set of test cases (Javali programs, valid and invalid) are provided. The ones
|
with array type's vtables for easier cast type comparisons). Then they
|
||||||
designed specifically for this homework assignment are located in
|
contain all the methods available to an object of that type, including
|
||||||
`javali_tests/HW3_nop90`, and they cover all the code in the `cd.frontend.semantic`
|
all those inherited. As the dynamic type is not known, the number and
|
||||||
and `cd.ir` packages. They cover all the kinds of errors that can arise
|
position of the methods is not known when executing a method, therefore
|
||||||
from a semantic analysis of Javali programs. As the order of the errors
|
each method's pointer is prepended by the method's name hash (similar to
|
||||||
is not determined, each test case only checks one failing condition, and
|
fields in an object).
|
||||||
therefore many more test cases are needed for this homework than for
|
|
||||||
previous ones.
|
|
||||||
|
|
||||||
The test files are organized in folders, with names ending in Tests or Errors,
|
supertype pointer <-- vtable pointer
|
||||||
the latter only contain test cases that result in semantic errors.
|
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
|
1. The caller pushes the arguments in the stack, from right-to-left,
|
||||||
This project is designed for Eclipse and GNU/Linux, but it can be run
|
leaving the 0th argument (the reference to `this`) at the top of the stack.
|
||||||
in other environments, as long as they have a 32bit `IA 86x assembly` compiler
|
2. The caller executes `call` (the instruction pointer gets stored in the stack)
|
||||||
and a `JVM`. Check the `Config` class for more information.
|
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
|
Therefore, the stack frame follows the following structure (N arguments
|
||||||
Available as [javadoc](javadoc/index.html). It is generated running the following command:
|
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
|
# Variable and object representation
|
||||||
* `TODO.md`: contains tasks to complete
|
## Primitive variables
|
||||||
* `SemanticChecks.md`: contains the semantic checks numbered for quick reference
|
They are represented by value, with both integers and booleans occupying
|
||||||
* [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)
|
4 bytes, and booleans being represented as TRUE = 0, FALSE = 1.
|
||||||
* [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)
|
|
||||||
|
## 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)
|
|
47
build.xml
47
build.xml
|
@ -15,11 +15,6 @@
|
||||||
<property name="antlr.jar" value="${basedir}/lib/antlr-4.4-complete.jar"/>
|
<property name="antlr.jar" value="${basedir}/lib/antlr-4.4-complete.jar"/>
|
||||||
<property name="antlr.profile" value="false"/>
|
<property name="antlr.profile" value="false"/>
|
||||||
<property name="antlr.report" 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! -->
|
<!-- Cleans generated code, but NOT the parser source! -->
|
||||||
<target name="clean">
|
<target name="clean">
|
||||||
|
@ -53,13 +48,8 @@
|
||||||
</delete>
|
</delete>
|
||||||
</target>
|
</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! -->
|
<!-- Runs the tests. Use the compile target first! -->
|
||||||
<target name="test" depends="compile">
|
<target name="test" depends="compile">
|
||||||
<jacoco:coverage destfile="${coverage.file}">
|
|
||||||
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
||||||
<formatter type="brief" usefile="false"/>
|
<formatter type="brief" usefile="false"/>
|
||||||
<batchtest skipNonTests="true">
|
<batchtest skipNonTests="true">
|
||||||
|
@ -75,44 +65,7 @@
|
||||||
<pathelement location="${parser.jar}"/>
|
<pathelement location="${parser.jar}"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
</junit>
|
</junit>
|
||||||
</jacoco:coverage>
|
|
||||||
<fail if="tests-failed" />
|
<fail if="tests-failed" />
|
||||||
</target>
|
</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>
|
</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;
|
i6 = 6;
|
||||||
i7 = 7;
|
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();
|
||||||
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;
|
d = 40;
|
||||||
e = 1;
|
e = 1;
|
||||||
|
|
||||||
while ( b > c ) {
|
while ( b >= c ) {
|
||||||
write(b);
|
write(b);
|
||||||
b = b-1;
|
b = b-1;
|
||||||
}
|
}
|
||||||
a = c < (b+4-5/2);
|
a = c <= (b+4-5/2);
|
||||||
if (a){
|
if (a){
|
||||||
write(13);}
|
write(13);}
|
||||||
if (100/2>d*e){
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,13 +8,9 @@ class Main {
|
||||||
boolean b;
|
boolean b;
|
||||||
Object c;
|
Object c;
|
||||||
|
|
||||||
a = null;
|
|
||||||
b = null;
|
|
||||||
c = null;
|
c = null;
|
||||||
|
|
||||||
write(a);
|
write(a);
|
||||||
writeln();
|
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
|
MACOSX
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* What kind of system we are on
|
|
||||||
*/
|
|
||||||
public static final SystemKind systemKind;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the extension used for assembler files on this platform.
|
* Defines the extension used for assembler files on this platform.
|
||||||
* Currently always {@code .s}.
|
* Currently always {@code .s}.
|
||||||
|
@ -84,11 +79,9 @@ public class Config {
|
||||||
public static final String JAVA_EXE;
|
public static final String JAVA_EXE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
||||||
final String os = System.getProperty("os.name").toLowerCase();
|
final String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
|
||||||
if(os.contains("windows") || os.contains("nt")) {
|
if(os.contains("windows") || os.contains("nt")) {
|
||||||
systemKind = SystemKind.WINDOWS;
|
|
||||||
BINARYEXT = ".exe";
|
BINARYEXT = ".exe";
|
||||||
MAIN = "_main";
|
MAIN = "_main";
|
||||||
PRINTF = "_printf";
|
PRINTF = "_printf";
|
||||||
|
@ -108,7 +101,6 @@ public class Config {
|
||||||
COMMENT_SEP = "#";
|
COMMENT_SEP = "#";
|
||||||
}
|
}
|
||||||
else if(os.contains("mac os x") || os.contains("darwin")) {
|
else if(os.contains("mac os x") || os.contains("darwin")) {
|
||||||
systemKind = SystemKind.MACOSX;
|
|
||||||
BINARYEXT = ".bin";
|
BINARYEXT = ".bin";
|
||||||
MAIN = "_main";
|
MAIN = "_main";
|
||||||
PRINTF = "_printf";
|
PRINTF = "_printf";
|
||||||
|
@ -126,7 +118,6 @@ public class Config {
|
||||||
COMMENT_SEP = "#";
|
COMMENT_SEP = "#";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
systemKind = SystemKind.LINUX;
|
|
||||||
BINARYEXT = ".bin";
|
BINARYEXT = ".bin";
|
||||||
MAIN = "main";
|
MAIN = "main";
|
||||||
PRINTF = "printf";
|
PRINTF = "printf";
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
package cd;
|
package cd;
|
||||||
|
|
||||||
import java.io.File;
|
import cd.backend.codegen.AstCodeGenerator;
|
||||||
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.frontend.parser.JavaliAstVisitor;
|
import cd.frontend.parser.JavaliAstVisitor;
|
||||||
import cd.frontend.parser.JavaliLexer;
|
import cd.frontend.parser.JavaliLexer;
|
||||||
import cd.frontend.parser.JavaliParser;
|
import cd.frontend.parser.JavaliParser;
|
||||||
|
@ -24,6 +11,14 @@ import cd.ir.Ast.ClassDecl;
|
||||||
import cd.ir.Symbol;
|
import cd.ir.Symbol;
|
||||||
import cd.ir.Symbol.TypeSymbol;
|
import cd.ir.Symbol.TypeSymbol;
|
||||||
import cd.util.debug.AstDump;
|
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
|
* 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
|
// Set to non-null to write debug info out
|
||||||
public Writer debug = null;
|
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;
|
public File cfgdumpbase;
|
||||||
|
|
||||||
/** Symbol for the Main type */
|
/** Symbol for the Main type */
|
||||||
|
@ -74,6 +69,12 @@ public class Main {
|
||||||
|
|
||||||
// Run the semantic check:
|
// Run the semantic check:
|
||||||
m.semanticCheck(astRoots);
|
m.semanticCheck(astRoots);
|
||||||
|
|
||||||
|
// Generate code:
|
||||||
|
String sFile = arg + Config.ASMEXT;
|
||||||
|
try (FileWriter fout = new FileWriter(sFile)) {
|
||||||
|
m.generateCode(astRoots, fout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +113,13 @@ public class Main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void generateCode(List<ClassDecl> astRoots, Writer out) {
|
||||||
|
{
|
||||||
|
AstCodeGenerator cg = AstCodeGenerator.createCodeGenerator(this, out);
|
||||||
|
cg.go(astRoots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Dumps the AST to the debug stream */
|
/** Dumps the AST to the debug stream */
|
||||||
private void dumpAst(List<ClassDecl> astRoots) throws IOException {
|
private void dumpAst(List<ClassDecl> astRoots) throws IOException {
|
||||||
if (this.debug == null) return;
|
if (this.debug == null) return;
|
||||||
|
|
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