Search in sources :

Example 1 with DefinitionStmt

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

use of soot.jimple.DefinitionStmt in project robovm by robovm.

the class MethodCompiler method doCompile.

protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) {
    function = createMethodFunction(method);
    moduleBuilder.addFunction(function);
    this.moduleBuilder = moduleBuilder;
    env = function.getParameterRef(0);
    trapsAt = new HashMap<Unit, List<Trap>>();
    Body body = method.retrieveActiveBody();
    NopStmt prependedNop = null;
    if (method.isStatic() && !body.getUnits().getFirst().getBoxesPointingToThis().isEmpty()) {
        // Fix for issue #1. This prevents an NPE in Soot's ArrayBoundsCheckerAnalysis. The NPE
        // occurs for static methods which start with a unit that is the target of some other
        // unit. We work around this by inserting a nop statement as the first unit in such 
        // methods. See http://www.sable.mcgill.ca/listarchives/soot-list/msg01397.html.
        Unit insertionPoint = body.getUnits().getFirst();
        prependedNop = Jimple.v().newNopStmt();
        body.getUnits().getNonPatchingChain().insertBefore(prependedNop, insertionPoint);
    }
    PackManager.v().getPack("jtp").apply(body);
    PackManager.v().getPack("jop").apply(body);
    PackManager.v().getPack("jap").apply(body);
    if (body.getUnits().getFirst() == prependedNop && prependedNop.getBoxesPointingToThis().isEmpty()) {
        // Remove the nop we inserted above to work around the bug in Soot's 
        // ArrayBoundsCheckerAnalysis which has now been run.
        body.getUnits().getNonPatchingChain().removeFirst();
    }
    PatchingChain<Unit> units = body.getUnits();
    Map<Unit, List<Unit>> branchTargets = getBranchTargets(body);
    Map<Unit, Integer> trapHandlers = getTrapHandlers(body);
    Map<Unit, Integer> selChanges = new HashMap<Unit, Integer>();
    int multiANewArrayMaxDims = 0;
    Set<Local> locals = new HashSet<Local>();
    boolean emitCheckStackOverflow = false;
    for (Unit unit : units) {
        if (unit instanceof DefinitionStmt) {
            DefinitionStmt stmt = (DefinitionStmt) unit;
            if (stmt.getLeftOp() instanceof Local) {
                Local local = (Local) stmt.getLeftOp();
                if (!locals.contains(local)) {
                    Type type = getLocalType(local.getType());
                    Alloca alloca = new Alloca(function.newVariable(local.getName(), type), type);
                    alloca.attach(local);
                    function.add(alloca);
                    locals.add(local);
                }
            }
            if (stmt.getRightOp() instanceof NewMultiArrayExpr) {
                NewMultiArrayExpr expr = (NewMultiArrayExpr) stmt.getRightOp();
                multiANewArrayMaxDims = Math.max(multiANewArrayMaxDims, expr.getSizeCount());
            }
            if (stmt.getRightOp() instanceof InvokeExpr) {
                emitCheckStackOverflow = true;
            }
        }
        if (unit instanceof InvokeStmt) {
            emitCheckStackOverflow = true;
        }
    }
    dims = null;
    if (multiANewArrayMaxDims > 0) {
        dims = function.newVariable("dims", new PointerType(new ArrayType(multiANewArrayMaxDims, I32)));
        function.add(new Alloca(dims, new ArrayType(multiANewArrayMaxDims, I32)));
    }
    if (emitCheckStackOverflow) {
        call(CHECK_STACK_OVERFLOW);
    }
    Value trycatchContext = null;
    if (!body.getTraps().isEmpty()) {
        List<List<Trap>> recordedTraps = new ArrayList<List<Trap>>();
        for (Unit unit : units) {
            // Calculate the predecessor units of unit 
            Set<Unit> incoming = new HashSet<Unit>();
            if (units.getFirst() != unit && units.getPredOf(unit).fallsThrough()) {
                incoming.add(units.getPredOf(unit));
            }
            if (branchTargets.keySet().contains(unit)) {
                incoming.addAll(branchTargets.get(unit));
            }
            if (unit == units.getFirst() || trapHandlers.containsKey(unit) || trapsDiffer(unit, incoming)) {
                List<Trap> traps = getTrapsAt(unit);
                if (traps.isEmpty()) {
                    selChanges.put(unit, 0);
                } else {
                    int index = recordedTraps.indexOf(traps);
                    if (index == -1) {
                        index = recordedTraps.size();
                        recordedTraps.add(traps);
                    }
                    selChanges.put(unit, index + 1);
                }
            }
        }
        StructureConstantBuilder landingPadsPtrs = new StructureConstantBuilder();
        for (List<Trap> traps : recordedTraps) {
            StructureConstantBuilder landingPads = new StructureConstantBuilder();
            for (Trap trap : traps) {
                SootClass exClass = trap.getException();
                StructureConstantBuilder landingPad = new StructureConstantBuilder();
                if ("java.lang.Throwable".equals(exClass.getName()) || exClass.isPhantom()) {
                    landingPad.add(new NullConstant(I8_PTR));
                } else {
                    catches.add(getInternalName(exClass));
                    if (exClass == sootClass) {
                        /*
                             * The class being compiled is an exception class
                             * with a catch clause which catches itself. We
                             * cannot reference the info struct directly since
                             * we don't know the type of it and it hasn't been
                             * emitted by ClassCompiler yet. Use the internal
                             * i8* alias instead which ClassCompiler will emit.
                             * See #1007.
                             */
                        landingPad.add(new AliasRef(Symbols.infoStructSymbol(getInternalName(exClass)) + "_i8ptr", I8_PTR));
                    } else {
                        Global g = new Global(Symbols.infoStructSymbol(getInternalName(exClass)), I8_PTR, true);
                        if (!moduleBuilder.hasSymbol(g.getName())) {
                            moduleBuilder.addGlobal(g);
                        }
                        landingPad.add(g.ref());
                    }
                }
                landingPad.add(new IntegerConstant(trapHandlers.get(trap.getHandlerUnit()) + 1));
                landingPads.add(landingPad.build());
            }
            landingPads.add(new StructureConstantBuilder().add(new NullConstant(I8_PTR)).add(new IntegerConstant(0)).build());
            Global g = moduleBuilder.newGlobal(landingPads.build(), true);
            landingPadsPtrs.add(new ConstantBitcast(g.ref(), I8_PTR));
        }
        Global g = moduleBuilder.newGlobal(landingPadsPtrs.build(), true);
        Variable ctx = function.newVariable(TRYCATCH_CONTEXT_PTR);
        Variable bcCtx = function.newVariable(BC_TRYCATCH_CONTEXT_PTR);
        function.add(new Alloca(bcCtx, BC_TRYCATCH_CONTEXT));
        Variable selPtr = function.newVariable(new PointerType(I32));
        function.add(new Getelementptr(selPtr, bcCtx.ref(), 0, 0, 1));
        function.add(new Store(new IntegerConstant(0), selPtr.ref()));
        Variable bcCtxLandingPadsPtr = function.newVariable(I8_PTR_PTR);
        function.add(new Getelementptr(bcCtxLandingPadsPtr, bcCtx.ref(), 0, 1));
        function.add(new Store(new ConstantBitcast(g.ref(), I8_PTR), bcCtxLandingPadsPtr.ref()));
        function.add(new Bitcast(ctx, bcCtx.ref(), TRYCATCH_CONTEXT_PTR));
        trycatchContext = ctx.ref();
        Value result = call(RVM_TRYCATCH_ENTER, env, trycatchContext);
        Map<IntegerConstant, BasicBlockRef> alt = new TreeMap<IntegerConstant, BasicBlockRef>();
        for (Entry<Unit, Integer> entry : trapHandlers.entrySet()) {
            alt.put(new IntegerConstant(entry.getValue() + 1), function.newBasicBlockRef(new Label(entry.getKey())));
        }
        function.add(new Switch(result, function.newBasicBlockRef(new Label(units.getFirst())), alt));
        if (!branchTargets.containsKey(units.getFirst())) {
            function.newBasicBlock(new Label(units.getFirst()));
        }
    }
    if ("<clinit>".equals(method.getName())) {
        initializeClassFields();
    }
    for (Unit unit : units) {
        if (branchTargets.containsKey(unit) || trapHandlers.containsKey(unit)) {
            BasicBlock oldBlock = function.getCurrentBasicBlock();
            function.newBasicBlock(new Label(unit));
            if (oldBlock != null) {
                Instruction last = oldBlock.last();
                if (last == null || !isTerminator(last)) {
                    oldBlock.add(new Br(function.newBasicBlockRef(new Label(unit))));
                }
            }
        }
        if (selChanges.containsKey(unit)) {
            int sel = selChanges.get(unit);
            // trycatchContext->sel = sel
            Variable selPtr = function.newVariable(new PointerType(I32));
            function.add(new Getelementptr(selPtr, trycatchContext, 0, 1)).attach(unit);
            function.add(new Store(new IntegerConstant(sel), selPtr.ref())).attach(unit);
        }
        if (unit instanceof DefinitionStmt) {
            assign((DefinitionStmt) unit);
        } else if (unit instanceof ReturnStmt) {
            if (!body.getTraps().isEmpty()) {
                trycatchLeave(function);
            }
            return_((ReturnStmt) unit);
        } else if (unit instanceof ReturnVoidStmt) {
            if (!body.getTraps().isEmpty()) {
                trycatchLeave(function);
            }
            returnVoid((ReturnVoidStmt) unit);
        } else if (unit instanceof IfStmt) {
            if_((IfStmt) unit);
        } else if (unit instanceof LookupSwitchStmt) {
            lookupSwitch((LookupSwitchStmt) unit);
        } else if (unit instanceof TableSwitchStmt) {
            tableSwitch((TableSwitchStmt) unit);
        } else if (unit instanceof GotoStmt) {
            goto_((GotoStmt) unit);
        } else if (unit instanceof ThrowStmt) {
            throw_((ThrowStmt) unit);
        } else if (unit instanceof InvokeStmt) {
            invoke((InvokeStmt) unit);
        } else if (unit instanceof EnterMonitorStmt) {
            enterMonitor((EnterMonitorStmt) unit);
        } else if (unit instanceof ExitMonitorStmt) {
            exitMonitor((ExitMonitorStmt) unit);
        } else if (unit instanceof NopStmt) {
            nop((NopStmt) unit);
        } else {
            throw new IllegalArgumentException("Unknown Unit type: " + unit.getClass());
        }
    }
    if (this.className.equals("java/lang/Object") && "<init>".equals(method.getName())) {
        // If it is the object will be registered for finalization.
        for (BasicBlock bb : function.getBasicBlocks()) {
            if (bb.last() instanceof Ret) {
                // Insert a call to register_finalizable() before this ret
                Call call = new Call(REGISTER_FINALIZABLE, env, function.getParameterRef(1));
                call.attach(bb.last().getAttachment(Unit.class));
                bb.insertBefore(bb.last(), call);
            }
        }
    }
    return function;
}
Also used : Ret(org.robovm.compiler.llvm.Ret) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Label(org.robovm.compiler.llvm.Label) Store(org.robovm.compiler.llvm.Store) Unit(soot.Unit) Instruction(org.robovm.compiler.llvm.Instruction) Global(org.robovm.compiler.llvm.Global) ArrayType(org.robovm.compiler.llvm.ArrayType) TableSwitchStmt(soot.jimple.TableSwitchStmt) InterfaceInvokeExpr(soot.jimple.InterfaceInvokeExpr) SpecialInvokeExpr(soot.jimple.SpecialInvokeExpr) InstanceInvokeExpr(soot.jimple.InstanceInvokeExpr) VirtualInvokeExpr(soot.jimple.VirtualInvokeExpr) InvokeExpr(soot.jimple.InvokeExpr) StaticInvokeExpr(soot.jimple.StaticInvokeExpr) GotoStmt(soot.jimple.GotoStmt) ArrayList(java.util.ArrayList) List(java.util.List) EnterMonitorStmt(soot.jimple.EnterMonitorStmt) HashSet(java.util.HashSet) BasicBlock(org.robovm.compiler.llvm.BasicBlock) Local(soot.Local) StructureConstantBuilder(org.robovm.compiler.llvm.StructureConstantBuilder) IntegerConstant(org.robovm.compiler.llvm.IntegerConstant) Switch(org.robovm.compiler.llvm.Switch) Bitcast(org.robovm.compiler.llvm.Bitcast) ConstantBitcast(org.robovm.compiler.llvm.ConstantBitcast) Value(org.robovm.compiler.llvm.Value) DefinitionStmt(soot.jimple.DefinitionStmt) ReturnStmt(soot.jimple.ReturnStmt) ThrowStmt(soot.jimple.ThrowStmt) ExitMonitorStmt(soot.jimple.ExitMonitorStmt) Variable(org.robovm.compiler.llvm.Variable) InvokeStmt(soot.jimple.InvokeStmt) AliasRef(org.robovm.compiler.llvm.AliasRef) NewMultiArrayExpr(soot.jimple.NewMultiArrayExpr) ConstantBitcast(org.robovm.compiler.llvm.ConstantBitcast) ReturnVoidStmt(soot.jimple.ReturnVoidStmt) PointerType(org.robovm.compiler.llvm.PointerType) Getelementptr(org.robovm.compiler.llvm.Getelementptr) BasicBlockRef(org.robovm.compiler.llvm.BasicBlockRef) Body(soot.Body) Call(org.robovm.compiler.llvm.Call) Alloca(org.robovm.compiler.llvm.Alloca) NullConstant(org.robovm.compiler.llvm.NullConstant) Trap(soot.Trap) LookupSwitchStmt(soot.jimple.LookupSwitchStmt) SootClass(soot.SootClass) TreeMap(java.util.TreeMap) Br(org.robovm.compiler.llvm.Br) FloatingPointType(org.robovm.compiler.llvm.FloatingPointType) IntegerType(org.robovm.compiler.llvm.IntegerType) PointerType(org.robovm.compiler.llvm.PointerType) NullType(soot.NullType) FunctionType(org.robovm.compiler.llvm.FunctionType) ArrayType(org.robovm.compiler.llvm.ArrayType) CharType(soot.CharType) RefLikeType(soot.RefLikeType) Type(org.robovm.compiler.llvm.Type) PrimType(soot.PrimType) IfStmt(soot.jimple.IfStmt) NopStmt(soot.jimple.NopStmt)

Aggregations

ArrayList (java.util.ArrayList)2 Body (soot.Body)2 Local (soot.Local)2 SootClass (soot.SootClass)2 Unit (soot.Unit)2 DefinitionStmt (soot.jimple.DefinitionStmt)2 File (java.io.File)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 TreeMap (java.util.TreeMap)1 CompilerException (org.robovm.compiler.CompilerException)1 AliasRef (org.robovm.compiler.llvm.AliasRef)1 Alloca (org.robovm.compiler.llvm.Alloca)1 ArrayType (org.robovm.compiler.llvm.ArrayType)1 BasicBlock (org.robovm.compiler.llvm.BasicBlock)1 BasicBlockRef (org.robovm.compiler.llvm.BasicBlockRef)1 Bitcast (org.robovm.compiler.llvm.Bitcast)1 Br (org.robovm.compiler.llvm.Br)1