Search in sources :

Example 1 with Value

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

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

Example 3 with Value

use of soot.Value in project robovm by robovm.

the class ObjCMemberPlugin method createBridge.

private void createBridge(SootClass sootClass, SootMethod method, String selectorName, boolean strongRefSetter, boolean extensions) {
    Jimple j = Jimple.v();
    boolean usingGenericInstanceMethod = false;
    SootMethod msgSendMethod = getMsgSendMethod(selectorName, method, extensions);
    /*
         * Add the method even if we might remove it later to make marshaler
         * lookup on the method work as expected.
         */
    sootClass.addMethod(msgSendMethod);
    addBridgeAnnotation(msgSendMethod);
    SootMethodRef msgSendMethodRef = getGenericMsgSendReplacementMethod(msgSendMethod);
    if (!msgSendMethodRef.declaringClass().getType().equals(msgSendMethod.getDeclaringClass().getType())) {
        /*
             * There's a generic objc_msgSend method we can use. Remove
             * msgSendMethod from the class.
             */
        sootClass.removeMethod(msgSendMethod);
        /*
             * Can we use a generic <name>_instance method from $M? If we can we
             * won't have to make a call to objc_msgSendSuper.
             */
        if (!method.isStatic()) {
            // Yes!
            msgSendMethodRef = Scene.v().makeMethodRef(msgSendMethodRef.declaringClass(), msgSendMethodRef.name() + "_instance", msgSendMethodRef.parameterTypes(), msgSendMethodRef.returnType(), true);
            usingGenericInstanceMethod = true;
        }
    }
    SootMethodRef msgSendSuperMethodRef = null;
    if (!usingGenericInstanceMethod && !extensions && !method.isStatic()) {
        SootMethod msgSendSuperMethod = getMsgSendSuperMethod(selectorName, method);
        /*
             * Add the method even if we might remove it later to make marshaler
             * lookup on the method work as expected.
             */
        sootClass.addMethod(msgSendSuperMethod);
        addBridgeAnnotation(msgSendSuperMethod);
        msgSendSuperMethodRef = getGenericMsgSendSuperReplacementMethod(msgSendSuperMethod);
        if (!msgSendSuperMethodRef.declaringClass().getType().equals(msgSendSuperMethod.getDeclaringClass().getType())) {
            /*
                 * There's a generic objc_msgSendSuper method we can use. Remove
                 * msgSendSuperMethod from the class.
                 */
            sootClass.removeMethod(msgSendSuperMethod);
        }
    }
    method.setModifiers(method.getModifiers() & ~NATIVE);
    Body body = j.newBody(method);
    method.setActiveBody(body);
    PatchingChain<Unit> units = body.getUnits();
    Local thiz = null;
    if (extensions) {
        thiz = j.newLocal("$this", method.getParameterType(0));
        body.getLocals().add(thiz);
        units.add(j.newIdentityStmt(thiz, j.newParameterRef(method.getParameterType(0), 0)));
    } else if (!method.isStatic()) {
        thiz = j.newLocal("$this", sootClass.getType());
        body.getLocals().add(thiz);
        units.add(j.newIdentityStmt(thiz, j.newThisRef(sootClass.getType())));
    }
    LinkedList<Value> args = new LinkedList<>();
    for (int i = extensions ? 1 : 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)));
        args.add(p);
    }
    Local objCClass = null;
    if (!extensions && method.isStatic()) {
        objCClass = j.newLocal("$objCClass", org_robovm_objc_ObjCClass.getType());
        body.getLocals().add(objCClass);
        units.add(j.newAssignStmt(objCClass, j.newStaticFieldRef(Scene.v().makeFieldRef(sootClass, "$objCClass", org_robovm_objc_ObjCClass.getType(), true))));
    }
    if (strongRefSetter) {
        Type propType = method.getParameterType(extensions ? 1 : 0);
        if (propType instanceof RefLikeType) {
            SootMethodRef getter = findStrongRefGetter(sootClass, method, extensions).makeRef();
            Local before = j.newLocal("$before", propType);
            body.getLocals().add(before);
            units.add(j.newAssignStmt(before, extensions ? j.newStaticInvokeExpr(getter, thiz) : (objCClass != null ? j.newStaticInvokeExpr(getter) : j.newVirtualInvokeExpr(thiz, getter))));
            Value after = args.get(0);
            if (extensions) {
                units.add(j.newInvokeStmt(j.newStaticInvokeExpr(org_robovm_objc_ObjCExtensions_updateStrongRef, Arrays.asList(thiz, before, after))));
            } else {
                units.add(j.newInvokeStmt(j.newVirtualInvokeExpr(objCClass != null ? objCClass : thiz, org_robovm_objc_ObjCObject_updateStrongRef, before, after)));
            }
        }
    }
    Local sel = j.newLocal("$sel", org_robovm_objc_Selector.getType());
    body.getLocals().add(sel);
    // $sel = <selector>
    units.add(j.newAssignStmt(sel, j.newStaticFieldRef(Scene.v().makeFieldRef(sootClass, getSelectorFieldName(selectorName), org_robovm_objc_Selector.getType(), true))));
    args.addFirst(sel);
    Local customClass = null;
    if (!usingGenericInstanceMethod && !extensions && !Modifier.isFinal(sootClass.getModifiers()) && !method.isStatic()) {
        customClass = j.newLocal("$customClass", BooleanType.v());
        body.getLocals().add(customClass);
        units.add(j.newAssignStmt(customClass, j.newInstanceFieldRef(thiz, org_robovm_objc_ObjCObject_customClass)));
    }
    Local ret = null;
    if (method.getReturnType() != VoidType.v()) {
        ret = j.newLocal("$ret", msgSendMethodRef.returnType());
        body.getLocals().add(ret);
    }
    Local castRet = null;
    if (!msgSendMethodRef.returnType().equals(method.getReturnType())) {
        /*
             * We're calling a generic method in $M which returns an NSObject.
             * We need to cast that to the return type declared by the method
             * being generated.
             */
        castRet = j.newLocal("$castRet", method.getReturnType());
        body.getLocals().add(castRet);
    }
    StaticInvokeExpr invokeMsgSendExpr = j.newStaticInvokeExpr(msgSendMethodRef, l(thiz != null ? thiz : objCClass, args));
    Stmt invokeMsgSendStmt = ret == null ? j.newInvokeStmt(invokeMsgSendExpr) : j.newAssignStmt(ret, invokeMsgSendExpr);
    if (customClass != null) {
        // if $customClass == 0 goto <invokeMsgSendStmt>
        units.add(j.newIfStmt(j.newEqExpr(customClass, IntConstant.v(0)), invokeMsgSendStmt));
        // $super = this.getSuper()
        Local zuper = j.newLocal("$super", org_robovm_objc_ObjCSuper.getType());
        body.getLocals().add(zuper);
        units.add(j.newAssignStmt(zuper, j.newVirtualInvokeExpr(body.getThisLocal(), org_robovm_objc_ObjCObject_getSuper)));
        StaticInvokeExpr invokeMsgSendSuperExpr = j.newStaticInvokeExpr(msgSendSuperMethodRef, l(zuper, args));
        units.add(ret == null ? j.newInvokeStmt(invokeMsgSendSuperExpr) : j.newAssignStmt(ret, invokeMsgSendSuperExpr));
        if (ret != null) {
            if (castRet != null) {
                units.add(j.newAssignStmt(castRet, j.newCastExpr(ret, castRet.getType())));
                units.add(j.newReturnStmt(castRet));
            } else {
                units.add(j.newReturnStmt(ret));
            }
        } else {
            units.add(j.newReturnVoidStmt());
        }
    }
    units.add(invokeMsgSendStmt);
    if (ret != null) {
        if (castRet != null) {
            units.add(j.newAssignStmt(castRet, j.newCastExpr(ret, castRet.getType())));
            units.add(j.newReturnStmt(castRet));
        } else {
            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) InvokeStmt(soot.jimple.InvokeStmt) Stmt(soot.jimple.Stmt) RefLikeType(soot.RefLikeType) StaticInvokeExpr(soot.jimple.StaticInvokeExpr) 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) Value(soot.Value) SootMethod(soot.SootMethod) Jimple(soot.jimple.Jimple) Body(soot.Body)

Aggregations

LinkedList (java.util.LinkedList)3 Body (soot.Body)3 Local (soot.Local)3 RefType (soot.RefType)3 SootMethod (soot.SootMethod)3 SootMethodRef (soot.SootMethodRef)3 Type (soot.Type)3 Unit (soot.Unit)3 Value (soot.Value)3 CompilerException (org.robovm.compiler.CompilerException)2 SootMethodType (org.robovm.compiler.util.generic.SootMethodType)2 BooleanType (soot.BooleanType)2 DoubleType (soot.DoubleType)2 FloatType (soot.FloatType)2 LongType (soot.LongType)2 PrimType (soot.PrimType)2 RefLikeType (soot.RefLikeType)2 VoidType (soot.VoidType)2 Jimple (soot.jimple.Jimple)2 StaticInvokeExpr (soot.jimple.StaticInvokeExpr)2