use of soot.SootMethodType in project robovm by robovm.
the class LambdaClassGenerator method pushArguments.
private void pushArguments(SootClass caller, String lambdaClassName, MethodVisitor mv, GeneratorAdapter caster, List<Type> parameters, List<Type> invokedParameters, SootMethodHandle implMethod, SootMethodType instantiatedMethodType, boolean isInstanceMethod) {
// create the object itself
if (implMethod.getReferenceKind() == SootMethodHandle.REF_newInvokeSpecial) {
mv.visitTypeInsn(NEW, implMethod.getMethodRef().declaringClass().getName().replace('.', '/'));
mv.visitInsn(DUP);
}
// push the captured arguments
for (int i = 0; i < invokedParameters.size(); i++) {
Object obj = invokedParameters.get(i);
Type captureType = (Type) obj;
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, lambdaClassName, "arg$" + (i + 1), Types.getDescriptor(captureType));
}
// push the functional interface parameters
// first check if the parameters include the received, e.g.
// "hello"::contains
// would be called on "hello" and not on the class containing the
// invoke dynamic call. We need to handle that parameter separately as
// it's not part of the method signature of the implementation
boolean paramsContainReceiver = isInstanceMethod & !caller.getName().equals(implMethod.getMethodRef().declaringClass().getName()) && parameters.size() > implMethod.getMethodRef().parameterTypes().size();
int paramsIndex = 0;
// we start at slot index 1, because this occupies
int localIndex = 1;
// slot 0
if (paramsContainReceiver && !parameters.isEmpty()) {
Type param = parameters.get(0);
mv.visitVarInsn(loadOpcodeForType(param), localIndex);
castOrWiden(mv, caster, param, implMethod.getMethodRef().declaringClass().getType());
localIndex += slotsForType(param);
paramsIndex++;
}
int samParamsOffset = implMethod.getMethodRef().parameterTypes().size() - parameters.size() + (paramsContainReceiver ? 1 : 0);
for (int i = 0; paramsIndex < parameters.size(); paramsIndex++, i++) {
Type param = parameters.get(paramsIndex);
mv.visitVarInsn(loadOpcodeForType(param), localIndex);
castOrWiden(mv, caster, param, (Type) implMethod.getMethodRef().parameterTypes().get(samParamsOffset + i));
localIndex += slotsForType(param);
}
}
use of soot.SootMethodType in project robovm by robovm.
the class LambdaPlugin method transformMethod.
private void transformMethod(Config config, Clazz clazz, SootClass sootClass, SootMethod method, ModuleBuilder moduleBuilder) throws IOException {
if (!method.isConcrete()) {
return;
}
int tmpCounter = 0;
Body body = method.retrieveActiveBody();
PatchingChain<Unit> 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())) {
LambdaClassGenerator generator = null;
synchronized (generators) {
generator = generators.get(sootClass);
if (generator == null) {
generator = new LambdaClassGenerator();
generators.put(sootClass, generator);
}
}
List<Value> bsmArgs = expr.getBootstrapArgs();
SootClass caller = sootClass;
String invokedName = expr.getMethodRef().name();
SootMethodRef invokedType = expr.getMethodRef();
SootMethodType samMethodType = (SootMethodType) bsmArgs.get(0);
SootMethodHandle implMethod = (SootMethodHandle) bsmArgs.get(1);
SootMethodType instantiatedMethodType = (SootMethodType) bsmArgs.get(2);
try {
LambdaClass callSite = null;
List<Type> markerInterfaces = new ArrayList<>();
List<SootMethodType> bridgeMethods = new ArrayList<>();
if (expr.getBootstrapMethodRef().name().equals("altMetafactory")) {
int flags = ((IntConstant) bsmArgs.get(3)).value;
int bsmArgsIdx = 4;
if ((flags & FLAG_MARKERS) > 0) {
int count = ((IntConstant) bsmArgs.get(bsmArgsIdx++)).value;
for (int i = 0; i < count; i++) {
Object value = bsmArgs.get(bsmArgsIdx++);
if (value instanceof Type) {
markerInterfaces.add((Type) value);
} else if (value instanceof ClassConstant) {
String className = ((ClassConstant) value).getValue().replace('/', '.');
markerInterfaces.add(SootResolver.v().resolveClass(className, SootClass.HIERARCHY).getType());
}
}
}
if ((flags & FLAG_BRIDGES) > 0) {
int count = ((IntConstant) bsmArgs.get(bsmArgsIdx++)).value;
for (int i = 0; i < count; i++) {
bridgeMethods.add((SootMethodType) bsmArgs.get(bsmArgsIdx++));
}
}
}
// see issue #1087
if (bridgeMethods.size() == 0) {
SootClass targetType = SootResolver.v().resolveClass(invokedType.returnType().toString().replace('/', '.'), SootClass.SIGNATURES);
String samDescriptor = Types.getDescriptor(samMethodType.getParameterTypes(), samMethodType.getReturnType());
for (SootMethod targetTypeMethod : targetType.getMethods()) {
boolean isBridgeMethod = targetTypeMethod.getName().equals(invokedName);
isBridgeMethod &= targetTypeMethod.getName().equals(invokedName);
isBridgeMethod &= targetTypeMethod.getParameterCount() == samMethodType.getParameterTypes().size();
isBridgeMethod &= ((targetTypeMethod.getModifiers() & BRIDGE) != 0);
isBridgeMethod &= ((targetTypeMethod.getModifiers() & SYNTHETIC) != 0);
if (isBridgeMethod) {
String targetTypeMethodDesc = Types.getDescriptor(targetTypeMethod);
if (!targetTypeMethodDesc.equals(samDescriptor)) {
bridgeMethods.add(new BridgeMethodType(targetTypeMethod.getReturnType(), targetTypeMethod.getParameterTypes()));
}
}
}
}
// generate the lambda class
callSite = generator.generate(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType, markerInterfaces, bridgeMethods);
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 = callSite.getTargetMethodReturnType();
LinkedList<Unit> newUnits = new LinkedList<>();
if (callSite.getTargetMethodName().equals("<init>")) {
// 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);
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.<Type>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(), callSite.getTargetMethodParameters(), samType, true), expr.getArgs())));
}
units.insertAfter(newUnits, unit);
units.remove(unit);
unit = newUnits.getLast();
} catch (Throwable e) {
// LambdaConversionException at runtime.
throw new CompilerException(e);
}
}
}
}
}
}
use of soot.SootMethodType in project robovm by robovm.
the class LambdaClassGenerator method createFieldsAndConstructor.
private void createFieldsAndConstructor(String lambdaClassName, ClassWriter cw, SootMethodRef invokedType, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
StringBuffer constructorDescriptor = new StringBuffer();
// create the fields on the class
int i = 0;
for (Object obj : invokedType.parameterTypes()) {
Type captureType = (Type) obj;
String typeDesc = Types.getDescriptor(captureType);
cw.visitField(ACC_PRIVATE + ACC_FINAL, "arg$" + (i + 1), typeDesc, null, null);
constructorDescriptor.append(typeDesc);
i++;
}
// create constructor
MethodVisitor mv = cw.visitMethod(0, "<init>", "(" + constructorDescriptor.toString() + ")V", null, null);
mv.visitCode();
// calls super
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
// store the captures into the fields
i = 0;
// we start at slot 1, because this occupies slot 0
int localIndex = 1;
for (Object obj : invokedType.parameterTypes()) {
Type captureType = (Type) obj;
// load this for put field
mv.visitVarInsn(ALOAD, 0);
// load capture from argument slot
mv.visitVarInsn(loadOpcodeForType(captureType), localIndex);
localIndex += slotsForType(captureType);
// store the capture into the field
mv.visitFieldInsn(PUTFIELD, lambdaClassName, "arg$" + (i + 1), Types.getDescriptor(captureType));
i++;
}
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
use of soot.SootMethodType in project robovm by robovm.
the class LambdaClassGenerator method createForwardingMethod.
private void createForwardingMethod(SootClass caller, String lambdaClassName, ClassWriter cw, String name, List<Type> parameters, Type returnType, List<Type> invokedParameters, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType, boolean isBridgeMethod) {
String descriptor = Types.getDescriptor(parameters, returnType);
String implClassName = implMethod.getMethodRef().declaringClass().getName().replace('.', '/');
int accessFlags = ACC_PUBLIC | (isBridgeMethod ? ACC_BRIDGE : 0);
MethodVisitor mv = cw.visitMethod(accessFlags, name, descriptor, null, null);
mv.visitCode();
// figure out the invoke op code for the lambda implementation
// as well as if it's an instance method.
int invokeOpCode = INVOKESTATIC;
boolean isInstanceMethod = false;
switch(implMethod.getReferenceKind()) {
case SootMethodHandle.REF_invokeInterface:
invokeOpCode = INVOKEINTERFACE;
isInstanceMethod = true;
break;
case SootMethodHandle.REF_invokeSpecial:
invokeOpCode = INVOKESPECIAL;
isInstanceMethod = true;
break;
case SootMethodHandle.REF_newInvokeSpecial:
invokeOpCode = INVOKESPECIAL;
break;
case SootMethodHandle.REF_invokeStatic:
invokeOpCode = INVOKESTATIC;
break;
case SootMethodHandle.REF_invokeVirtual:
invokeOpCode = INVOKEVIRTUAL;
isInstanceMethod = true;
break;
default:
throw new CompilerException("Unknown invoke type: " + implMethod.getReferenceKind());
}
GeneratorAdapter caster = new GeneratorAdapter(mv, accessFlags, name, descriptor);
// push the arguments
pushArguments(caller, lambdaClassName, mv, caster, parameters, invokedParameters, implMethod, instantiatedMethodType, isInstanceMethod);
// generate a descriptor for the lambda implementation
// to invoke based on the parameters. If the lambda
// is an instance method, we need to remove the first
// parameter for the descriptor generation as it's
// not part of the method signature.
String implDescriptor = null;
List<Type> paramTypes = new ArrayList<Type>(implMethod.getMethodType().getParameterTypes());
if (isInstanceMethod)
paramTypes.remove(0);
implDescriptor = Types.getDescriptor(paramTypes, implMethod.getMethodType().getReturnType());
// call the lambda implementation
mv.visitMethodInsn(invokeOpCode, implClassName, implMethod.getMethodRef().name(), implDescriptor, invokeOpCode == INVOKEINTERFACE);
// emit the return instruction based on the return type
createForwardingMethodReturn(mv, caster, returnType, samMethodType, implMethod, instantiatedMethodType);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
use of soot.SootMethodType in project robovm by robovm.
the class LambdaClassGenerator method createFactory.
private String createFactory(String lambdaClassName, ClassWriter cw, SootMethodRef invokedType, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "get$Lambda", Types.getDescriptor(invokedType.parameterTypes(), invokedType.returnType()), null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, lambdaClassName);
mv.visitInsn(DUP);
int i = 0;
for (Object obj : invokedType.parameterTypes()) {
Type captureType = (Type) obj;
mv.visitVarInsn(loadOpcodeForType(captureType), i);
i += slotsForType(captureType);
}
mv.visitMethodInsn(INVOKESPECIAL, lambdaClassName, "<init>", Types.getDescriptor(invokedType.parameterTypes(), VoidType.v()), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
return "get$Lambda";
}
Aggregations