From d23df6513f89e9cf36863446d809517a3ee67145 Mon Sep 17 00:00:00 2001 From: Luisa Sinzker Fantin Date: Fri, 28 May 2021 15:02:12 +0000 Subject: [PATCH] rebased --- .gitignore | 1 + pom.xml | 78 +++---- .../decompiler/jimplify/LambdaTransformer.rsc | 208 +++++++++++++++++- .../java/samples/arrays/ArrayExample.java | 23 +- .../samples/lambdaExpressions/AddLambda.java | 14 ++ .../samples/lambdaExpressions/IncClosure.java | 15 ++ .../lambdaExpressions/LambdaExpressions.java | 80 +++---- .../lambdaExpressions/Palindromes.java | 29 +++ .../samples/lambdaExpressions/Runners.java | 44 ++++ .../samples/lambdaExpressions/SumList.java | 13 ++ .../lambdaExpressions/functionalArg.java | 5 +- .../lambdaExpressions/functionalRet.java | 2 +- .../lambdaExpressions/functionalVoid.java | 5 +- .../jimple/tests/TestLambdaTransformer.rsc | 20 +- 14 files changed, 426 insertions(+), 111 deletions(-) create mode 100644 src/test/java/samples/lambdaExpressions/AddLambda.java create mode 100644 src/test/java/samples/lambdaExpressions/IncClosure.java create mode 100644 src/test/java/samples/lambdaExpressions/Palindromes.java create mode 100644 src/test/java/samples/lambdaExpressions/Runners.java create mode 100644 src/test/java/samples/lambdaExpressions/SumList.java diff --git a/.gitignore b/.gitignore index 09e3bc9b..3ddf6acc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /bin/ /target/ +.gitignore \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2fa0cb42..1cc3a6cf 100644 --- a/pom.xml +++ b/pom.xml @@ -46,44 +46,44 @@ src/main/java src/test/java - - - maven-compiler-plugin - 3.7.0 - - 1.8 - 1.8 - - - - - org.rascalmpl - rascal-maven-plugin - 0.3.4 - - ${project.build.outputDirectory} - true - true - - ${project.basedir}/src/main/rascal - - - ${project.basedir}/src/main/rascal/lang/jimple/toolkit/GraphUtil.rsc - ${project.basedir}/src/main/rascal/lang/jimple/toolkit/CallGraph.rsc - - - - - it-compile - compile - - compile - - - - - - + + + + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + org.rascalmpl + rascal-maven-plugin + 0.3.4 + + ${project.build.outputDirectory} + true + true + + ${project.basedir}/src/main/rascal + + + ${project.basedir}/src/main/rascal/lang/jimple/toolkit/GraphUtil.rsc + ${project.basedir}/src/main/rascal/lang/jimple/toolkit/CallGraph.rsc + + + + + it-compile + compile + + compile + + + + + + - diff --git a/src/main/rascal/lang/jimple/decompiler/jimplify/LambdaTransformer.rsc b/src/main/rascal/lang/jimple/decompiler/jimplify/LambdaTransformer.rsc index 50111aff..d92fe412 100644 --- a/src/main/rascal/lang/jimple/decompiler/jimplify/LambdaTransformer.rsc +++ b/src/main/rascal/lang/jimple/decompiler/jimplify/LambdaTransformer.rsc @@ -1,28 +1,216 @@ module lang::jimple::decompiler::jimplify::LambdaTransformer -import lang::jimple::core::Syntax; +import lang::jimple::core::Syntax; -str bootstrapMethod = "bootstrap$"; +import String; +import List; + +alias CID = ClassOrInterfaceDeclaration; + +str bootstrapMethod = "bootstrap$"; public list[ClassOrInterfaceDeclaration] lambdaTransformer(ClassOrInterfaceDeclaration c) { - list[ClassOrInterfaceDeclaration] classes = []; + list[ClassOrInterfaceDeclaration] classes = []; - c = visit(c) { - case dynamicInvoke(_, bsmArgs, sig, args)=> generateStaticInvokeExp(mh, sig, args) - when iValue(methodHandle(mh)):= bsmArgs[1] + c = visit(c) { //TODO: add LambdaMetafactory verification(in the indy's first argument) + + //Accumulating Transformer: traverse the tree, collect information and also transform tree. + case dynamicInvoke(_, bsmArgs, sig, args): { + classes += generateBootstrapClass(bsmArgs, sig); + MethodSignature mh = methodSignature(bsmArgs[1]); + //iValue(methodHandle(mh)) := bsmArgs[1]; + insert generateStaticInvokeExp(mh, sig, args); + } + } - classes += c; return classes; } private InvokeExp generateStaticInvokeExp(MethodSignature sig1, MethodSignature sig2, list[Immediate] args) { - MethodSignature sig = methodSignature("$", returnType(sig2), bootstrapMethod, formals(sig2)); - + MethodSignature sig = methodSignature("$", returnType(sig2), bootstrapMethod, formals(sig2)); + return staticMethodInvoke(sig, args); -} +} + +private CID generateBootstrapClass(list[Immediate] bsmArgs, MethodSignature bsmSig) { + + MethodSignature targetSig = methodSignature(bsmArgs[1]); + + str bsmClassName = "$"; + + MethodSignature bootstrapSig = methodSignature(bsmClassName, TVoid(), "\", formals(bsmSig)); + + MethodSignature initSig = methodSignature("java.lang.Object", TVoid(), "\", []); + + list[Type] classVars = formals(bsmSig); + + list[Immediate] lambdaArgs = []; + + //MethodBody local variable declarations + list[LocalVariableDeclaration] bsmLocals = generateLocalVarDecls(formals(bsmSig), 0) //instance attribute vars + + generateLocalVarDecls([TObject(bsmClassName)], size(formals(bsmSig))); //thisClass var + + list[LocalVariableDeclaration] initLocals = generateLocalVarDecls([TObject(bsmClassName)], 0) //thisClass var + + generateLocalVarDecls(formals(bsmSig), 1); //instance attribute vars + + // if the type is TObject("java.lang.Object"), jlO, we need to create local variables for type casting + + list[Type] frm1 = valueFormals(bsmArgs[0]); //lambda signature formals + list[Type] frm2 = valueFormals(bsmArgs[2]); //erased signature formals (real types) + int numArgs = size(frm1); + int numCasts = 0; + + for(i <- [0..size(frm1)]) { + if(frm1[i]!=frm2[i]) + numCasts+=1; + } + + list[LocalVariableDeclaration] targetLocals = generateLocalVarDecls([TObject(bsmClassName)], 0) //thisClass var + + generateLocalVarDecls(valueFormals(bsmArgs[0]), 1); //method param vars (Object) + //variables for cast + if(numCasts>0) + targetLocals += generateLocalVarDecls(valueFormals(bsmArgs[2]), size(valueFormals(bsmArgs[0]))+1); + + targetLocals += generateLocalVarDecls(formals(bsmSig), size(valueFormals(bsmArgs[0]))+numCasts+1); //instance attribute vars + + //return var + if(returnType(targetSig) != TVoid()) + targetLocals += [localVariableDeclaration(returnType(targetSig), "$i0")]; + + //MethodBody statements + //instantiation "fase" + list[Statement] bsmStmts = instantiateParameters(formals(bsmSig), 0) + + [assign(localVariable("$r"), newInstance(TObject(bsmClassName)))]; + + list[Statement] initStmts = [identity("$r0", "@this", TObject(bsmClassName))] + + instantiateParameters(formals(bsmSig), 1); + + list[Statement] targetStmts = [identity("$r0", "@this", TObject(bsmClassName))] + + instantiateParameters(valueFormals(bsmArgs[0]), 1); + + //BSM STATEMENTS + list[Immediate] bsmParams = [local(localVariable(decl))|decl <- prefix(bsmLocals)]; + + bsmStmts += invokeStmt(specialInvoke(localVariable(last(bsmLocals)), bootstrapSig, bsmParams)); + bsmStmts += returnStmt(local(localVariable(last(bsmLocals)))); + + //INIT STATEMENTS + initStmts += invokeStmt(specialInvoke("$r0", initSig, [])); + //fieldRef assignment + list[Immediate] initParams = [local(localVariable(decl))|decl <- tail(initLocals)]; + + for(int i <- [0..size(initParams)]) { + FieldSignature fieldSig = fieldSignature(bsmClassName, formals(bsmSig)[i], "cap"); + initStmts += assign(fieldRef("$r0", fieldSig), immediate(initParams[i])); + } + + initStmts += returnEmptyStmt(); + //TARGET STATEMENTS + //1st: casts + //for(int i <- [0..numCasts]) { + for(int i <- [0..size(valueFormals(bsmArgs[2]))]) { + if(numCasts>0) + targetStmts += assign(localVariable("$r"), cast(valueFormals(bsmArgs[2])[i], local("$r"))); + lambdaArgs += local("$r"); + } + + //2nd: localFieldRef : instance attributes (cap0,..) + for(int i <- [0..size(formals(bsmSig))]){ + Expression lfr = localFieldRef("$r0", bsmClassName, formals(bsmSig)[i], "cap"); + targetStmts += assign(localVariable("$r"), lfr); + lambdaArgs += local("$r"); + } + + if(size(frm1)>0 && size(formals(bsmSig))>0) + lambdaArgs=reverse(lambdaArgs); + + //invoke (staticinvoke) lambdaMethod + if(returnType(methodSignature(bsmArgs[1]))==TVoid()){ + targetStmts += invokeStmt(staticMethodInvoke(methodSignature(bsmArgs[1]), lambdaArgs)); + targetStmts += returnEmptyStmt(); + } else { + targetStmts += assign(localVariable("$i0"), invokeExp(staticMethodInvoke(methodSignature(bsmArgs[1]), lambdaArgs))); + targetStmts += returnStmt(local("$i0")); + } + + //Method bodies + MethodBody bsmBody = methodBody(bsmLocals, bsmStmts, []); + MethodBody initBody = methodBody(initLocals, initStmts, []); + MethodBody targetBody = methodBody(targetLocals, targetStmts, []); + + //Methods + Method bsm = method([Public(), Static()], // list[Modifiers] modifiers + returnType(bsmSig), // Type returnType + "bootstrap$", // Name name + formals(bsmSig), // list[Type] formals + [], // list[Type] exceptions //TODO! + bsmBody); // MethodBody body + + Method initMethod = method([Public(), Static()], + TVoid(), + "\", + formals(bsmSig), + [], + initBody); + + + Method targetMethod = method([Public(), Static()], + returnType(targetSig), + methodName(bsmSig), + valueFormals(bsmArgs[0]), + [], + targetBody); + + + CID bsmClass = classDecl(TObject(bsmClassName), // Type typeName + [Public(), Final()], // list[Modifiers] modifiers + object(), // Type superClass + [returnType(bsmSig)], // list[Type] interfaces + getFields(bsmSig), // list[Fields] fields + [bsm, initMethod, targetMethod]); // list[Method] methods + + return bsmClass; +} + +list[Field] getFields(MethodSignature sig){ + list[Field] fields = []; + + for(int i <- [0..size(formals(sig))]){ + fields += field([], formals(sig)[i], "cap"); + } + + return fields; +} + +list[LocalVariableDeclaration] generateLocalVarDecls(list[Type] frmls, int n){ + list[LocalVariableDeclaration] localVarDecls = []; + + for(int i <- [0..size(frmls)]){ + localVarDecls += localVariableDeclaration(frmls[i], "$r"); + } + + return localVarDecls; +} + +list[Statement] instantiateParameters(list[Type] frmls, int n){ + list[Statement] stmts = []; + + for(int i <- [0..size(frmls)]){ + stmts += identity("$r", "@parameter", frmls[i]); + } + + return stmts; +} + private str className(methodSignature(name, _, _, _)) = name; private str methodName(methodSignature(_, _, name,_)) = name; private Type returnType(methodSignature(_, aType, _, _)) = aType; private list[Type] formals(methodSignature(_, _, _, formalArgs)) = formalArgs; +private list[Type] valueFormals(iValue(methodValue(_, formalArgs))) = formalArgs; +private MethodSignature methodSignature(iValue(methodHandle(mh))) = mh; +private Identifier localVariable(localVariableDeclaration(_, local)) = local; + + + diff --git a/src/test/java/samples/arrays/ArrayExample.java b/src/test/java/samples/arrays/ArrayExample.java index e4be2189..193b0fc2 100644 --- a/src/test/java/samples/arrays/ArrayExample.java +++ b/src/test/java/samples/arrays/ArrayExample.java @@ -5,24 +5,25 @@ public class ArrayExample { - public static void main(String[] args) throws Exception { - List nums = Arrays.asList(-3, 0, 1, 8); + public static void main(String[] args) throws Exception { + List nums = Arrays.asList(-3, 0, 1, 8); - Runnable r = () -> nums.forEach(n -> { + Runnable r = () -> nums.forEach(n -> { - if (n < 0) System.out.println("Negative: " + n); + if (n < 0) + System.out.println("Negative: " + n); - else System.out.println("Positive: " + n); + else + System.out.println("Positive: " + n); - }); + }); - Thread t = new Thread(r); + Thread t = new Thread(r); - t.start(); + t.start(); - t.join(); + t.join(); - } + } } - diff --git a/src/test/java/samples/lambdaExpressions/AddLambda.java b/src/test/java/samples/lambdaExpressions/AddLambda.java new file mode 100644 index 00000000..04eab7e9 --- /dev/null +++ b/src/test/java/samples/lambdaExpressions/AddLambda.java @@ -0,0 +1,14 @@ +package samples.lambdaExpressions; + +interface Addable { + int add(int a, int b); +} + +public class AddLambda { + public static void main(String[] args) { + + Addable sum = (a, b) -> (a + b); + + System.out.println(sum.add(1, 2)); + } +} \ No newline at end of file diff --git a/src/test/java/samples/lambdaExpressions/IncClosure.java b/src/test/java/samples/lambdaExpressions/IncClosure.java new file mode 100644 index 00000000..2cdd284b --- /dev/null +++ b/src/test/java/samples/lambdaExpressions/IncClosure.java @@ -0,0 +1,15 @@ +package samples.lambdaExpressions; + +interface Incrementable { + int inc(int a); +} + +public class IncClosure { + public static void main(String[] args) { + int i = 1; + + Incrementable incBy = (a) -> (a+i); + + System.out.println(incBy.inc(1)); + } +} diff --git a/src/test/java/samples/lambdaExpressions/LambdaExpressions.java b/src/test/java/samples/lambdaExpressions/LambdaExpressions.java index 7486bf89..23eda242 100644 --- a/src/test/java/samples/lambdaExpressions/LambdaExpressions.java +++ b/src/test/java/samples/lambdaExpressions/LambdaExpressions.java @@ -2,45 +2,45 @@ public class LambdaExpressions { - private static void doSomethingStatic() { - System.out.println("something"); - } - - public void lambdaRet() { - functionalRet f1 = () -> "foo"; - System.out.println(f1.eval()); - } - - public void lambdaParam() { - functionalArg f1 = (arg) -> System.out.println(arg); - f1.eval("arg"); - } - - public void lambdaVoid() { - functionalVoid f1 = () -> System.out.println("void"); - f1.eval(); - } - - public void passToMethod() { - doEval(() -> System.out.println("passed as param")); - } - - private void doEval(functionalVoid re) { - re.eval(); - } - - public void functionPointer() { - LambdaExpressions es = new LambdaExpressions(); - doEval(es::doSomething); - } - - private void doSomething() { - System.out.println("something"); - } - - public void functionPointerStatic() { - LambdaExpressions es = new LambdaExpressions(); - doEval(LambdaExpressions::doSomethingStatic); - } + private static void doSomethingStatic() { + System.out.println("something"); + } + + public void lambdaRet() { + functionalRet f1 = () -> "foo"; + System.out.println(f1.eval()); + } + + public void lambdaParam() { + functionalArg f1 = (arg) -> System.out.println(arg); + f1.eval("arg"); + } + + public void lambdaVoid() { + functionalVoid f1 = () -> System.out.println("void"); + f1.eval(); + } + + public void passToMethod() { + doEval(() -> System.out.println("passed as param")); + } + + private void doEval(functionalVoid re) { + re.eval(); + } + + public void functionPointer() { + LambdaExpressions es = new LambdaExpressions(); + doEval(es::doSomething); + } + + private void doSomething() { + System.out.println("something"); + } + + public void functionPointerStatic() { + LambdaExpressions es = new LambdaExpressions(); + doEval(LambdaExpressions::doSomethingStatic); + } } diff --git a/src/test/java/samples/lambdaExpressions/Palindromes.java b/src/test/java/samples/lambdaExpressions/Palindromes.java new file mode 100644 index 00000000..75499ff8 --- /dev/null +++ b/src/test/java/samples/lambdaExpressions/Palindromes.java @@ -0,0 +1,29 @@ +package samples.lambdaExpressions; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class Palindromes { + public static void main(String[] args) throws Exception { + + List words = Arrays.asList("Hannah", "Ana", "Elle", "Bob", "Otto", "Natan", "Luisa", "Andre", "Renata"); + + // compare to the inverse + Comparator stringComparator = (s1, s2) -> s1.toLowerCase() + .compareTo(new StringBuilder(s2).reverse().toString().toLowerCase()); + + Runnable r = () -> words.forEach(w -> { + if (stringComparator.compare(w, w) == 0) { + System.out.println(w + " is a palindrome."); + } else { + System.out.println(w + " is not a palindrome."); + } + }); + + Thread t = new Thread(r); + t.start(); + t.join(); + + } +} \ No newline at end of file diff --git a/src/test/java/samples/lambdaExpressions/Runners.java b/src/test/java/samples/lambdaExpressions/Runners.java new file mode 100644 index 00000000..82ac0df7 --- /dev/null +++ b/src/test/java/samples/lambdaExpressions/Runners.java @@ -0,0 +1,44 @@ +package samples.lambdaExpressions; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class Runners { + public static void main(String[] args) throws Exception { + + List words = Arrays.asList("Hannah", "Bob", "Otto", "Natan", "Luisa", "Andre", "Renata"); + + List nums = Arrays.asList(-2, -1, 0, 1, 2); + + // compare to the inverse + Comparator stringComparator = (s1, s2) -> s1.toLowerCase() + .compareTo(new StringBuilder(s2).reverse().toString().toLowerCase()); + + Runnable rString = () -> words.forEach(w -> { + if (stringComparator.compare(w, w) == 0) { + System.out.println(w + " is a palindrome."); + } else { + System.out.println(w + " is not a palindrome."); + } + }); + + Comparator intComparator = (i1, i2) -> i1.compareTo(i2); + + Runnable rInteger = () -> nums.forEach(n -> { + if (intComparator.compare(n, 0) == 0) { + System.out.println("ZERO"); + } else { + System.out.println(n); + } + }); + + Thread t1 = new Thread(rString); + t1.start(); + t1.join(); + + Thread t2 = new Thread(rInteger); + t2.start(); + t2.join(); + } +} \ No newline at end of file diff --git a/src/test/java/samples/lambdaExpressions/SumList.java b/src/test/java/samples/lambdaExpressions/SumList.java new file mode 100644 index 00000000..d6ad3f0b --- /dev/null +++ b/src/test/java/samples/lambdaExpressions/SumList.java @@ -0,0 +1,13 @@ +package samples.lambdaExpressions; + +import java.util.Arrays; +import java.util.List; + +public class SumList { + public static void main(String[] args) throws Exception { + + List nums = Arrays.asList(1, 2, 3, 4); + + System.out.println(nums.stream().mapToInt(n -> n.intValue()).sum()); + } +} \ No newline at end of file diff --git a/src/test/java/samples/lambdaExpressions/functionalArg.java b/src/test/java/samples/lambdaExpressions/functionalArg.java index a45b43a3..cf2525ef 100644 --- a/src/test/java/samples/lambdaExpressions/functionalArg.java +++ b/src/test/java/samples/lambdaExpressions/functionalArg.java @@ -1,10 +1,9 @@ package samples.lambdaExpressions; /** - * @author Manuel Benz - * created on 12.07.18 + * @author Manuel Benz created on 12.07.18 */ @FunctionalInterface public interface functionalArg { - void eval(String foo); + void eval(String foo); } diff --git a/src/test/java/samples/lambdaExpressions/functionalRet.java b/src/test/java/samples/lambdaExpressions/functionalRet.java index 53c40199..1b38a7f9 100644 --- a/src/test/java/samples/lambdaExpressions/functionalRet.java +++ b/src/test/java/samples/lambdaExpressions/functionalRet.java @@ -2,5 +2,5 @@ @FunctionalInterface interface functionalRet { - String eval(); + String eval(); } \ No newline at end of file diff --git a/src/test/java/samples/lambdaExpressions/functionalVoid.java b/src/test/java/samples/lambdaExpressions/functionalVoid.java index 4507d3cb..757ccb8d 100644 --- a/src/test/java/samples/lambdaExpressions/functionalVoid.java +++ b/src/test/java/samples/lambdaExpressions/functionalVoid.java @@ -1,10 +1,9 @@ package samples.lambdaExpressions; /** - * @author Manuel Benz - * created on 12.07.18 + * @author Manuel Benz created on 12.07.18 */ @FunctionalInterface public interface functionalVoid { - void eval(); + void eval(); } diff --git a/src/test/rascal/lang/jimple/tests/TestLambdaTransformer.rsc b/src/test/rascal/lang/jimple/tests/TestLambdaTransformer.rsc index 401b4846..9de444b5 100644 --- a/src/test/rascal/lang/jimple/tests/TestLambdaTransformer.rsc +++ b/src/test/rascal/lang/jimple/tests/TestLambdaTransformer.rsc @@ -5,22 +5,34 @@ import lang::jimple::decompiler::Decompiler; import lang::jimple::decompiler::jimplify::LambdaTransformer; import lang::jimple::util::JPrettyPrinter; -import List; +import List; import IO; -loc classLocation = |project://JimpleFramework/target/test-classes/samples/SimpleLambdaExpression.class|; +str samplesPath = "JimpleFramework/target/test-classes/samples/"; + +lrel[loc, int] testRelation = [<|project://SimpleLambdaExpression.class|, 2>, // 0: Simple Lambda + <|project:///arrays/ArrayExample.class|, 3>, // 1: Array Lambda + <|project:///lambdaExpressions/AddLambda.class|, 2>, // 2: Interface Lambda + <|project:///lambdaExpressions/Palindromes.class|, 4>, // 3: Palindrome Lambda + <|project:///lambdaExpressions/Runners.class|, 7>, // 4: Multiple Runnable Lambdas + <|project:///lambdaExpressions/SumList.class|, 2>, // 5: List Lambda + <|project:///lambdaExpressions/IncClosure.class|, 2>]; // 6: Closure test bool testLambdaTransformer() { + + int n = 3; + loc classLocation = testRelation[n][0]; + ClassOrInterfaceDeclaration c = decompile(classLocation); println(prettyPrint(c)); list[ClassOrInterfaceDeclaration] classes = lambdaTransformer(c); - for(ClassOrInterfaceDeclaration aClass <- classes) { println(prettyPrint(aClass)); + //println(aClass); //abstract syntax tree } - return size(classes) == 1; + return size(classes) == testRelation[n][1]; } \ No newline at end of file