Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
4b2edaa6ff
@ -4,6 +4,7 @@ import TypeCheck.AbstractType;
|
|||||||
import TypeCheck.TypeCheckException;
|
import TypeCheck.TypeCheckException;
|
||||||
import TypeCheck.TypeCheckHelper;
|
import TypeCheck.TypeCheckHelper;
|
||||||
import TypeCheck.TypeCheckResult;
|
import TypeCheck.TypeCheckResult;
|
||||||
|
import abstractSyntaxTree.Expression.IExpression;
|
||||||
import abstractSyntaxTree.Node;
|
import abstractSyntaxTree.Node;
|
||||||
import abstractSyntaxTree.Program;
|
import abstractSyntaxTree.Program;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
@ -18,11 +19,13 @@ import java.util.Objects;
|
|||||||
public class FieldDecl extends AbstractType implements Node {
|
public class FieldDecl extends AbstractType implements Node {
|
||||||
|
|
||||||
public String type; // from parser
|
public String type; // from parser
|
||||||
public String identifier; // from parser
|
public String identifier;// from parser
|
||||||
|
public IExpression expression; // value of the field
|
||||||
|
|
||||||
public FieldDecl(String type, String identifier){
|
public FieldDecl(String type, String identifier, IExpression expression){
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
public TypeCheckResult typeCheck(HashMap<String, HashMap<String, String>> typeContext) throws TypeCheckException {
|
public TypeCheckResult typeCheck(HashMap<String, HashMap<String, String>> typeContext) throws TypeCheckException {
|
||||||
|
|
||||||
@ -46,7 +49,6 @@ public class FieldDecl extends AbstractType implements Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void codeGen(ClassWriter cw) {
|
public void codeGen(ClassWriter cw) {
|
||||||
//TODO: Do we have fields with initial values? --> No dont think so --> assign
|
|
||||||
FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, identifier, getFieldDescriptor(), null, null);
|
FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, identifier, getFieldDescriptor(), null, null);
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,10 @@ public class MethodDecl implements Node {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO: Es wird kein Bytecode für Standardkonstruktoren für die Klassen generiert
|
|
||||||
//TODO: Stack computing schlägt fehl --> Reihenfolge aufruf? --> Sobald if-else / if / while drin sind? --> vllt auch schon bei MethodenDecl
|
|
||||||
//Need to get the returnType of the method if it is an object
|
//Need to get the returnType of the method if it is an object
|
||||||
// methodContext (class, (returnType, (identifier, parameter)))
|
// methodContext (class, (returnType, (identifier, parameter)))
|
||||||
public void codeGen(ClassWriter cw, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext, HashMap<String, HashMap<String, String>> typeContext) throws Exception {
|
// typeContext (class, (type, identifier))
|
||||||
|
public void codeGen(ClassWriter cw, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext, HashMap<String, HashMap<String, String>> typeContext, List<FieldDecl> fieldDecls) throws Exception {
|
||||||
|
|
||||||
localVars.put("this", classThatContainsMethod);
|
localVars.put("this", classThatContainsMethod);
|
||||||
for (Parameter param : parameters.parameterList) {
|
for (Parameter param : parameters.parameterList) {
|
||||||
@ -73,18 +71,40 @@ public class MethodDecl implements Node {
|
|||||||
|
|
||||||
//Call the superclass constructor
|
//Call the superclass constructor
|
||||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||||
|
|
||||||
|
HashMap<String, String> classFields = typeContext.get(classThatContainsMethod);
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : classFields.entrySet()) {
|
||||||
|
String fieldName = entry.getKey();
|
||||||
|
for (FieldDecl field : fieldDecls) {
|
||||||
|
if (field.identifier.equals(fieldName)) {
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
field.expression.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
descriptor = getFieldDescriptor(field.type);
|
||||||
|
mv.visitFieldInsn(Opcodes.PUTFIELD, classThatContainsMethod, fieldName, descriptor);
|
||||||
|
} else {
|
||||||
|
throw new Exception("Field " + fieldName + " not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Load the parameters onto the stack
|
//Load the parameters onto the stack
|
||||||
int localVarIndex = 1;
|
int localVarIndex = 1;
|
||||||
for (Parameter param : parameters.parameterList) {
|
for (Parameter param : parameters.parameterList) {
|
||||||
String paramType = param.type;
|
String paramType = param.type;
|
||||||
switch(paramType) {
|
switch(paramType) {
|
||||||
case "int", "boolean", "char" -> mv.visitVarInsn(Opcodes.ILOAD, localVarIndex);
|
case "int", "boolean", "char" -> {
|
||||||
default -> mv.visitVarInsn(Opcodes.ALOAD, localVarIndex);
|
mv.visitVarInsn(Opcodes.ILOAD, localVarIndex);
|
||||||
|
mv.visitVarInsn(Opcodes.ISTORE, localVarIndex);
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, localVarIndex);
|
||||||
|
mv.visitVarInsn(Opcodes.ASTORE, localVarIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
localVarIndex++;
|
localVarIndex++;
|
||||||
}
|
}
|
||||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", descriptor, false);
|
|
||||||
|
|
||||||
mv.visitCode();
|
mv.visitCode();
|
||||||
codeBlock.codeGen(mv, localVars, typeContext, methodContext);
|
codeBlock.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
@ -171,6 +191,19 @@ public class MethodDecl implements Node {
|
|||||||
return descriptor.toString();
|
return descriptor.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getFieldDescriptor(String type) {
|
||||||
|
switch (type) {
|
||||||
|
case "int":
|
||||||
|
return "I";
|
||||||
|
case "boolean":
|
||||||
|
return "Z";
|
||||||
|
case "char":
|
||||||
|
return "C";
|
||||||
|
default:
|
||||||
|
return "L" + type + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -6,6 +6,7 @@ import TypeCheck.TypeCheckResult;
|
|||||||
import abstractSyntaxTree.Expression.IExpression;
|
import abstractSyntaxTree.Expression.IExpression;
|
||||||
import abstractSyntaxTree.Node;
|
import abstractSyntaxTree.Node;
|
||||||
import abstractSyntaxTree.Parameter.ParameterList;
|
import abstractSyntaxTree.Parameter.ParameterList;
|
||||||
|
import abstractSyntaxTree.Statement.BlockStatement;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ public class RefType extends AbstractType implements Node {
|
|||||||
// Method for code generation which iterates over all the field declarations
|
// Method for code generation which iterates over all the field declarations
|
||||||
// and method declarations and calls their CodeGen methods
|
// and method declarations and calls their CodeGen methods
|
||||||
|
|
||||||
public void codeGen(ClassWriter cw, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext, HashMap<String, HashMap<String, String>> typeContext) throws Exception {
|
public void codeGen(ClassWriter cw, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext, HashMap<String, HashMap<String, String>> typeContext) throws Exception {
|
||||||
|
|
||||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name, null,
|
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name, null,
|
||||||
"java/lang/Object", null);
|
"java/lang/Object", null);
|
||||||
@ -88,8 +89,21 @@ public class RefType extends AbstractType implements Node {
|
|||||||
field.codeGen(cw);
|
field.codeGen(cw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasCustomConstructor = false;
|
||||||
for (MethodDecl method : methodDecls) {
|
for (MethodDecl method : methodDecls) {
|
||||||
method.codeGen(cw, methodContext, typeContext);
|
if (method.name.equals(name) && method.returnType == null) {
|
||||||
|
hasCustomConstructor = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasCustomConstructor) {
|
||||||
|
MethodDecl standardConstructor = new MethodDecl(name, null, name, new ParameterList(new ArrayList<>()), new BlockStatement(new ArrayList<>(), "void"));
|
||||||
|
standardConstructor.codeGen(cw, methodContext, typeContext, fieldDecls);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MethodDecl method : methodDecls) {
|
||||||
|
method.codeGen(cw, methodContext, typeContext, fieldDecls);
|
||||||
}
|
}
|
||||||
cw.visitEnd();
|
cw.visitEnd();
|
||||||
}
|
}
|
||||||
|
@ -76,105 +76,96 @@ public class BinaryExpression extends AbstractType implements IExpression{
|
|||||||
Label expressionEnd = new Label(); //End of the whole expression
|
Label expressionEnd = new Label(); //End of the whole expression
|
||||||
|
|
||||||
// Bytecode for the binary operation
|
// Bytecode for the binary operation
|
||||||
switch (operator) {
|
if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/")) {
|
||||||
case "&&":
|
switch (operator) {
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
case "+" -> {
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // IFEQ --> "if equals to zero" (false) --> if left exp is false
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
mv.visitInsn(Opcodes.IADD);
|
||||||
|
}
|
||||||
|
case "-" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
mv.visitInsn(Opcodes.ISUB);
|
||||||
|
}
|
||||||
|
case "*" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
mv.visitInsn(Opcodes.IMUL);
|
||||||
|
}
|
||||||
|
case "/" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
mv.visitInsn(Opcodes.IDIV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (operator) {
|
||||||
|
case "&&" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // IFEQ --> "if equals to zero" (false) --> if left exp is false
|
||||||
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // If right exp is false, jump to the end of the whole expression
|
mv.visitJumpInsn(Opcodes.IFEQ, operationFalse);
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, operationTrue); // If it reaches this point, the right exp is true
|
mv.visitJumpInsn(Opcodes.GOTO, operationTrue); // If it reaches this point, the right exp is true
|
||||||
break;
|
}
|
||||||
|
case "||" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true
|
||||||
|
|
||||||
case "||":
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
mv.visitJumpInsn(Opcodes.IFNE, operationTrue);
|
||||||
mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true
|
}
|
||||||
|
case "==" -> {
|
||||||
|
// Keep in mind that only primitive types are allowed in this case (at this time)
|
||||||
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
mv.visitJumpInsn(Opcodes.IFNE, operationTrue);
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
break;
|
|
||||||
|
|
||||||
case "==":
|
mv.visitJumpInsn(Opcodes.IF_ICMPEQ, operationTrue); // If the two values are equal, jump to the end of the expression
|
||||||
// Keep in mind that only primitive types are allowed in this case (at this time)
|
}
|
||||||
|
case "<" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
mv.visitJumpInsn(Opcodes.IF_ICMPLT, operationTrue); // Checks only on less than, not equal
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
}
|
||||||
|
case ">" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IF_ICMPEQ, operationTrue); // If the two values are equal, jump to the end of the expression
|
mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal
|
||||||
break;
|
}
|
||||||
|
case "<=" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
|
||||||
case "<":
|
mv.visitJumpInsn(Opcodes.IF_ICMPLE, operationTrue); // Checks on less than OR equal
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
}
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
case ">=" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IF_ICMPLT, operationTrue); // Checks only on less than, not equal
|
mv.visitJumpInsn(Opcodes.IF_ICMPGE, operationTrue); // Checks on greater than OR equal
|
||||||
break;
|
}
|
||||||
|
case "!=" -> {
|
||||||
|
left.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
right.codeGen(mv, localVars, typeContext, methodContext);
|
||||||
|
|
||||||
case ">":
|
mv.visitJumpInsn(Opcodes.IF_ICMPNE, operationTrue); // Checks on not equal
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
}
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
default -> throw new TypeCheckException("The operator " + operator + " is not known.");
|
||||||
|
}
|
||||||
|
mv.visitLabel(operationFalse);
|
||||||
|
mv.visitInsn(Opcodes.ICONST_0); // Push false on the stack
|
||||||
|
mv.visitJumpInsn(Opcodes.GOTO, expressionEnd); // Jump to the end of the expression (skip the true push)
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal
|
mv.visitLabel(operationTrue);
|
||||||
break;
|
mv.visitInsn(Opcodes.ICONST_1); // Push true on the stack
|
||||||
|
|
||||||
case "<=":
|
mv.visitLabel(expressionEnd);
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IF_ICMPLE, operationTrue); // Checks on less than OR equal
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ">=":
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IF_ICMPGE, operationTrue); // Checks on greater than OR equal
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "!=":
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IF_ICMPNE, operationTrue); // Checks on not equal
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "+":
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
mv.visitInsn(Opcodes.IADD);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "-":
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
mv.visitInsn(Opcodes.ISUB);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "*":
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
mv.visitInsn(Opcodes.IMUL);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "/":
|
|
||||||
left.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
right.codeGen(mv, localVars, typeContext, methodContext);
|
|
||||||
mv.visitInsn(Opcodes.IDIV);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new TypeCheckException("The operator " + operator + " is not known.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mv.visitLabel(operationFalse);
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0); // Push false on the stack
|
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, expressionEnd); // Jump to the end of the expression (skip the true push)
|
|
||||||
|
|
||||||
mv.visitLabel(operationTrue);
|
|
||||||
mv.visitInsn(Opcodes.ICONST_1); // Push true on the stack
|
|
||||||
|
|
||||||
mv.visitLabel(expressionEnd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,40 +46,60 @@ public class LocalVarIdentifier extends AbstractType implements IExpression{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void codeGen(MethodVisitor mv, LinkedHashMap<String, String> localVars, HashMap<String, HashMap<String, String>> typeContext, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext) throws Exception {
|
public void codeGen(MethodVisitor mv, LinkedHashMap<String, String> localVars, HashMap<String, HashMap<String, String>> typeContext, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext) throws Exception {
|
||||||
// Check if the variable is in the list of local variables
|
String type = null;
|
||||||
String type = localVars.get(identifier);
|
|
||||||
if (type == null){
|
|
||||||
throw new Exception("Variable " + identifier + " not declared");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the index of the variable
|
// if it's a local variable
|
||||||
int index = -1;
|
if (localVars.containsKey(identifier)) {
|
||||||
int counter = 0;
|
type = localVars.get(identifier);
|
||||||
for (String key : localVars.keySet()){
|
// Find the index of the variable
|
||||||
if (key.equals(identifier)){
|
int index = -1;
|
||||||
index = counter+1; // +1 because the first local variable is at index 1, 0 is used for "this"
|
int counter = 0;
|
||||||
break;
|
for (String key : localVars.keySet()) {
|
||||||
|
if (key.equals(identifier)) {
|
||||||
|
index = counter + 1; // +1 because the first local variable is at index 1, 0 is used for "this"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
}
|
}
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == -1){
|
if (index == -1) {
|
||||||
|
throw new Exception("Variable " + identifier + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the variable onto the stack
|
||||||
|
switch (type) {
|
||||||
|
case "int", "boolean", "char":
|
||||||
|
mv.visitVarInsn(Opcodes.ILOAD, index);
|
||||||
|
break;
|
||||||
|
case "void":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If it's a field
|
||||||
|
} else if (typeContext.get(thisClass).get(identifier) != null){
|
||||||
|
type = typeContext.get(thisClass).get(identifier);
|
||||||
|
|
||||||
|
// Load "this" onto the stack
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
// Get the field from "this"
|
||||||
|
mv.visitFieldInsn(Opcodes.GETFIELD, thisClass, identifier, getFieldDescriptor(type));
|
||||||
|
} else
|
||||||
throw new Exception("Variable " + identifier + " not found");
|
throw new Exception("Variable " + identifier + " not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the variable onto the stack
|
//TODO move this to a helper class and remove the doubled code in MethodDecl and in other places
|
||||||
switch (type){
|
private String getFieldDescriptor(String type) {
|
||||||
|
switch (type) {
|
||||||
case "int":
|
case "int":
|
||||||
mv.visitVarInsn(Opcodes.ILOAD, index);
|
return "I";
|
||||||
break;
|
|
||||||
case "boolean":
|
case "boolean":
|
||||||
mv.visitVarInsn(Opcodes.ILOAD, index);
|
return "Z";
|
||||||
break;
|
case "char":
|
||||||
case "void":
|
return "C";
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
mv.visitVarInsn(Opcodes.ALOAD, index);
|
return "L" + type + ";";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ public class Program implements Node {
|
|||||||
typeContext.put(oneClass.name, classVars);
|
typeContext.put(oneClass.name, classVars);
|
||||||
|
|
||||||
// build method context
|
// build method context
|
||||||
|
|
||||||
HashMap<String, HashMap<String, ParameterList>> identifierAndMethod = new HashMap<>();
|
HashMap<String, HashMap<String, ParameterList>> identifierAndMethod = new HashMap<>();
|
||||||
for (MethodDecl methodDecl : oneClass.methodDecls){
|
for (MethodDecl methodDecl : oneClass.methodDecls){
|
||||||
if(methodDecl.returnType == null) continue;
|
if(methodDecl.returnType == null) continue;
|
||||||
@ -52,15 +53,14 @@ public class Program implements Node {
|
|||||||
methodContext.put(oneClass.name, identifierAndMethod);
|
methodContext.put(oneClass.name, identifierAndMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int mainCounter = 0;
|
int mainCounter = 0;
|
||||||
// check if main exists
|
// check if main exists
|
||||||
for(RefType oneClass : classes){
|
for(RefType oneClass : classes){
|
||||||
if(oneClass.hasMain)
|
if(oneClass.hasMain)
|
||||||
mainCounter++;
|
mainCounter++;
|
||||||
}
|
}
|
||||||
// if(mainCounter != 1)
|
if(mainCounter != 1)
|
||||||
// throw new TypeCheckException("There is not 1 Main method.");
|
throw new TypeCheckException("There is not 1 Main method.");
|
||||||
|
|
||||||
// typecheck each class
|
// typecheck each class
|
||||||
TypeCheckResult result = new TypeCheckResult();
|
TypeCheckResult result = new TypeCheckResult();
|
||||||
@ -72,7 +72,7 @@ public class Program implements Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void codeGen() throws Exception{
|
public void codeGen() throws Exception{
|
||||||
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream("output.jar"))) {
|
try {
|
||||||
for (RefType oneClass : classes) {
|
for (RefType oneClass : classes) {
|
||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, oneClass.name, null, "java/lang/Object", null);
|
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, oneClass.name, null, "java/lang/Object", null);
|
||||||
@ -88,30 +88,9 @@ public class Program implements Node {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*// Write the bytecode to a .class file in the .jar file
|
|
||||||
JarEntry entry = new JarEntry(oneClass.name + ".class");
|
|
||||||
jos.putNextEntry(entry);
|
|
||||||
jos.write(bytecode);
|
|
||||||
jos.closeEntry();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
for(RefType oneClass : classes){
|
|
||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
|
||||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,oneClass.name, null,
|
|
||||||
"java/lang/Object", null);
|
|
||||||
oneClass.codeGen(cw);
|
|
||||||
|
|
||||||
cw.visitEnd();
|
|
||||||
byte[] bytecode = cw.toByteArray();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,7 +54,7 @@ public class IfElseStatement extends AbstractType implements IStatement{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: There are 2 NOPs and one athrow on the stack between the if-Block and else-Block execution --> I have no idea why
|
||||||
@Override
|
@Override
|
||||||
public void codeGen(MethodVisitor mv, LinkedHashMap<String, String> localVars, HashMap<String, HashMap<String, String>> typeContext, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext) throws Exception {
|
public void codeGen(MethodVisitor mv, LinkedHashMap<String, String> localVars, HashMap<String, HashMap<String, String>> typeContext, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext) throws Exception {
|
||||||
|
|
||||||
@ -67,7 +67,8 @@ public class IfElseStatement extends AbstractType implements IStatement{
|
|||||||
|
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); //Checks if the condition is false (0)
|
mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); //Checks if the condition is false (0)
|
||||||
ifStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is true, execute the ifBlock
|
ifStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is true, execute the ifBlock
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, statementEnd); //Jump to the end of the if-else statement
|
|
||||||
|
mv.visitJumpInsn(Opcodes.GOTO, statementEnd); //Jump to the end of the else statement
|
||||||
|
|
||||||
mv.visitLabel(conditionFalse);
|
mv.visitLabel(conditionFalse);
|
||||||
elseStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is false, execute the elseBlock
|
elseStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is false, execute the elseBlock
|
||||||
|
@ -55,7 +55,19 @@ public class LocalVarDecl extends AbstractType implements IStatement{
|
|||||||
public void codeGen(MethodVisitor mv, LinkedHashMap<String, String> localVars, HashMap<String, HashMap<String, String>> typeContext, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext) throws Exception {
|
public void codeGen(MethodVisitor mv, LinkedHashMap<String, String> localVars, HashMap<String, HashMap<String, String>> typeContext, HashMap<String, HashMap<String, HashMap<String, ParameterList>>> methodContext) throws Exception {
|
||||||
localVars.put(identifier, type);
|
localVars.put(identifier, type);
|
||||||
|
|
||||||
int index = localVars.size() - 1;
|
int index = -1;
|
||||||
|
int counter = 0;
|
||||||
|
for (String key : localVars.keySet()){
|
||||||
|
if (key.equals(identifier)){
|
||||||
|
index = counter+1; // +1 because the first local variable is at index 1, 0 is used for "this"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == -1){
|
||||||
|
throw new Exception("Variable " + identifier + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
expression.codeGen(mv, localVars, typeContext,methodContext);
|
expression.codeGen(mv, localVars, typeContext,methodContext);
|
||||||
@ -72,11 +84,7 @@ public class LocalVarDecl extends AbstractType implements IStatement{
|
|||||||
|
|
||||||
// Set a default value for the variable --> less problems
|
// Set a default value for the variable --> less problems
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "int":
|
case "int", "boolean", "char":
|
||||||
mv.visitInsn(Opcodes.ICONST_1);
|
|
||||||
mv.visitVarInsn(Opcodes.ISTORE, index);
|
|
||||||
break;
|
|
||||||
case "boolean":
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0);
|
mv.visitInsn(Opcodes.ICONST_0);
|
||||||
mv.visitVarInsn(Opcodes.ISTORE, index);
|
mv.visitVarInsn(Opcodes.ISTORE, index);
|
||||||
break;
|
break;
|
||||||
|
@ -55,7 +55,6 @@ public class WhileStatement extends AbstractType implements IStatement {
|
|||||||
mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); // Checks if the condition is false (0)
|
mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); // Checks if the condition is false (0)
|
||||||
|
|
||||||
statement.codeGen(mv, blockLocalVars, typeContext, methodContext);
|
statement.codeGen(mv, blockLocalVars, typeContext, methodContext);
|
||||||
//statement.codeGen(mv);
|
|
||||||
//TODO: If the block ends with a return statement, we might have to pop it from the stack
|
//TODO: If the block ends with a return statement, we might have to pop it from the stack
|
||||||
// So the next iteration starts with a clean stack
|
// So the next iteration starts with a clean stack
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, LoopStart); // Jump to the start of the while loop
|
mv.visitJumpInsn(Opcodes.GOTO, LoopStart); // Jump to the start of the while loop
|
||||||
|
@ -56,7 +56,12 @@ public class ASTGenerator extends DecafBaseVisitor<Node> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Node generateFieldDecl(DecafParser.LocalVarDeclContext ctx) {
|
public Node generateFieldDecl(DecafParser.LocalVarDeclContext ctx) {
|
||||||
return new FieldDecl(ctx.type().getText(), ctx.Identifier().getText());
|
if (ctx.expression() != null) {
|
||||||
|
IExpression expression = (IExpression) visit(ctx.expression());
|
||||||
|
return new FieldDecl(ctx.type().getText(), ctx.Identifier().getText(), expression);
|
||||||
|
} else {
|
||||||
|
return new FieldDecl(ctx.type().getText(), ctx.Identifier().getText(), null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -198,7 +203,6 @@ public class ASTGenerator extends DecafBaseVisitor<Node> {
|
|||||||
return new AssignStatementExpression(ctx.Assign().getText(),(IExpression) left, (IExpression) right);
|
return new AssignStatementExpression(ctx.Assign().getText(),(IExpression) left, (IExpression) right);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitMethodCall(DecafParser.MethodCallContext ctx) {
|
public Node visitMethodCall(DecafParser.MethodCallContext ctx) {
|
||||||
String methodName = ctx.Identifier().getText();
|
String methodName = ctx.Identifier().getText();
|
||||||
|
Loading…
Reference in New Issue
Block a user