Skip to content

Commit

Permalink
Add 'LambdaMetaFactory' (maybe this should be renamed) which implemen…
Browse files Browse the repository at this point in the history
…ts the

'modeling' of java.lang.invoke.LambdaMetaFactory.

We model this by making a real 'thunk' class which implements the functional
interface of the lambda and dispatches to the real implementation. This is
sort of like what RetroLambda does.

This isn't wired up to the rest of soot yet.
  • Loading branch information
balliet-gt committed Feb 28, 2017
1 parent 0372025 commit 027281f
Showing 1 changed file with 222 additions and 0 deletions.
222 changes: 222 additions & 0 deletions src/soot/LambdaMetaFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/* Soot - a J*va Optimization Framework
* Copyright (C) 1997-2014 Raja Vallee-Rai and others
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot;

import soot.jimple.*;
import soot.coffi.Util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public final class LambdaMetaFactory {
public static SootMethodRef makeLambdaHelper(
List<? extends Value> bootstrapArgs,
int tag,
String name,
Type[] types) {
if(bootstrapArgs.size() != 3 ||
!(bootstrapArgs.get(0) instanceof ClassConstant) ||
!(bootstrapArgs.get(1) instanceof MethodHandle) ||
!(bootstrapArgs.get(2) instanceof ClassConstant)) {
G.v().out.println("warning: LambdaMetaFactory: unexpected arguments for LambdaMetaFactor.metaFactory");
return null;
}
String samMethodType = ((ClassConstant) bootstrapArgs.get(0)).getValue();
SootMethodRef implMethod = ((MethodHandle) bootstrapArgs.get(1)).getMethodRef();
String instantiatedMethodType = ((ClassConstant) bootstrapArgs.get(2)).getValue();
if(!samMethodType.equals(instantiatedMethodType)) {
G.v().out.println("warning: LambdaMetaFactory: " +
samMethodType + " != " + instantiatedMethodType);
return null;
}

Type[] samTypes = Util.v().jimpleTypesOfFieldOrMethodDescriptor(samMethodType);
List<Type> paramTypes = Arrays.asList(samTypes).subList(0, samTypes.length - 1);
Type retType = samTypes[samTypes.length - 1];

List<Type> capTypes = Arrays.asList(types).subList(0, types.length - 1);
if(!(types[types.length-1] instanceof RefType)) {
G.v().out.println("unexpected interface type: " + types[types.length-1]);
return null;
}
SootClass iface = ((RefType) types[types.length-1]).getSootClass();

// Our thunk class implements the functional interface
String className = "soot.dummy." + implMethod.name() + "$" + uniqSupply();
SootClass tclass = new SootClass(className);
tclass.addInterface(iface);

// It contains fields for all the captures in the lambda
List<SootField> capFields = new ArrayList<SootField>(capTypes.size());
for(int i=0;i<capTypes.size();i++) {
SootField f = new SootField(
"cap" + i,
capTypes.get(i),
0);
capFields.add(f);
tclass.addField(f);
}

MethodSource ms = new ThunkMethodSource(capFields, paramTypes, retType, implMethod);

// Bootstrap method creates a new instance of this class
SootMethod tboot = new SootMethod(
"bootstrap$",
capTypes,
iface.getType(),
Modifier.STATIC
);
tclass.addMethod(tboot);
tboot.setSource(ms);

// Constructor just copies the captures
SootMethod tctor = new SootMethod(
"<init>",
capTypes,
VoidType.v());
tclass.addMethod(tctor);
tctor.setSource(ms);

// Dispatch runs the 'real' method implementing the body of the lambda
SootMethod tdispatch = new SootMethod(
name,
paramTypes,
retType);
tclass.addMethod(tdispatch);
tdispatch.setSource(ms);

Scene.v().addClass(tclass);

System.out.println(tboot);

/*java.io.PrintWriter pw = new java.io.PrintWriter(System.out);
Printer.v().printTo(tclass, pw);
pw.close();*/

return tboot.makeRef();
}

private static class ThunkMethodSource implements MethodSource {
private List<SootField> capFields;
private List<Type> paramTypes;
private Type retType;
private SootMethodRef implMethod;

public ThunkMethodSource(List<SootField> capFields, List<Type> paramTypes, Type retType, SootMethodRef implMethod) {
this.capFields = capFields;
this.paramTypes = paramTypes;
this.retType = retType;
this.implMethod = implMethod;
}

public Body getBody(SootMethod m, String phaseName) {
if(!phaseName.equals("jb"))
throw new Error("unsupported body type: " + phaseName); // FIXME?

SootClass tclass = m.getDeclaringClass();
JimpleBody jb = Jimple.v().newBody(m);
PatchingChain<Unit> us = jb.getUnits();

if(m.getName().equals("<init>")) {
Local l = Jimple.v().newLocal("r", tclass.getType());
us.add(Jimple.v().newIdentityStmt(
l,
Jimple.v().newThisRef(tclass.getType())
));
us.add(Jimple.v().newInvokeStmt(
Jimple.v().newSpecialInvokeExpr(
l,
Scene.v().makeConstructorRef(
Scene.v().getObjectType().getSootClass(), Collections.<Type>emptyList()
),
Collections.<Value>emptyList())
));
for(SootField f : capFields) {
int i = us.size() - 2;
Local l2 = Jimple.v().newLocal("c" + i, f.getType());
us.add(Jimple.v().newIdentityStmt(
l2,
Jimple.v().newParameterRef(f.getType(), i)
));
us.add(Jimple.v().newAssignStmt(
Jimple.v().newInstanceFieldRef(l, f.makeRef()),
l2
));
}
us.add(Jimple.v().newReturnVoidStmt());
} else if(m.getName().equals("bootstrap$")) {
Local l = Jimple.v().newLocal("r", tclass.getType());
Value val = Jimple.v().newNewExpr(tclass.getType());
List<Value> capVals = new ArrayList<Value>();
us.add(Jimple.v().newAssignStmt(l, val));
us.add(Jimple.v().newInvokeStmt(
Jimple.v().newSpecialInvokeExpr(
l,
Scene.v().makeConstructorRef(tclass, Collections.<Type>emptyList()),
Collections.<Value>emptyList())
));
us.add(Jimple.v().newReturnStmt(l));
} else {
Local this_ = Jimple.v().newLocal("r", tclass.getType());
us.add(Jimple.v().newIdentityStmt(
this_,
Jimple.v().newThisRef(tclass.getType())
));

List<Local> args = new ArrayList<Local>();

for(SootField f : capFields) {
int i = args.size();
Local l = Jimple.v().newLocal("c" + i, f.getType());
us.add(Jimple.v().newAssignStmt(
l,
Jimple.v().newInstanceFieldRef(this_, f.makeRef())
));
args.add(l);
}

for(Type ty : paramTypes) {
int i = args.size();
Local l = Jimple.v().newLocal("a" + i, ty);
us.add(Jimple.v().newIdentityStmt(
l,
Jimple.v().newParameterRef(ty, i)
));
args.add(l);
}

Local ret = Jimple.v().newLocal("r", retType);
us.add(Jimple.v().newAssignStmt(
ret,
Jimple.v().newStaticInvokeExpr(implMethod, args)
));
us.add(Jimple.v().newReturnStmt(ret));
}
return jb;
}
}

// FIXME: Move to 'G'?
private static int uniq;
private static synchronized long uniqSupply() {
return ++uniq;
}
}

0 comments on commit 027281f

Please sign in to comment.