Search in sources :

Example 1 with SootMethod

use of soot.SootMethod 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 SootMethod

use of soot.SootMethod 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 SootMethod

use of soot.SootMethod in project robovm by robovm.

the class ObjCMemberPlugin method getOrCreateStaticInitializer.

static SootMethod getOrCreateStaticInitializer(SootClass sootClass) {
    for (SootMethod m : sootClass.getMethods()) {
        if ("<clinit>".equals(m.getName())) {
            return m;
        }
    }
    SootMethod m = new SootMethod("<clinit>", Collections.emptyList(), VoidType.v(), STATIC);
    Body body = Jimple.v().newBody(m);
    body.getUnits().add(Jimple.v().newReturnVoidStmt());
    m.setActiveBody(body);
    sootClass.addMethod(m);
    return m;
}
Also used : SootMethod(soot.SootMethod) Body(soot.Body)

Example 4 with SootMethod

use of soot.SootMethod in project robovm by robovm.

the class ObjCMemberPlugin method addObjCClassField.

private void addObjCClassField(SootClass sootClass) {
    Jimple j = Jimple.v();
    SootMethod clinit = getOrCreateStaticInitializer(sootClass);
    Body body = clinit.retrieveActiveBody();
    Local objCClass = Jimple.v().newLocal("$objCClass", org_robovm_objc_ObjCClass.getType());
    body.getLocals().add(objCClass);
    Chain<Unit> units = body.getUnits();
    SootField f = new SootField("$objCClass", org_robovm_objc_ObjCClass.getType(), STATIC | PRIVATE | FINAL);
    sootClass.addField(f);
    units.insertBefore(Arrays.<Unit>asList(j.newAssignStmt(objCClass, j.newStaticInvokeExpr(org_robovm_objc_ObjCClass_getByType, ClassConstant.v(sootClass.getName().replace('.', '/')))), j.newAssignStmt(j.newStaticFieldRef(f.makeRef()), objCClass)), units.getLast());
}
Also used : SootMethod(soot.SootMethod) Local(soot.Local) Jimple(soot.jimple.Jimple) SootField(soot.SootField) Unit(soot.Unit) Body(soot.Body)

Example 5 with SootMethod

use of soot.SootMethod in project robovm by robovm.

the class ObjCMemberPlugin method createCallback.

private void createCallback(SootClass sootClass, SootMethod method, SootMethod annotatedMethod, String selectorName, Type receiverType) {
    Jimple j = Jimple.v();
    SootMethod callbackMethod = getCallbackMethod(selectorName, method, annotatedMethod, receiverType);
    sootClass.addMethod(callbackMethod);
    addCallbackAnnotation(callbackMethod);
    addBindSelectorAnnotation(callbackMethod, selectorName);
    if (!hasAnnotation(annotatedMethod, TYPE_ENCODING) && (isCustomClass(sootClass) || ObjCProtocolProxyPlugin.isObjCProxy(sootClass))) {
        String encoding = generateTypeEncoding(callbackMethod);
        try {
            addTypeEncodingAnnotation(callbackMethod, encoding);
        } catch (IllegalArgumentException e) {
            throw new CompilerException("Failed to determine method type encoding for method " + method + ": " + e.getMessage());
        }
    }
    Body body = j.newBody(callbackMethod);
    callbackMethod.setActiveBody(body);
    PatchingChain<Unit> units = body.getUnits();
    Local thiz = null;
    if (!method.isStatic()) {
        thiz = j.newLocal("$this", receiverType);
        body.getLocals().add(thiz);
        units.add(j.newIdentityStmt(thiz, j.newParameterRef(receiverType, 0)));
    }
    LinkedList<Value> args = new LinkedList<>();
    for (int i = 0; i < method.getParameterCount(); i++) {
        Type t = method.getParameterType(i);
        Local p = j.newLocal("$p" + i, t);
        body.getLocals().add(p);
        units.add(j.newIdentityStmt(p, j.newParameterRef(t, i + 2)));
        args.add(p);
    }
    Local ret = null;
    if (method.getReturnType() != VoidType.v()) {
        ret = j.newLocal("$ret", method.getReturnType());
        body.getLocals().add(ret);
    }
    SootMethodRef targetMethod = method.makeRef();
    if (((RefType) receiverType).getSootClass().isInterface()) {
        @SuppressWarnings("unchecked") List<Type> parameterTypes = method.getParameterTypes();
        targetMethod = Scene.v().makeMethodRef(((RefType) receiverType).getSootClass(), method.getName(), parameterTypes, method.getReturnType(), false);
    }
    InvokeExpr expr = method.isStatic() ? j.newStaticInvokeExpr(targetMethod, args) : (((RefType) receiverType).getSootClass().isInterface() ? j.newInterfaceInvokeExpr(thiz, targetMethod, args) : j.newVirtualInvokeExpr(thiz, targetMethod, args));
    units.add(ret == null ? j.newInvokeStmt(expr) : j.newAssignStmt(ret, expr));
    if (ret != null) {
        units.add(j.newReturnStmt(ret));
    } else {
        units.add(j.newReturnVoidStmt());
    }
}
Also used : SootMethodRef(soot.SootMethodRef) Local(soot.Local) Unit(soot.Unit) LinkedList(java.util.LinkedList) RefType(soot.RefType) RefType(soot.RefType) BooleanType(soot.BooleanType) SootMethodType(org.robovm.compiler.util.generic.SootMethodType) Type(soot.Type) DoubleType(soot.DoubleType) FloatType(soot.FloatType) LongType(soot.LongType) RefLikeType(soot.RefLikeType) PrimType(soot.PrimType) VoidType(soot.VoidType) InvokeExpr(soot.jimple.InvokeExpr) StaticInvokeExpr(soot.jimple.StaticInvokeExpr) Value(soot.Value) SootMethod(soot.SootMethod) CompilerException(org.robovm.compiler.CompilerException) Jimple(soot.jimple.Jimple) Body(soot.Body)

Aggregations

SootMethod (soot.SootMethod)237 SootClass (soot.SootClass)95 RefType (soot.RefType)56 ArrayList (java.util.ArrayList)49 Type (soot.Type)47 Unit (soot.Unit)47 Value (soot.Value)36 Stmt (soot.jimple.Stmt)35 Test (org.junit.Test)34 Local (soot.Local)34 Body (soot.Body)32 VoidType (soot.VoidType)31 PrimType (soot.PrimType)28 SootField (soot.SootField)28 BooleanType (soot.BooleanType)26 Iterator (java.util.Iterator)23 DoubleType (soot.DoubleType)23 FloatType (soot.FloatType)23 LongType (soot.LongType)23 InvokeExpr (soot.jimple.InvokeExpr)23