Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.robovm.compiler.plugin.lambda.LambdaPlugin Maven / Gradle / Ivy
package org.robovm.compiler.plugin.lambda;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.robovm.compiler.CompilerException;
import org.robovm.compiler.ModuleBuilder;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.plugin.AbstractCompilerPlugin;
import org.robovm.compiler.plugin.lambda.java.lang.invoke.LambdaConversionException;
import org.robovm.compiler.plugin.lambda.java.lang.invoke.LambdaMetafactory;
import soot.Body;
import soot.Local;
import soot.Modifier;
import soot.PatchingChain;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.SootMethodHandle;
import soot.SootMethodRef;
import soot.SootMethodType;
import soot.SootResolver;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.jimple.DefinitionStmt;
import soot.jimple.DynamicInvokeExpr;
import soot.jimple.IntConstant;
import soot.jimple.Jimple;
import soot.jimple.NullConstant;
public class LambdaPlugin extends AbstractCompilerPlugin {
private static boolean isLambdaBootstrapMethod(SootMethodRef methodRef) {
return methodRef.declaringClass().getName().equals("java.lang.invoke.LambdaMetafactory")
&& (methodRef.name().equals("metafactory") || methodRef.name().equals("altMetafactory"));
}
private static SMethodType toSMethodType(SootMethodRef ref) {
return new SMethodType(SootSClass.forType(ref.returnType()), SootSClass.forTypes(ref.parameterTypes()));
}
private static SMethodType toSMethodType(SootMethodType t) {
return new SMethodType(SootSClass.forType(t.getReturnType()), SootSClass.forTypes(t.getParameterTypes()));
}
private static SMethodHandle toSMethodHandle(SootMethodHandle h) {
return new SMethodHandle(
toSMethodType(h.getMethodType()),
new SMethodHandleInfo(
SootSClass.forType(h.getMethodRef().declaringClass().getType()),
h.getMethodRef().name(),
toSMethodType(h.getMethodRef()), h.getReferenceKind()));
}
private static soot.Type toSootType(SClass> type) {
if (type instanceof SootSClass) {
return ((SootSClass) type).type;
}
SootSClassLookup lookup = (SootSClassLookup) SClass.getLookup();
return ((SootSClass) lookup.lookup(type.getDescriptor())).type;
}
private static List toSootTypes(List> types) {
List result = new ArrayList<>();
for (SClass> type : types) {
result.add(toSootType(type));
}
return result;
}
@Override
public void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) throws IOException {
SootClass sootClass = clazz.getSootClass();
for (SootMethod method : sootClass.getMethods()) {
transformMethod(config, clazz, sootClass, method, moduleBuilder);
}
}
private void transformMethod(Config config, Clazz clazz, SootClass sootClass,
SootMethod method, ModuleBuilder moduleBuilder) throws IOException {
if (!method.isConcrete()) {
return;
}
SClass.setLookup(new SootSClassLookup());
int tmpCounter = 0;
Body body = method.retrieveActiveBody();
PatchingChain units = body.getUnits();
for (Unit unit = units.getFirst(); unit != null; unit = body.getUnits().getSuccOf(unit)) {
if (unit instanceof DefinitionStmt) {
if (((DefinitionStmt) unit).getRightOp() instanceof DynamicInvokeExpr) {
DynamicInvokeExpr expr = (DynamicInvokeExpr) ((DefinitionStmt) unit).getRightOp();
if (isLambdaBootstrapMethod(expr.getBootstrapMethodRef())) {
List bsmArgs = expr.getBootstrapArgs();
SMethodHandles.Lookup caller = new SMethodHandles.Lookup(SootSClass.forType(sootClass.getType()));
String invokedName = expr.getMethodRef().name();
SMethodType invokedType = toSMethodType(expr.getMethodRef());
SMethodType samMethodType = toSMethodType((SootMethodType) bsmArgs.get(0));
SMethodHandle implMethod = toSMethodHandle((SootMethodHandle) bsmArgs.get(1));
SMethodType instantiatedMethodType = toSMethodType((SootMethodType) bsmArgs.get(2));
try {
SCallSite callSite = null;
if (expr.getBootstrapMethodRef().name().equals("altMetafactory")) {
callSite = altMetafactory(caller, invokedName, invokedType, samMethodType, implMethod,
instantiatedMethodType, bsmArgs);
} else {
callSite = LambdaMetafactory.metafactory(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
}
File f = clazz.getPath().getGeneratedClassFile(callSite.getLambdaClassName());
FileUtils.writeByteArrayToFile(f, callSite.getClassData());
// The lambda class is created after the caller is compiled.
// This prevents the triggering of a recompile of the caller.
f.setLastModified(clazz.lastModified());
SootClass lambdaClass = SootResolver.v().makeClassRef(callSite.getLambdaClassName().replace('/', '.'));
Local l = (Local) ((DefinitionStmt) unit).getLeftOp();
Type samType = toSootType(callSite.getTargetMethodType().returnType());
LinkedList newUnits = new LinkedList<>();
if (callSite.getTargetMethodName().equals("")) {
// Constant lambda. Create an instance once and reuse for
// every call.
String fieldName = lambdaClass.getName().substring(lambdaClass.getName().lastIndexOf('.') + 1);
SootField field = new SootField(fieldName, lambdaClass.getType(),
Modifier.STATIC | Modifier.PRIVATE | Modifier.TRANSIENT | 0x1000 /*SYNTHETIC*/);
method.getDeclaringClass().addField(field);
// l = LambdaClass.lambdaField
newUnits.add(Jimple.v().newAssignStmt(l, Jimple.v().newStaticFieldRef(field.makeRef())));
// if l != null goto succOfInvokedynamic
newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(l, NullConstant.v()), units.getSuccOf(unit)));
// $tmpX = new LambdaClass()
Local tmp = Jimple.v().newLocal("$tmp" + (tmpCounter++), lambdaClass.getType());
body.getLocals().add(tmp);
newUnits.add(Jimple.v().newAssignStmt(tmp, Jimple.v().newNewExpr(lambdaClass.getType())));
newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newSpecialInvokeExpr(tmp, Scene.v().makeConstructorRef(lambdaClass, Collections.emptyList()))));
// LambdaClass.lambdaField = $tmpX
newUnits.add(Jimple.v().newAssignStmt(Jimple.v().newStaticFieldRef(field.makeRef()), tmp));
// l = $tmpX
newUnits.add(Jimple.v().newAssignStmt(l, tmp));
} else {
// Static factory method returns the lambda to use.
newUnits.add(Jimple.v().newAssignStmt(l,
Jimple.v().newStaticInvokeExpr(
Scene.v().makeMethodRef(lambdaClass,
callSite.getTargetMethodName(),
toSootTypes(callSite.getTargetMethodType().parameterList()),
samType, true),
expr.getArgs())));
}
units.insertAfter(newUnits, unit);
units.remove(unit);
unit = newUnits.getLast();
} catch (LambdaConversionException e) {
// TODO: Change the jimple of the method to throw a
// LambdaConversionException at runtime.
throw new CompilerException(e);
}
}
}
}
}
}
private SCallSite altMetafactory(SMethodHandles.Lookup caller, String invokedName, SMethodType invokedType,
SMethodType samMethodType, SMethodHandle implMethod, SMethodType instantiatedMethodType, List bsmArgs)
throws LambdaConversionException {
int flags = ((IntConstant) bsmArgs.get(3)).value;
List args = new ArrayList<>();
args.add(samMethodType);
args.add(implMethod);
args.add(instantiatedMethodType);
args.add(flags);
int bsmArgsIdx = 4;
if ((flags & LambdaMetafactory.FLAG_MARKERS) > 0) {
int count = ((IntConstant) bsmArgs.get(bsmArgsIdx++)).value;
args.add(count);
for (int i = 0; i < count; i++) {
args.add(SootSClass.forType((soot.Type) bsmArgs.get(bsmArgsIdx++)));
}
}
if ((flags & LambdaMetafactory.FLAG_BRIDGES) > 0) {
int count = ((IntConstant) bsmArgs.get(bsmArgsIdx++)).value;
args.add(count);
for (int i = 0; i < count; i++) {
args.add(toSMethodType((SootMethodType) bsmArgs.get(bsmArgsIdx++)));
}
}
return LambdaMetafactory.altMetafactory(caller, invokedName, invokedType, args.toArray());
}
}