Search in sources :

Example 1 with SootClass

use of soot.SootClass 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 2 with SootClass

use of soot.SootClass in project robovm by robovm.

the class ObjCBlockPlugin method getBlockTargetMethod.

protected static SootMethod getBlockTargetMethod(SootMethod method) {
    soot.Type type = method.getReturnType();
    if (!(type instanceof RefType)) {
        throw new CompilerException("@Block annotated return type of method " + method + " must be of interface type");
    }
    SootClass blockType = ((RefType) type).getSootClass();
    if (!blockType.isInterface()) {
        throw new CompilerException("@Block annotated parameter return type " + "of method " + method + " must be of interface type");
    }
    List<SootMethod> allMethods = collectAbstractMethods(blockType);
    if (allMethods.isEmpty()) {
        throw new CompilerException("No abstract method found in interface " + blockType + " used in @Block annotated return type of method " + method);
    }
    if (allMethods.size() > 1) {
        throw new CompilerException("More than 1 abstract method found in interface " + blockType + " used in @Block annotated return type of method " + method);
    }
    return allMethods.get(0);
}
Also used : RefType(soot.RefType) CompilerException(org.robovm.compiler.CompilerException) SootMethod(soot.SootMethod) SootClass(soot.SootClass)

Example 3 with SootClass

use of soot.SootClass in project robovm by robovm.

the class ObjCProtocolProxyPlugin method beforeClass.

@Override
public void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) {
    init();
    SootClass sootClass = clazz.getSootClass();
    if (isObjCProtocol(sootClass)) {
        try {
            String proxyInternalName = clazz.getInternalName() + PROXY_CLASS_NAME_SUFFIX;
            ArrayList<String> interfazes = new ArrayList<>();
            collectProxyInterfaceInternalNames(sootClass, interfazes);
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            cw.visit(51, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC + ACC_PUBLIC, proxyInternalName, null, getProxySuperclassInternalName(sootClass), new String[] { clazz.getInternalName() });
            generateProxyMethods(config, interfazes, cw);
            cw.visitEnd();
            File f = clazz.getPath().getGeneratedClassFile(proxyInternalName);
            FileUtils.writeByteArrayToFile(f, cw.toByteArray());
            // The proxy class is created after the interface is compiled.
            // This prevents the triggering of a recompile of the interface.
            f.setLastModified(clazz.lastModified());
            // Add the proxy class as a dependency for the protocol interface.
            // Important! This must be done AFTER the class file has been written.
            clazz.getClazzInfo().addClassDependency(proxyInternalName, false);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
Also used : ArrayList(java.util.ArrayList) IOException(java.io.IOException) SootClass(soot.SootClass) File(java.io.File) ClassWriter(org.objectweb.asm.ClassWriter)

Example 4 with SootClass

use of soot.SootClass in project robovm by robovm.

the class ObjCBlockPlugin method beforeClass.

@Override
public void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) throws IOException {
    init();
    SootClass sootClass = clazz.getSootClass();
    if (!sootClass.isInterface()) {
        Map<String, Integer> blockTypeIds = new HashMap<>();
        for (SootMethod method : sootClass.getMethods()) {
            if (method.isNative() && hasBridgeAnnotation(method) || hasCallbackAnnotation(method)) {
                int[] indexes = getBlockParameterIndexes(method);
                if (indexes != null || hasAnnotation(method, BLOCK)) {
                    transformMethod(config, clazz, method, indexes, blockTypeIds);
                }
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) SootMethod(soot.SootMethod) SootClass(soot.SootClass)

Example 5 with SootClass

use of soot.SootClass in project robovm by robovm.

the class ObjCBlockPlugin method getBlockTargetMethod.

protected static SootMethod getBlockTargetMethod(SootMethod method, int paramIndex) {
    soot.Type type = method.getParameterType(paramIndex);
    if (!(type instanceof RefType)) {
        throw new CompilerException("@Block annotated parameter " + (paramIndex + 1) + " of method " + method + " must be of interface type");
    }
    SootClass blockType = ((RefType) type).getSootClass();
    if (!blockType.isInterface()) {
        throw new CompilerException("@Block annotated parameter " + (paramIndex + 1) + " of method " + method + " must be of interface type");
    }
    List<SootMethod> allMethods = collectAbstractMethods(blockType);
    if (allMethods.isEmpty()) {
        throw new CompilerException("No abstract method found in interface " + blockType + " used in @Block annotated parameter " + (paramIndex + 1) + " of method " + method);
    }
    if (allMethods.size() > 1) {
        throw new CompilerException("More than 1 abstract method found in interface " + blockType + " used in @Block annotated parameter " + (paramIndex + 1) + " of method " + method);
    }
    return allMethods.get(0);
}
Also used : RefType(soot.RefType) CompilerException(org.robovm.compiler.CompilerException) SootMethod(soot.SootMethod) SootClass(soot.SootClass)

Aggregations

SootClass (soot.SootClass)194 SootMethod (soot.SootMethod)99 RefType (soot.RefType)69 ArrayList (java.util.ArrayList)60 Type (soot.Type)57 VoidType (soot.VoidType)33 ArrayType (soot.ArrayType)32 Iterator (java.util.Iterator)29 BooleanType (soot.BooleanType)29 DoubleType (soot.DoubleType)29 LongType (soot.LongType)29 Value (soot.Value)29 FloatType (soot.FloatType)28 Local (soot.Local)27 SootField (soot.SootField)27 List (java.util.List)26 CharType (soot.CharType)26 IntType (soot.IntType)26 ByteType (soot.ByteType)25 PrimType (soot.PrimType)23