Search in sources :

Example 1 with SootMethodType

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);
    }
}
Also used : RefType(soot.RefType) DoubleType(soot.DoubleType) FloatType(soot.FloatType) Type(soot.Type) PrimType(soot.PrimType) VoidType(soot.VoidType) LongType(soot.LongType) SootMethodType(soot.SootMethodType)

Example 2 with SootMethodType

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);
                    }
                }
            }
        }
    }
}
Also used : SootMethodHandle(soot.SootMethodHandle) ArrayList(java.util.ArrayList) Unit(soot.Unit) IntConstant(soot.jimple.IntConstant) CompilerException(org.robovm.compiler.CompilerException) Body(soot.Body) SootMethodRef(soot.SootMethodRef) SootMethodType(soot.SootMethodType) Local(soot.Local) SootClass(soot.SootClass) LinkedList(java.util.LinkedList) RefType(soot.RefType) SootMethodType(soot.SootMethodType) Type(soot.Type) Value(soot.Value) SootMethod(soot.SootMethod) SootField(soot.SootField) DynamicInvokeExpr(soot.jimple.DynamicInvokeExpr) DefinitionStmt(soot.jimple.DefinitionStmt) File(java.io.File) ClassConstant(soot.jimple.ClassConstant)

Example 3 with SootMethodType

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();
}
Also used : RefType(soot.RefType) DoubleType(soot.DoubleType) FloatType(soot.FloatType) Type(soot.Type) PrimType(soot.PrimType) VoidType(soot.VoidType) LongType(soot.LongType) SootMethodType(soot.SootMethodType) MethodVisitor(org.objectweb.asm.MethodVisitor)

Example 4 with SootMethodType

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();
}
Also used : RefType(soot.RefType) DoubleType(soot.DoubleType) FloatType(soot.FloatType) Type(soot.Type) PrimType(soot.PrimType) VoidType(soot.VoidType) LongType(soot.LongType) SootMethodType(soot.SootMethodType) ArrayList(java.util.ArrayList) CompilerException(org.robovm.compiler.CompilerException) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) MethodVisitor(org.objectweb.asm.MethodVisitor)

Example 5 with SootMethodType

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";
}
Also used : RefType(soot.RefType) DoubleType(soot.DoubleType) FloatType(soot.FloatType) Type(soot.Type) PrimType(soot.PrimType) VoidType(soot.VoidType) LongType(soot.LongType) SootMethodType(soot.SootMethodType) MethodVisitor(org.objectweb.asm.MethodVisitor)

Aggregations

RefType (soot.RefType)6 SootMethodType (soot.SootMethodType)6 Type (soot.Type)6 DoubleType (soot.DoubleType)5 FloatType (soot.FloatType)5 LongType (soot.LongType)5 PrimType (soot.PrimType)5 VoidType (soot.VoidType)5 ArrayList (java.util.ArrayList)3 MethodVisitor (org.objectweb.asm.MethodVisitor)3 CompilerException (org.robovm.compiler.CompilerException)2 File (java.io.File)1 LinkedList (java.util.LinkedList)1 ClassWriter (org.objectweb.asm.ClassWriter)1 GeneratorAdapter (org.objectweb.asm.commons.GeneratorAdapter)1 Body (soot.Body)1 Local (soot.Local)1 SootClass (soot.SootClass)1 SootField (soot.SootField)1 SootMethod (soot.SootMethod)1