Search in sources :

Example 1 with DynamicInvokeExpr

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

Aggregations

File (java.io.File)1 ArrayList (java.util.ArrayList)1 LinkedList (java.util.LinkedList)1 CompilerException (org.robovm.compiler.CompilerException)1 Body (soot.Body)1 Local (soot.Local)1 RefType (soot.RefType)1 SootClass (soot.SootClass)1 SootField (soot.SootField)1 SootMethod (soot.SootMethod)1 SootMethodHandle (soot.SootMethodHandle)1 SootMethodRef (soot.SootMethodRef)1 SootMethodType (soot.SootMethodType)1 Type (soot.Type)1 Unit (soot.Unit)1 Value (soot.Value)1 ClassConstant (soot.jimple.ClassConstant)1 DefinitionStmt (soot.jimple.DefinitionStmt)1 DynamicInvokeExpr (soot.jimple.DynamicInvokeExpr)1 IntConstant (soot.jimple.IntConstant)1