Lab 12: Complete Code Generation
Overview
In this lab, you complete your code generator to handle arbitrary MiniJava programs.
Overview
Objectives
-
Write MiniJava programs and corresponding Jasmin programs which cover the following constructs:
- classes with fields,
- methods with parameters and local variables,
- variable and field access,
- both kinds of assignments,
- method calls with arguments.
-
Extend your code generator to handle these constructs.
Submission
You need to submit your MiniJava project with a pull request against branch assignment12
on GitHub.
The Git documentation explains how to file such a request.
The deadline for submissions is December 20th, 23:59.
Grading
You can earn up to 25 points for your example MiniJava programs and their corresponding Jasmin programs. We will focus on completeness and correctness of your examples. Furthermore, you can earn up to 65 points for your code generator:
- transformation (55 points)
- types (5 points)
- fields (10 points)
- methods (15 points)
- variables (10 points)
- assignments (15 points)
- challenges (10 points)
- reusable field descriptors (2 points)
- reusable variable accessors (2 points)
- debug information (6 points)
You can earn up to 10 points for the quality of your code. We focus on readability in general, meaningful variable names and the consistent use of Stratego paradigms. We will consider the fact that Stratego is new to you.
In this assignment, we also give you the chance to earn up to 10 bonus points. This works like a challenge, but it gives you extra points on top of the total points of your assignment.
To make automated grading possible, you should make sure the Run main class builder from the Generate menu works! If this does not work, check if you are using JBCSource
(see lab 10).
Early Feedback
This assignment is graded manually. Thus, we do not provide early feedback for this submission.
Detailed Instructions
Git Repository
You continue with your work from the previous assignment.
See the Git documentation on how to create the assignment12
branch from your previous work.
Write Even More Jasmin Code
Before you complete your code generator, you should come up with small example MiniJava programs, which cover the following constructs:
- fields,
- parameters and local variables,
- variable and field access,
- assignments, and
- method calls with arguments.
Write corresponding Jasmin programs, which you expect to be the result of a MiniJava-to-Jasmin compiler. Generate Java class files from them and run them. Improve your programs until they run without errors.
Generate Code for Fields and Field Access
You now need to extend your code generator to handle field declarations and field access.
-
Provide rules for
type-to-jbc
, which translate MiniJava types into Jasmin types as used in field and method descriptors. -
Provide a rule for
field-to-jbc
, which translates field declarations from MiniJava into Jasmin field declarations. -
Extend your rules for
class-to-jbc
to handle field declarations. -
Provide a rule for
exp-to-jbc
, which translates field access expressions from MiniJava into sequences of Java bytecode instructions. We desugarVarRef
terms that refer to fields intoFieldRef
terms for you. This happens in a post-desugaring step, soFieldRef
terms are only visible in the analyzed AST, not the desugared AST. Similar to methods, you can query the type associated with the field name withget-type
. -
Provide a rule for
stmt-to-jbc
, which translates assignments to fields from MiniJava into sequences of Java bytecode instructions. This rule should callexp-to-jbc
to translate expressions to Java bytecode sequences. -
Provide a rule for
stmt-to-jbc
, which translates array assignments to fields from MiniJava into sequences of Java bytecode instructions.
You can test each rule by selecting a code fragment in the MiniJava editor and running your code generation builder.
Generate Code for Local Variables and Variable Access
Finally, you need to extend your code generator to cover parameters, local variables, and access to them.
-
Declare a custom property
var-index
of typeInt
for the namespaceVariable
in an NaBL file. See the challenges from last week on how to declare such a property. - Store the index
i
of each parameter and local variable on its name by applyingstore-var-index(|ctx, i)
to its name. See the challenges from last week on how to store properties on names. In order to calculate indices, you need to match the method declaration, not the parameter or variable declarations. The following strategies might be useful:map-with-index(s)
works likemap(s)
, but appliess
to pairs(i, e)
wheree
is an element of the list andi
is the index of this element in the list.nmap(s|i)
mapss
over a list where the strategys
takes the indexi
as a term argument and incrementsi
after each element.
Parameters are indexed from left to right, starting with
1
. Local variables can be indexed in arbitrary order, starting with the next available index. -
Extend your rule for
method-to-jbc
, which handles method declarations. Support parameters by generating variable declarations, which map variable numbers in generated Java bytecode to variable names in the original MiniJava program. Do the same for local variables. To get the variable number associated with a parameter or local variable, applyget-var-index
to its name. -
Extend your rule for
exp-to-jbc
, which handles method calls. Support calls with arguments by callingexp-to-jbc
recursively to translate argument expressions. -
Provide a rule for
exp-to-jbc
, which translates variable access expressions from MiniJava into sequences of Java bytecode instructions. -
Provide a rule for
stmt-to-jbc
, which translates assignments to variables from MiniJava into sequences of Java bytecode instructions. This rule should callexp-to-jbc
to translate expressions to Java bytecode sequences. - Provide a rule for
stmt-to-jbc
, which translates array assignments to variables from MiniJava into sequences of Java bytecode instructions. This rule should callexp-to-jbc
to translate expressions to Java bytecode sequences.
Challenges
Challenges are meant to distinguish excellent solutions from good solutions. Typically, they are less guided and require more investigation and programming skills.
Reusable Field Descriptors
Store and retrieve field descriptors or field references instead of re-generating them in different places. See the challenges from last week to get an idea how this can be achieved.
Reusable Variable Accessors
In your current implementation, you generate load and store instructions for parameters and local variables over and over again. Avoid this by storing and retrieving these instructions instead of storing their index.
Generate Debug Information
Real-life compilers add debug information to generated code. This information is used by debuggers, but also in runtime error messages.
Write a MiniJava program that causes a runtime exception, e.g. by accessing an array out of bound. When you compile this program to Java classes and run it in the virtual machine, you expect the runtime error to report you the position of the expression causing the error in the source code. To achieve this behaviour, you need to add source file and line number directives.
-
Extend your
class-to-jbc
rules for classes, to include a source file directive pointing to the MiniJava file you compile. -
When compiling expressions and statements, you can add line number directives in front of the resulting Java Bytecode instructions. To find out the line number of an AST element, you can apply the strategy
origin-line
from theeditor/origins
library. Instead of adding code for line numbers to every rule ofexp-to-jbc
andstmt-to-jbc
, you should define new strategiesexp-to-jbc-ln
andstmt-to-jbc-ln
which generates just the line number directive and calls your original code generation strategiesexp-to-jbc
orstmt-to-jbc
, respectively. Next, replace applications ofexp-to-jbc
andstmt-to-jbc
byexp-to-jbc-ln
andstmt-to-jbc-ln
wherever you need to generate line numbers. -
It is pretty common, that several expression occur in the same line. In this case, you should generate only a single line number directive. To achieve this, you have various options:
- Remove redundant line number directives in a post-processing step.
- Keep track of already used line numbers by passing a state as a term argument to strategy invocations.
- Maintain a state using dynamic rules.
- Maintain a state in the index.
Bonus
Generate Precise Ranges for Local Variables
A precise range of a local variable covers only the parts in the code where the variable is defined and used:
- There is only one continuous range for each variable (in contrast, variable liveness as discussed in the lecture can be fragmented).
- The range should cover at least all instructions between the first and last load or store (whatever comes first/last) of a local variable.
- Loops might extend the range, since they might require a variable to survive.
You should extend your code generator to generate precise ranges. Similar to the stack limit challenge from last week, the analysis should be performed on the MiniJava code, not on the Java bytecode.