From 01703a73c544e707af7884ba9c42880da353253c Mon Sep 17 00:00:00 2001 From: Fayez Abu Alia Date: Tue, 7 Nov 2017 10:55:33 +0100 Subject: [PATCH] generiert Bytecode fuer einfachen Lambda --- .../dhbwstuttgart/bytecode/BytecodeGen.java | 9 ++- .../bytecode/BytecodeGenMethod.java | 72 +++++++++++++------ test/bytecode/Interface1.jav | 3 + test/bytecode/JavaTXCompilerTest.java | 13 +++- test/bytecode/LamRunnable.jav | 9 +++ test/bytecode/VoidMeth.jav | 5 ++ 6 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 test/bytecode/Interface1.jav create mode 100644 test/bytecode/LamRunnable.jav create mode 100644 test/bytecode/VoidMeth.jav diff --git a/src/de/dhbwstuttgart/bytecode/BytecodeGen.java b/src/de/dhbwstuttgart/bytecode/BytecodeGen.java index 425582ad..dbdb1dc2 100644 --- a/src/de/dhbwstuttgart/bytecode/BytecodeGen.java +++ b/src/de/dhbwstuttgart/bytecode/BytecodeGen.java @@ -29,6 +29,7 @@ public class BytecodeGen implements ASTVisitor { String type; String className; + private boolean isInterface; // stores parameter, local vars and the next index on the local variable table, which use for aload_i, astore_i,... HashMap paramsAndLocals;// = new HashMap<>(); @@ -43,6 +44,8 @@ public class BytecodeGen implements ASTVisitor { @Override public void visit(SourceFile sourceFile) { for(ClassOrInterface cl : sourceFile.getClasses()) { + isInterface = (cl.getModifiers()&512)==512; + System.out.println("IS Interface = "+"modifiers= "+cl.getModifiers()+" ->"+(cl.getModifiers()&512) + isInterface); BytecodeGen classGen = new BytecodeGen(classFiles); cl.accept(classGen); classGen.writeClass(cl.getClassName().toString()); @@ -88,9 +91,9 @@ public class BytecodeGen implements ASTVisitor { MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", desc.getDesc(), null, null); mv.visitCode(); System.out.println("-----Constructor-----"); - BytecodeGenMethod gen = new BytecodeGenMethod(className,field, mv,paramsAndLocals,desc.getDesc(),cw); + BytecodeGenMethod gen = new BytecodeGenMethod(className,field, mv,paramsAndLocals,desc.getDesc(),cw,isInterface); - mv.visitInsn(Opcodes.RETURN); +// mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } @@ -103,7 +106,7 @@ public class BytecodeGen implements ASTVisitor { MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methDesc.getDesc(), null, null); mv.visitCode(); - BytecodeGenMethod gen = new BytecodeGenMethod(className,method, mv,paramsAndLocals,methDesc.getDesc(),cw); + BytecodeGenMethod gen = new BytecodeGenMethod(className,method, mv,paramsAndLocals,methDesc.getDesc(),cw,isInterface); mv.visitMaxs(0, 0); mv.visitEnd(); } diff --git a/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java b/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java index 7547abc5..ff9c8faa 100644 --- a/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java +++ b/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java @@ -1,11 +1,10 @@ package de.dhbwstuttgart.bytecode; -import java.io.PrintStream; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.security.GeneralSecurityException; +import java.util.ArrayList; import java.util.HashMap; import de.dhbwstuttgart.syntaxtree.statement.*; @@ -31,17 +30,21 @@ public class BytecodeGenMethod implements StatementVisitor{ private String className; private int lamCounter; private ClassWriter cw; + private boolean isInterface; //for tests ** private String fieldName; private String fieldDesc; private Expression rightSideTemp; private String where; + private boolean isRightSideALambda = false; + + private ArrayList varsFunInterface; public BytecodeGenMethod(String className, Method m, MethodVisitor mv, HashMap paramsAndLocals, - String desc, ClassWriter cw) { + String desc, ClassWriter cw, boolean isInterface) { - this.where = "NORMAL METHOD"; + this.where = "<<<<<< NORMAL METHOD >>>>>>"; this.className = className; this.m = m; @@ -49,23 +52,27 @@ public class BytecodeGenMethod implements StatementVisitor{ this.paramsAndLocals = paramsAndLocals; this.desc = desc; this.cw = cw; + this.isInterface = isInterface; this.lamCounter = -1; + this.varsFunInterface = new ArrayList<>(); + this.m.block.accept(this); } public BytecodeGenMethod(LambdaExpression lambdaExpression, MethodVisitor mv, - HashMap paramsAndLocals, String desc) { - System.out.println("++++++IN LAMBDA -------"); + HashMap paramsAndLocals, String desc,boolean isInterface) { + System.out.println("\t\t++++++IN LAMBDA -------"); - this.where = "&&&&&&&& LAMBDA METHOD"; + this.where = "<<<<<< LAMBDA METHOD >>>>>>"; this.mv = mv; this.paramsAndLocals = paramsAndLocals; this.desc = desc; - + this.isInterface = isInterface; this.lamCounter = -1; + this.varsFunInterface = new ArrayList<>(); lambdaExpression.methodBody.accept(this); } @@ -85,19 +92,20 @@ public class BytecodeGenMethod implements StatementVisitor{ superCall.receiver.accept(this); superCall.arglist.accept(this); // mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", superCall.name, desc,false); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), superCall.name, desc,false); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), superCall.name, desc,isInterface); } // ?? @Override public void visit(LocalVar localVar) { System.out.println("in Local Var"); + mv.visitVarInsn(Opcodes.ALOAD, paramsAndLocals.get(localVar.name)); } // ?? @Override public void visit(LocalVarDecl localVarDecl) { // Integer i; - paramsAndLocals.put(localVarDecl.getName(), paramsAndLocals.size()+1); +// paramsAndLocals.put(localVarDecl.getName(), paramsAndLocals.size()+1); System.out.println("In localVarDecl"); } @@ -105,6 +113,14 @@ public class BytecodeGenMethod implements StatementVisitor{ public void visit(Assign assign) { System.out.println("Assign : \nright = "+assign.rightSide + "\nLeft = " + assign.lefSide); + // if the right side is a lambda => the left side must be a functional interface + if(assign.rightSide.getClass().equals(LambdaExpression.class)) { + isRightSideALambda = true; + }else { + isRightSideALambda = false; + } + + System.out.println("\t isRight Side lambda: " + isRightSideALambda); if(assign.lefSide.getClass().equals(AssignToField.class)) { // load_0, ldc or .. then putfield this.rightSideTemp = assign.rightSide; @@ -120,7 +136,7 @@ public class BytecodeGenMethod implements StatementVisitor{ @Override public void visit(Binary binary) { - System.out.println("++ In Binary: "); + System.out.println("\t++ In Binary: "); } @Override @@ -148,11 +164,12 @@ public class BytecodeGenMethod implements StatementVisitor{ methodName, arg3.toString(), null, null); // new BytecodeGenLambda(lambdaExpression, mvLambdaBody); HashMap paramsAndLocalsLambda = new HashMap<>(); - new BytecodeGenMethod(lambdaExpression, mvLambdaBody, paramsAndLocalsLambda, arg3.toString()); + new BytecodeGenMethod(lambdaExpression, mvLambdaBody, paramsAndLocalsLambda, arg3.toString(),isInterface); - mv.visitVarInsn(Opcodes.ASTORE, paramsAndLocalsLambda.size()); mvLambdaBody.visitMaxs(0, 0); mvLambdaBody.visitEnd(); + cw.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", + Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL); } @Override @@ -204,7 +221,7 @@ public class BytecodeGenMethod implements StatementVisitor{ @Override public void visit(MethodCall methodCall) { System.out.println(" In Methodcall: (" +methodCall.name+")" ); - System.out.print(" Method-Receiver: "); + System.out.print("\t\tMethod-Receiver: "); if(methodCall.receiver instanceof ExpressionReceiver){ System.out.print(((ExpressionReceiver) methodCall.receiver).expr + "\n"); }else{ @@ -216,18 +233,26 @@ public class BytecodeGenMethod implements StatementVisitor{ Descriptor mDesc = new Descriptor(methodCall.arglist, methodCall.getType()); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, methodCall.receiver.getType().toString(), - methodCall.name, mDesc.getDesc(), false); - // test - if(!methodCall.getType().toString().equals("V")) { - mv.visitInsn(Opcodes.POP); + System.out.println("is Vars empty: "+varsFunInterface.isEmpty()); + + // is methodCall.receiver functional Interface)? + if(varsFunInterface.contains(methodCall.receiver.getType())) { + mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, methodCall.receiver.getType().toString().replace(".", "/"), + methodCall.name, mDesc.getDesc(), false); + }else { + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, methodCall.receiver.getType().toString().replace(".", "/"), + methodCall.name, mDesc.getDesc(), isInterface); } + // test +// if(!methodCall.getType().toString().equals("V")) { +// mv.visitInsn(Opcodes.POP); +// } } @Override public void visit(NewClass methodCall) { System.out.println("In NewClass: "); - System.out.println("name: " + methodCall.name + " *** " + "Receiver: " + methodCall.receiver); + System.out.println("\t\tname: " + methodCall.name + " *** " + "Receiver: " + methodCall.receiver); mv.visitTypeInsn(Opcodes.NEW, methodCall.name.replace(".", "/")); mv.visitInsn(Opcodes.DUP); @@ -239,7 +264,7 @@ public class BytecodeGenMethod implements StatementVisitor{ } d += ")V"; - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, methodCall.name.replace(".", "/"), "", d, false); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, methodCall.name.replace(".", "/"), "", d, isInterface); } @Override @@ -322,6 +347,8 @@ public class BytecodeGenMethod implements StatementVisitor{ @Override public void visit(AssignToField assignLeftSide) { + if(isRightSideALambda) + varsFunInterface.add(assignLeftSide.field.getType()); // Loads the an object reference from the local variable // array slot onto the top of the operand stack. assignLeftSide.field.receiver.accept(this); @@ -332,6 +359,9 @@ public class BytecodeGenMethod implements StatementVisitor{ @Override public void visit(AssignToLocal assignLeftSide) { + System.out.println("In Assign To Local: "); + if(isRightSideALambda) + varsFunInterface.add(assignLeftSide.localVar.getType()); paramsAndLocals.put(assignLeftSide.localVar.name, paramsAndLocals.size()+1); mv.visitVarInsn(Opcodes.ASTORE, paramsAndLocals.size()); } diff --git a/test/bytecode/Interface1.jav b/test/bytecode/Interface1.jav new file mode 100644 index 00000000..b741819c --- /dev/null +++ b/test/bytecode/Interface1.jav @@ -0,0 +1,3 @@ +public interface Interface1{ + public void test(); +} \ No newline at end of file diff --git a/test/bytecode/JavaTXCompilerTest.java b/test/bytecode/JavaTXCompilerTest.java index 228a4851..5c3e0a87 100644 --- a/test/bytecode/JavaTXCompilerTest.java +++ b/test/bytecode/JavaTXCompilerTest.java @@ -21,19 +21,26 @@ import static org.junit.Assert.*; public class JavaTXCompilerTest { - private static final String rootDirectory = System.getProperty("user.dir")+"/test/javFiles/"; + private static final String rootDirectory = System.getProperty("user.dir")+"/test/bytecode/"; private static final List filesToTest = new ArrayList<>(); @Test public void test() throws IOException, java.lang.ClassNotFoundException { System.out.println(rootDirectory); - filesToTest.add(new File(rootDirectory+"EmptyClass.jav")); + String fileName = "LamRunnable"; + filesToTest.add(new File(rootDirectory+fileName+".jav")); + System.out.println(rootDirectory+fileName+".jav"); JavaTXCompiler compiler = new JavaTXCompiler(filesToTest); System.out.println("test"); for(File f : filesToTest){ String content = readFile(f.getPath(), StandardCharsets.UTF_8); HashMap bytecode = this.getBytecode(compiler.sourceFiles.get(f)); - this.writeClassFile(bytecode, "EmptyClass"); + String name = ""; + int pos = f.getName().lastIndexOf("."); + if(pos != -1) { + name = f.getName().substring(0, pos); + } + this.writeClassFile(bytecode, name); } } diff --git a/test/bytecode/LamRunnable.jav b/test/bytecode/LamRunnable.jav new file mode 100644 index 00000000..451858f2 --- /dev/null +++ b/test/bytecode/LamRunnable.jav @@ -0,0 +1,9 @@ +public class LamRunnable{ + + public LamRunnable(){ + + Runnable lam = () -> {System.out.println("lambda");}; + lam.run(); + } +} + \ No newline at end of file diff --git a/test/bytecode/VoidMeth.jav b/test/bytecode/VoidMeth.jav new file mode 100644 index 00000000..bdbf2545 --- /dev/null +++ b/test/bytecode/VoidMeth.jav @@ -0,0 +1,5 @@ +public class VoidMeth{ + public void test(){ + System.out.print(""); + } +} \ No newline at end of file