diff --git a/src/main/java/abstractSyntaxTree/Class/FieldDecl.java b/src/main/java/abstractSyntaxTree/Class/FieldDecl.java index 72fa97b..b77814d 100644 --- a/src/main/java/abstractSyntaxTree/Class/FieldDecl.java +++ b/src/main/java/abstractSyntaxTree/Class/FieldDecl.java @@ -36,10 +36,6 @@ public class FieldDecl extends AbstractType implements Node { //write field table } - public TypeCheckResult typeCheck(HashMap>>> methodContext, HashMap> typeContext, List fieldsOrMethods) throws Exception { - return null; - } - 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); @@ -47,11 +43,10 @@ public class FieldDecl extends AbstractType implements Node { } private String getFieldDescriptor() { - //TODO: Maybe we have to check for arrays? switch (type) { case "int": return "I"; - case "bool": + case "boolean": return "Z"; case "char": return "C"; diff --git a/src/main/java/abstractSyntaxTree/Class/MethodDecl.java b/src/main/java/abstractSyntaxTree/Class/MethodDecl.java index 125b5c6..fc074c7 100644 --- a/src/main/java/abstractSyntaxTree/Class/MethodDecl.java +++ b/src/main/java/abstractSyntaxTree/Class/MethodDecl.java @@ -22,7 +22,8 @@ public class MethodDecl implements Node { public String returnType; public BlockStatement codeBlock; - public HashMap localVars; + //TODO: Can this be a linked hash map? --> need it to be to get the index of local variables + public LinkedHashMap localVars; public MethodDecl(String classThatContainsMethod, String returnType, String name, ParameterList parameters, BlockStatement codeBlock){ this.classThatContainsMethod = classThatContainsMethod; @@ -30,7 +31,7 @@ public class MethodDecl implements Node { this.name = name; this.parameters = parameters; this.codeBlock = codeBlock; - this.localVars = new HashMap<>(); + this.localVars = new LinkedHashMap<>(); } public TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext) throws Exception { diff --git a/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java b/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java index 7e8f8a8..f73af36 100644 --- a/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java @@ -8,6 +8,7 @@ import org.objectweb.asm.*; import java.beans.Expression; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Objects; public class BinaryExpression extends AbstractType implements IExpression{ @@ -66,7 +67,7 @@ public class BinaryExpression extends AbstractType implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { // Label for the jump instruction Label operationFalse = new Label(); //Operation is false Label operationTrue = new Label(); //Operation is true @@ -76,88 +77,88 @@ public class BinaryExpression extends AbstractType implements IExpression{ // Bytecode for the binary operation switch (operator) { case "&&": - left.codeGen(mv); + left.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // IFEQ --> "if equals to zero" (false) --> if left exp is false - right.codeGen(mv); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // If right exp is false, jump to the end of the whole expression mv.visitJumpInsn(Opcodes.GOTO, operationTrue); // If it reaches this point, the right exp is true break; case "||": - left.codeGen(mv); + left.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true - right.codeGen(mv); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFNE, operationTrue); break; case "==": // Keep in mind that only primitive types are allowed in this case (at this time) - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPEQ, operationTrue); // If the two values are equal, jump to the end of the expression break; case "<": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPLT, operationTrue); // Checks only on less than, not equal break; case ">": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal break; case "<=": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPLE, operationTrue); // Checks on less than OR equal break; case ">=": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPGE, operationTrue); // Checks on greater than OR equal break; case "!=": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPNE, operationTrue); // Checks on not equal break; case "+": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.IADD); break; case "-": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.ISUB); break; case "*": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.IMUL); break; case "/": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.IDIV); break; diff --git a/src/main/java/abstractSyntaxTree/Expression/IExpression.java b/src/main/java/abstractSyntaxTree/Expression/IExpression.java index 1245400..24c4ac1 100644 --- a/src/main/java/abstractSyntaxTree/Expression/IExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/IExpression.java @@ -5,6 +5,7 @@ import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; import java.util.HashMap; +import java.util.LinkedHashMap; public interface IExpression { // typeCheck method @@ -12,5 +13,5 @@ public interface IExpression { TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext, HashMap localVars) throws Exception; // visit method for code generation - void codeGen(MethodVisitor mv) throws Exception; + void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception; } diff --git a/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java b/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java index fed4bec..a1eee95 100644 --- a/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java @@ -5,15 +5,17 @@ import abstractSyntaxTree.Class.RefType; import abstractSyntaxTree.Parameter.ParameterList; import jdk.jshell.spi.ExecutionControl; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import java.util.HashMap; +import java.util.LinkedHashMap; public class InstVarExpression implements IExpression{ //TODO: We have to decide upon more parameters and where they come from, for // example here we need the index of the field, the class reference and the field name - private RefType classRef; - private String fieldName; + public RefType classRef; + public String fieldName; public InstVarExpression(RefType classRef, String fieldName){ this.classRef = classRef; @@ -29,11 +31,33 @@ public class InstVarExpression implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { - throw new ExecutionControl.NotImplementedException("CodeGen not implemented for InstVarExpression"); + // typeContext: (ClassName, (FieldName, FieldType)) + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { + // Load "this" onto the stack + mv.visitVarInsn(Opcodes.ALOAD, 0); - //ALOAD the index of the var - //GETFIELD the field - //visitFieldInsn(Opcodes.GETFIELD, "class reference", "field name", type); + //Get the field information + String fieldType = typeContext.get(classRef.name).get(fieldName); + + String fieldDescriptor; + + switch (fieldType) { + case "int": + fieldDescriptor = "I"; + break; + case "boolean": + fieldDescriptor = "Z"; + break; + case "char": + fieldDescriptor = "C"; + break; + default: + //TODO: We need the fully qualified name of the class here in field type + fieldDescriptor = "L" + fieldType + ";"; + break; + } + + // Load the variable onto the stack + mv.visitFieldInsn(Opcodes.GETFIELD, classRef.name, fieldName, fieldDescriptor); } } diff --git a/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java b/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java index 7c1888a..0fe02a0 100644 --- a/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java +++ b/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java @@ -2,7 +2,10 @@ package abstractSyntaxTree.Expression; import TypeCheck.TypeCheckHelper; import TypeCheck.TypeCheckResult; -import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.*; + +import java.util.HashMap; +import java.util.LinkedHashMap; public class LocalVarIdentifier implements IExpression{ @@ -21,7 +24,41 @@ public class LocalVarIdentifier implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) 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"); + } + // Load the variable onto the stack + int index = -1; + int counter = 0; + for (String key : localVars.keySet()){ + if (key.equals(identifier)){ + index = counter; + break; + } + counter++; + } + + 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 "bool": + mv.visitVarInsn(Opcodes.ILOAD, index); + break; + case "void": + break; + default: + mv.visitVarInsn(Opcodes.ALOAD, index); + break; + } } } diff --git a/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java b/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java index 6fb57f9..9669e84 100644 --- a/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java @@ -7,6 +7,8 @@ import abstractSyntaxTree.Datatype.IDatatype; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Objects; public class UnaryExpression extends AbstractType implements IExpression{ @@ -47,7 +49,7 @@ public class UnaryExpression extends AbstractType implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { operand.codeGen(mv); diff --git a/src/main/java/abstractSyntaxTree/Statement/IStatement.java b/src/main/java/abstractSyntaxTree/Statement/IStatement.java index 2225532..1ee7271 100644 --- a/src/main/java/abstractSyntaxTree/Statement/IStatement.java +++ b/src/main/java/abstractSyntaxTree/Statement/IStatement.java @@ -6,11 +6,14 @@ import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; public interface IStatement extends Node { TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext, HashMap localVars) throws Exception; - void codeGen(MethodVisitor mv, HashMap localVars) throws Exception; + void codeGen(MethodVisitor mv, LinkedHashMap localVars) throws Exception; + + void codeGen(MethodVisitor mv, LinkedHashMap localVars, HashMap> typeContext) throws Exception; } diff --git a/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java b/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java index b01d899..c53b3af 100644 --- a/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java +++ b/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java @@ -32,5 +32,24 @@ public class LocalVarDecl implements IStatement{ @Override public void codeGen(MethodVisitor mv, HashMap localVars) throws Exception { localVars.put(identifier, type); + + // Set a default value for the variable --> less problems + int index = localVars.size()-1; + + switch (type){ + case "int": + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ISTORE, index); + break; + case "bool": + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ISTORE, index); + break; + case "void": + break; + default: + mv.visitInsn(Opcodes.ACONST_NULL); + mv.visitVarInsn(Opcodes.ASTORE, index); + } } } diff --git a/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java b/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java index 7c48d8e..b3c5868 100644 --- a/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java +++ b/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java @@ -8,16 +8,16 @@ import abstractSyntaxTree.Expression.InstVarExpression; import abstractSyntaxTree.Expression.LocalVarIdentifier; import abstractSyntaxTree.Parameter.ParameterList; import abstractSyntaxTree.Statement.IStatement; -import abstractSyntaxTree.Statement.LocalVarDecl; import org.objectweb.asm.*; import java.util.HashMap; -import java.util.Objects; +import java.util.LinkedHashMap; public class AssignStatementExpression extends AbstractType implements IExpression, IStatement { public String operator; public IExpression left; public IExpression right; + private InstVarExpression instVar; public AssignStatementExpression(String operator, IExpression leftExpression, IExpression rightExpression){ this.operator = operator; @@ -50,37 +50,70 @@ public class AssignStatementExpression extends AbstractType implements IExpressi return result; } - @Override - public void codeGen(MethodVisitor mv, HashMap localVars) throws Exception { - if (left instanceof LocalVarIdentifier) { - LocalVarIdentifier localVar = (LocalVarIdentifier) left; - String varName = localVar.getIdentifier(); - - //Get the index of the local variable - int varIndex = localVars.get(varName) - - - } - - } - public TypeCheckResult typeCheck() throws Exception { return null; } @Override - public void codeGen(MethodVisitor mv) throws Exception { - left.codeGen(mv); - right.codeGen(mv); + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { + //TODO: Do we need the value on the stack after assigning it? + //TODO: WE do not differentiate between InstanceVar and FieldVar -// if (left instanceof VarRefExpression varRef) { -// //TODO: Implement the handling of a variable reference --> I need a list of local variables -// // for that to determine if the variable is a local or field variable -// } else if (left instanceof InstVarExpression instVar) { -// mv.visitInsn(Opcodes.DUP_X1); -// -// // We now again need the owner (class reference), name (of the Field in the owner) and type of the field -// //mv.visitFieldInsn(Opcodes.PUTFIELD, instVar.className, instVar.varName, instVar.type); -// } + // Call the codeGen on the right expression which will push the value of the right expression onto the stack + right.codeGen(mv, typeContext, localVars); + + if (left instanceof LocalVarIdentifier) { + LocalVarIdentifier localVar = (LocalVarIdentifier) left; + String varName = localVar.getIdentifier(); + + //Get the index of the local variable + int index = -1; + int counter = 0; + for (String key : localVars.keySet()){ + if (key.equals(varName)){ + index = counter; + break; + } + counter++; + } + + if (index == -1){ + throw new Exception("Variable " + varName + " not found"); + } + + String type = localVars.get(localVar.getIdentifier()); + switch (type) { + case "int": + case "bool": + mv.visitVarInsn(Opcodes.ISTORE, index); + break; + default: + mv.visitVarInsn(Opcodes.ASTORE, index); + break; + } + + } else if (left instanceof InstVarExpression){ + instVar = (InstVarExpression) left; + + // Load "this" onto the stack + mv.visitVarInsn(Opcodes.ALOAD, 0); + + String fieldType = typeContext.get(instVar.classRef.name).get(instVar.fieldName); + + String fieldDescriptor; + switch (fieldType) { + case "int": + fieldDescriptor = "I"; + break; + case "boolean": + fieldDescriptor = "Z"; + break; + default: + //TODO: We need the fully qualified name of the class here in field type + fieldDescriptor = "L" + fieldType + ";"; + break; + } + mv.visitFieldInsn(Opcodes.PUTFIELD, instVar.classRef.name, instVar.fieldName, fieldDescriptor); + } } } diff --git a/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java b/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java index 3fe48b6..7a8498c 100644 --- a/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java +++ b/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java @@ -12,6 +12,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; public class MethodCallStatementExpression extends AbstractType implements IExpression, IStatement { @@ -50,27 +51,44 @@ public class MethodCallStatementExpression extends AbstractType implements IExpr return null; } - @Override - public void codeGen(MethodVisitor mv, HashMap localVars) throws Exception { - - } //Errors occur due to the change in parameter in the RefType class + // I need the methodContext here to get the method descriptor @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { //Generate Bytecode for the receiver if(classThatHasTheMethodIfNotThis != null){ - // classThatHasTheMethodIfNotThis.codeGen(new ClassWriter(ClassWriter.COMPUTE_FRAMES)); + //TODO: classThatHasTheMethodIfNotThis must be an object --> instance of the class not the class itself + //classThatHasTheMethodIfNotThis.codeGen(); + + String descriptor; + List methodDecls = thisClass.methodDecls; + for (MethodDecl methodDecl : methodDecls) { + if (methodDecl.name.equals(methodName)) { + //Get the method descriptor + descriptor = methodDecl.getMethodDescriptor(methodContext); + } + } + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classThatHasTheMethodIfNotThis.name, methodName, descriptor, false); + } else { + // Load this onto the stack mv.visitVarInsn(Opcodes.ALOAD, 0); } for (IExpression argument : arguments) { - argument.codeGen(mv); + argument.codeGen(mv, typeContext, localVars); } - //We need the class reference and the return type of the method - //mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, thisClass.name, methodName, return type); - + // Get the method descriptor + String descriptor; + List methodDecls = thisClass.methodDecls; + for (MethodDecl methodDecl : methodDecls) { + if (methodDecl.name.equals(methodName)) { + //Get the method descriptor + descriptor = methodDecl.getMethodDescriptor(methodContext); + } + } + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, thisClass.name, methodName, descriptor, false); } }