Merge remote-tracking branch 'origin/master'

This commit is contained in:
Krauß, Josefine 2024-06-30 13:39:32 +02:00
commit 4b2edaa6ff
10 changed files with 213 additions and 162 deletions

View File

@ -4,6 +4,7 @@ import TypeCheck.AbstractType;
import TypeCheck.TypeCheckException;
import TypeCheck.TypeCheckHelper;
import TypeCheck.TypeCheckResult;
import abstractSyntaxTree.Expression.IExpression;
import abstractSyntaxTree.Node;
import abstractSyntaxTree.Program;
import org.objectweb.asm.ClassWriter;
@ -18,11 +19,13 @@ import java.util.Objects;
public class FieldDecl extends AbstractType implements Node {
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.identifier = identifier;
this.expression = expression;
}
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) {
//TODO: Do we have fields with initial values? --> No dont think so --> assign
FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, identifier, getFieldDescriptor(), null, null);
fv.visitEnd();
}

View File

@ -53,12 +53,10 @@ public class MethodDecl implements Node {
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
// 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);
for (Parameter param : parameters.parameterList) {
@ -73,18 +71,40 @@ public class MethodDecl implements Node {
//Call the superclass constructor
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
int localVarIndex = 1;
for (Parameter param : parameters.parameterList) {
String paramType = param.type;
switch(paramType) {
case "int", "boolean", "char" -> mv.visitVarInsn(Opcodes.ILOAD, localVarIndex);
default -> mv.visitVarInsn(Opcodes.ALOAD, localVarIndex);
case "int", "boolean", "char" -> {
mv.visitVarInsn(Opcodes.ILOAD, localVarIndex);
mv.visitVarInsn(Opcodes.ISTORE, localVarIndex);
}
default -> {
mv.visitVarInsn(Opcodes.ALOAD, localVarIndex);
mv.visitVarInsn(Opcodes.ASTORE, localVarIndex);
}
}
localVarIndex++;
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", descriptor, false);
mv.visitCode();
codeBlock.codeGen(mv, localVars, typeContext, methodContext);
@ -171,6 +191,19 @@ public class MethodDecl implements Node {
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
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -6,6 +6,7 @@ import TypeCheck.TypeCheckResult;
import abstractSyntaxTree.Expression.IExpression;
import abstractSyntaxTree.Node;
import abstractSyntaxTree.Parameter.ParameterList;
import abstractSyntaxTree.Statement.BlockStatement;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
@ -88,8 +89,21 @@ public class RefType extends AbstractType implements Node {
field.codeGen(cw);
}
boolean hasCustomConstructor = false;
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();
}

View File

@ -76,97 +76,87 @@ public class BinaryExpression extends AbstractType implements IExpression{
Label expressionEnd = new Label(); //End of the whole expression
// Bytecode for the binary operation
if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/")) {
switch (operator) {
case "&&":
case "+" -> {
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);
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
break;
case "||":
}
case "||" -> {
left.codeGen(mv, localVars, typeContext, methodContext);
mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true
right.codeGen(mv, localVars, typeContext, methodContext);
mv.visitJumpInsn(Opcodes.IFNE, operationTrue);
break;
case "==":
}
case "==" -> {
// Keep in mind that only primitive types are allowed in this case (at this time)
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
break;
case "<":
}
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
break;
case ">":
}
case ">" -> {
left.codeGen(mv, localVars, typeContext, methodContext);
right.codeGen(mv, localVars, typeContext, methodContext);
mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal
break;
case "<=":
}
case "<=" -> {
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 ">=":
}
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 "!=":
}
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.");
}
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)
@ -176,6 +166,7 @@ public class BinaryExpression extends AbstractType implements IExpression{
mv.visitLabel(expressionEnd);
}
}
@Override
public boolean equals(Object o) {

View File

@ -46,33 +46,29 @@ public class LocalVarIdentifier extends AbstractType implements IExpression{
@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 {
// Check if the variable is in the list of local variables
String type = localVars.get(identifier);
if (type == null){
throw new Exception("Variable " + identifier + " not declared");
}
String type = null;
// if it's a local variable
if (localVars.containsKey(identifier)) {
type = localVars.get(identifier);
// Find the index of the variable
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"
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){
if (index == -1) {
throw new Exception("Variable " + identifier + " not found");
}
// Load the variable onto the stack
switch (type){
case "int":
mv.visitVarInsn(Opcodes.ILOAD, index);
break;
case "boolean":
switch (type) {
case "int", "boolean", "char":
mv.visitVarInsn(Opcodes.ILOAD, index);
break;
case "void":
@ -81,6 +77,30 @@ public class LocalVarIdentifier extends AbstractType implements IExpression{
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");
}
//TODO move this to a helper class and remove the doubled code in MethodDecl and in other places
private String getFieldDescriptor(String type) {
switch (type) {
case "int":
return "I";
case "boolean":
return "Z";
case "char":
return "C";
default:
return "L" + type + ";";
}
}
@Override

View File

@ -42,6 +42,7 @@ public class Program implements Node {
typeContext.put(oneClass.name, classVars);
// build method context
HashMap<String, HashMap<String, ParameterList>> identifierAndMethod = new HashMap<>();
for (MethodDecl methodDecl : oneClass.methodDecls){
if(methodDecl.returnType == null) continue;
@ -52,15 +53,14 @@ public class Program implements Node {
methodContext.put(oneClass.name, identifierAndMethod);
}
int mainCounter = 0;
// check if main exists
for(RefType oneClass : classes){
if(oneClass.hasMain)
mainCounter++;
}
// if(mainCounter != 1)
// throw new TypeCheckException("There is not 1 Main method.");
if(mainCounter != 1)
throw new TypeCheckException("There is not 1 Main method.");
// typecheck each class
TypeCheckResult result = new TypeCheckResult();
@ -72,7 +72,7 @@ public class Program implements Node {
}
public void codeGen() throws Exception{
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream("output.jar"))) {
try {
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);
@ -88,30 +88,9 @@ public class Program implements Node {
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

View File

@ -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
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)
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);
elseStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is false, execute the elseBlock

View File

@ -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 {
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) {
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
switch (type) {
case "int":
mv.visitInsn(Opcodes.ICONST_1);
mv.visitVarInsn(Opcodes.ISTORE, index);
break;
case "boolean":
case "int", "boolean", "char":
mv.visitInsn(Opcodes.ICONST_0);
mv.visitVarInsn(Opcodes.ISTORE, index);
break;

View File

@ -55,7 +55,6 @@ public class WhileStatement extends AbstractType implements IStatement {
mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); // Checks if the condition is false (0)
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
// So the next iteration starts with a clean stack
mv.visitJumpInsn(Opcodes.GOTO, LoopStart); // Jump to the start of the while loop

View File

@ -56,7 +56,12 @@ public class ASTGenerator extends DecafBaseVisitor<Node> {
}
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
@ -198,7 +203,6 @@ public class ASTGenerator extends DecafBaseVisitor<Node> {
return new AssignStatementExpression(ctx.Assign().getText(),(IExpression) left, (IExpression) right);
}
//
@Override
public Node visitMethodCall(DecafParser.MethodCallContext ctx) {
String methodName = ctx.Identifier().getText();