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
-
-
-
-
- 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
+
+
+
+
+ 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