Search in sources :

Example 1 with Invokevirtual

use of org.robovm.compiler.trampoline.Invokevirtual in project robovm by robovm.

the class MethodCompiler method invokeExpr.

private Value invokeExpr(Stmt stmt, InvokeExpr expr) {
    SootMethodRef methodRef = expr.getMethodRef();
    ArrayList<Value> args = new ArrayList<Value>();
    args.add(env);
    if (!(expr instanceof StaticInvokeExpr)) {
        Value base = immediate(stmt, (Immediate) ((InstanceInvokeExpr) expr).getBase());
        checkNull(stmt, base);
        args.add(base);
    }
    int i = 0;
    for (soot.Value sootArg : (List<soot.Value>) expr.getArgs()) {
        Value arg = immediate(stmt, (Immediate) sootArg);
        args.add(narrowFromI32Value(stmt, getType(methodRef.parameterType(i)), arg));
        i++;
    }
    Value result = null;
    FunctionRef functionRef = config.isDebug() ? null : Intrinsics.getIntrinsic(sootMethod, stmt, expr);
    if (functionRef == null) {
        Trampoline trampoline = null;
        String targetClassName = getInternalName(methodRef.declaringClass());
        String methodName = methodRef.name();
        String methodDesc = getDescriptor(methodRef);
        if (expr instanceof SpecialInvokeExpr) {
            soot.Type runtimeType = ((SpecialInvokeExpr) expr).getBase().getType();
            String runtimeClassName = runtimeType == NullType.v() ? targetClassName : getInternalName(runtimeType);
            trampoline = new Invokespecial(this.className, targetClassName, methodName, methodDesc, runtimeClassName);
        } else if (expr instanceof StaticInvokeExpr) {
            trampoline = new Invokestatic(this.className, targetClassName, methodName, methodDesc);
        } else if (expr instanceof VirtualInvokeExpr) {
            soot.Type runtimeType = ((VirtualInvokeExpr) expr).getBase().getType();
            String runtimeClassName = runtimeType == NullType.v() ? targetClassName : getInternalName(runtimeType);
            trampoline = new Invokevirtual(this.className, targetClassName, methodName, methodDesc, runtimeClassName);
        } else if (expr instanceof InterfaceInvokeExpr) {
            trampoline = new Invokeinterface(this.className, targetClassName, methodName, methodDesc);
        }
        trampolines.add(trampoline);
        if (canCallDirectly(expr)) {
            SootMethod method = this.sootMethod.getDeclaringClass().getMethod(methodRef.name(), methodRef.parameterTypes(), methodRef.returnType());
            if (method.isSynchronized()) {
                functionRef = FunctionBuilder.synchronizedWrapper(method).ref();
            } else {
                functionRef = createMethodFunction(method).ref();
            }
        } else {
            functionRef = trampoline.getFunctionRef();
        }
    }
    result = call(stmt, functionRef, args.toArray(new Value[0]));
    if (result != null) {
        return widenToI32Value(stmt, result, methodRef.returnType().equals(CharType.v()));
    } else {
        return null;
    }
}
Also used : Trampoline(org.robovm.compiler.trampoline.Trampoline) SootMethodRef(soot.SootMethodRef) SpecialInvokeExpr(soot.jimple.SpecialInvokeExpr) ArrayList(java.util.ArrayList) InstanceInvokeExpr(soot.jimple.InstanceInvokeExpr) InterfaceInvokeExpr(soot.jimple.InterfaceInvokeExpr) Invokeinterface(org.robovm.compiler.trampoline.Invokeinterface) Invokespecial(org.robovm.compiler.trampoline.Invokespecial) StaticInvokeExpr(soot.jimple.StaticInvokeExpr) Invokestatic(org.robovm.compiler.trampoline.Invokestatic) Value(org.robovm.compiler.llvm.Value) SootMethod(soot.SootMethod) ArrayList(java.util.ArrayList) List(java.util.List) VirtualInvokeExpr(soot.jimple.VirtualInvokeExpr) Invokevirtual(org.robovm.compiler.trampoline.Invokevirtual) FunctionRef(org.robovm.compiler.llvm.FunctionRef)

Example 2 with Invokevirtual

use of org.robovm.compiler.trampoline.Invokevirtual in project robovm by robovm.

the class ClassCompiler method compile.

private void compile(Clazz clazz, OutputStream out) throws IOException {
    javaMethodCompiler.reset(clazz);
    bridgeMethodCompiler.reset(clazz);
    callbackMethodCompiler.reset(clazz);
    nativeMethodCompiler.reset(clazz);
    structMemberMethodCompiler.reset(clazz);
    globalValueMethodCompiler.reset(clazz);
    ClazzInfo ci = clazz.resetClazzInfo();
    mb = new ModuleBuilder();
    for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
        compilerPlugin.beforeClass(config, clazz, mb);
    }
    sootClass = clazz.getSootClass();
    trampolines = new HashMap<>();
    catches = new HashSet<String>();
    classFields = getClassFields(config.getOs(), config.getArch(), sootClass);
    instanceFields = getInstanceFields(config.getOs(), config.getArch(), sootClass);
    classType = getClassType(config.getOs(), config.getArch(), sootClass);
    instanceType = getInstanceType(config.getOs(), config.getArch(), sootClass);
    attributesEncoder.encode(mb, sootClass);
    // will never be initialized.
    if (!sootClass.declaresMethodByName("<clinit>") && hasConstantValueTags(classFields)) {
        SootMethod clinit = new SootMethod("<clinit>", Collections.EMPTY_LIST, VoidType.v(), Modifier.STATIC);
        JimpleBody body = Jimple.v().newBody(clinit);
        clinit.setActiveBody(body);
        body.getUnits().add(new JReturnVoidStmt());
        this.sootClass.addMethod(clinit);
    }
    if (isStruct(sootClass)) {
        SootMethod _sizeOf = new SootMethod("_sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PROTECTED | Modifier.NATIVE);
        sootClass.addMethod(_sizeOf);
        SootMethod sizeOf = new SootMethod("sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE);
        sootClass.addMethod(sizeOf);
    }
    mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch())));
    mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
    mb.addFunction(createLdcClass());
    mb.addFunction(createLdcClassWrapper());
    Function allocator = createAllocator();
    mb.addFunction(allocator);
    mb.addFunction(createClassInitWrapperFunction(allocator.ref()));
    for (SootField f : sootClass.getFields()) {
        Function getter = createFieldGetter(f, classFields, classType, instanceFields, instanceType);
        Function setter = createFieldSetter(f, classFields, classType, instanceFields, instanceType);
        mb.addFunction(getter);
        mb.addFunction(setter);
        if (f.isStatic() && !f.isPrivate()) {
            mb.addFunction(createClassInitWrapperFunction(getter.ref()));
            if (!f.isFinal()) {
                mb.addFunction(createClassInitWrapperFunction(setter.ref()));
            }
        }
    }
    // After this point no changes to methods/fields may be done by CompilerPlugins.
    ci.initClassInfo();
    for (SootMethod method : sootClass.getMethods()) {
        for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
            compilerPlugin.beforeMethod(config, clazz, method, mb);
        }
        String name = method.getName();
        Function function = null;
        if (hasBridgeAnnotation(method)) {
            function = bridgeMethod(method);
        } else if (hasGlobalValueAnnotation(method)) {
            function = globalValueMethod(method);
        } else if (isStruct(sootClass) && ("_sizeOf".equals(name) || "sizeOf".equals(name) || hasStructMemberAnnotation(method))) {
            function = structMember(method);
        } else if (method.isNative()) {
            function = nativeMethod(method);
        } else if (!method.isAbstract()) {
            function = method(method);
        }
        if (hasCallbackAnnotation(method)) {
            callbackMethod(method);
        }
        if (!name.equals("<clinit>") && !name.equals("<init>") && !method.isPrivate() && !method.isStatic() && !Modifier.isFinal(method.getModifiers()) && !Modifier.isFinal(sootClass.getModifiers())) {
            createLookupFunction(method);
        }
        if (method.isStatic() && !name.equals("<clinit>")) {
            String fnName = method.isSynchronized() ? Symbols.synchronizedWrapperSymbol(method) : Symbols.methodSymbol(method);
            FunctionRef fn = new FunctionRef(fnName, getFunctionType(method));
            mb.addFunction(createClassInitWrapperFunction(fn));
        }
        for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
            if (function != null) {
                compilerPlugin.afterMethod(config, clazz, method, mb, function);
            }
        }
    }
    for (Trampoline trampoline : trampolines.keySet()) {
        Set<String> deps = new HashSet<String>();
        Set<Triple<String, String, String>> mDeps = new HashSet<>();
        trampolineResolver.compile(mb, clazz, trampoline, deps, mDeps);
        for (SootMethod m : trampolines.get(trampoline)) {
            MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
            mi.addClassDependencies(deps, false);
            mi.addInvokeMethodDependencies(mDeps, false);
        }
    }
    /*
         * Add method dependencies from overriding methods to the overridden
         * super method(s). These will be reversed by the DependencyGraph to
         * create edges from the super/interface method to the overriding
         * method.
         */
    Map<SootMethod, Set<SootMethod>> overriddenMethods = getOverriddenMethods(this.sootClass);
    for (SootMethod from : overriddenMethods.keySet()) {
        MethodInfo mi = ci.getMethod(from.getName(), getDescriptor(from));
        for (SootMethod to : overriddenMethods.get(from)) {
            mi.addSuperMethodDependency(getInternalName(to.getDeclaringClass()), to.getName(), getDescriptor(to), false);
        }
    }
    /*
         * Edge case. A method in a superclass might satisfy an interface method
         * in the interfaces implemented by this class. See e.g. the abstract
         * class HashMap$HashIterator which doesn't implement Iterator but has
         * the hasNext() and other methods. We add a dependency from the current
         * class to the super method to ensure it's included if the current
         * class is linked in.
         */
    if (sootClass.hasSuperclass()) {
        for (SootClass interfaze : getImmediateInterfaces(sootClass)) {
            for (SootMethod m : interfaze.getMethods()) {
                if (!m.isStatic()) {
                    try {
                        this.sootClass.getMethod(m.getName(), m.getParameterTypes());
                    } catch (RuntimeException e) {
                        /*
                             * Not found. Find the implementation in
                             * superclasses.
                             */
                        SootMethod superMethod = null;
                        for (SootClass sc = sootClass.getSuperclass(); sc.hasSuperclass(); sc = sc.getSuperclass()) {
                            try {
                                SootMethod candidate = sc.getMethod(m.getName(), m.getParameterTypes());
                                if (!candidate.isStatic()) {
                                    superMethod = candidate;
                                    break;
                                }
                            } catch (RuntimeException e2) {
                            // Not found.
                            }
                        }
                        if (superMethod != null) {
                            ci.addSuperMethodDependency(getInternalName(superMethod.getDeclaringClass()), superMethod.getName(), getDescriptor(superMethod), false);
                        }
                    }
                }
            }
        }
    }
    Global classInfoStruct = null;
    try {
        if (!sootClass.isInterface()) {
            config.getVTableCache().get(sootClass);
        }
        classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), Linkage.weak, createClassInfoStruct());
    } catch (IllegalArgumentException e) {
        // VTable throws this if any of the superclasses of the class is actually an interface.
        // Shouldn't happen frequently but the DRLVM test suite has some tests for this.
        // The Linker will take care of making sure the class cannot be loaded at runtime.
        classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), I8_PTR, true);
    }
    mb.addGlobal(classInfoStruct);
    /*
         * Emit an internal i8* alias for the info struct which MethodCompiler
         * can use when referencing this info struct in exception landing pads
         * in methods in the same class. See #1007.
         */
    mb.addAlias(new Alias(classInfoStruct.getName() + "_i8ptr", Linkage._private, new ConstantBitcast(classInfoStruct.ref(), I8_PTR)));
    Function infoFn = FunctionBuilder.infoStruct(sootClass);
    infoFn.add(new Ret(new ConstantBitcast(classInfoStruct.ref(), I8_PTR_PTR)));
    mb.addFunction(infoFn);
    for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
        compilerPlugin.afterClass(config, clazz, mb);
    }
    OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
    mb.build().write(writer);
    writer.flush();
    ci.setCatchNames(catches);
    // Make sure no class or interface has zero dependencies
    ci.addClassDependency("java/lang/Object", false);
    if (sootClass.hasSuperclass() && !sootClass.isInterface()) {
        ci.addClassDependency(getInternalName(sootClass.getSuperclass()), false);
    }
    for (SootClass iface : sootClass.getInterfaces()) {
        ci.addClassDependency(getInternalName(iface), false);
    }
    for (SootField f : sootClass.getFields()) {
        addClassDependencyIfNeeded(clazz, f.getType(), false);
    }
    for (SootMethod m : sootClass.getMethods()) {
        MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
        addClassDependencyIfNeeded(clazz, mi, m.getReturnType(), false);
        @SuppressWarnings("unchecked") List<soot.Type> paramTypes = (List<soot.Type>) m.getParameterTypes();
        for (soot.Type type : paramTypes) {
            addClassDependencyIfNeeded(clazz, mi, type, false);
        }
    }
    ci.addClassDependencies(attributesEncoder.getDependencies(), false);
    ci.addClassDependencies(catches, false);
    for (Trampoline t : trampolines.keySet()) {
        if (t instanceof Checkcast) {
            ci.addCheckcast(t.getTarget());
        } else if (t instanceof Instanceof) {
            ci.addInstanceof(t.getTarget());
        } else if (t instanceof Invokevirtual || t instanceof Invokeinterface) {
            ci.addInvoke(t.getTarget() + "." + ((Invoke) t).getMethodName() + ((Invoke) t).getMethodDesc());
        }
    }
    clazz.saveClazzInfo();
}
Also used : Ret(org.robovm.compiler.llvm.Ret) Set(java.util.Set) HashSet(java.util.HashSet) CompilerPlugin(org.robovm.compiler.plugin.CompilerPlugin) ConstantBitcast(org.robovm.compiler.llvm.ConstantBitcast) Global(org.robovm.compiler.llvm.Global) Invoke(org.robovm.compiler.trampoline.Invoke) Function(org.robovm.compiler.llvm.Function) Instanceof(org.robovm.compiler.trampoline.Instanceof) ArrayList(java.util.ArrayList) List(java.util.List) JimpleBody(soot.jimple.JimpleBody) Checkcast(org.robovm.compiler.trampoline.Checkcast) Invokevirtual(org.robovm.compiler.trampoline.Invokevirtual) FunctionRef(org.robovm.compiler.llvm.FunctionRef) HashSet(java.util.HashSet) JReturnVoidStmt(soot.jimple.internal.JReturnVoidStmt) Trampoline(org.robovm.compiler.trampoline.Trampoline) ClazzInfo(org.robovm.compiler.clazz.ClazzInfo) SootClass(soot.SootClass) Invokeinterface(org.robovm.compiler.trampoline.Invokeinterface) Triple(org.apache.commons.lang3.tuple.Triple) CodeGenFileType(org.robovm.llvm.binding.CodeGenFileType) BooleanType(soot.BooleanType) StructureType(org.robovm.compiler.llvm.StructureType) PointerType(org.robovm.compiler.llvm.PointerType) ShortType(soot.ShortType) ByteType(soot.ByteType) DoubleType(soot.DoubleType) FloatType(soot.FloatType) IntType(soot.IntType) CharType(soot.CharType) LongType(soot.LongType) RefLikeType(soot.RefLikeType) Type(org.robovm.compiler.llvm.Type) PrimType(soot.PrimType) VoidType(soot.VoidType) Alias(org.robovm.compiler.llvm.Alias) SootMethod(soot.SootMethod) SootField(soot.SootField) MethodInfo(org.robovm.compiler.clazz.MethodInfo) OutputStreamWriter(java.io.OutputStreamWriter)

Example 3 with Invokevirtual

use of org.robovm.compiler.trampoline.Invokevirtual in project robovm by robovm.

the class TrampolineCompiler method checkMemberAccessible.

private boolean checkMemberAccessible(Function f, Trampoline t, ClassMember member) {
    Clazz caller = config.getClazzes().load(t.getCallingClass());
    Clazz target = config.getClazzes().load(member.getDeclaringClass().getName().replace('.', '/'));
    String runtimeClassName = null;
    runtimeClassName = t instanceof Invokevirtual ? ((Invokevirtual) t).getRuntimeClass() : runtimeClassName;
    runtimeClassName = t instanceof Invokespecial ? ((Invokespecial) t).getRuntimeClass() : runtimeClassName;
    runtimeClassName = t instanceof GetField ? ((GetField) t).getRuntimeClass() : runtimeClassName;
    runtimeClassName = t instanceof PutField ? ((PutField) t).getRuntimeClass() : runtimeClassName;
    SootClass runtimeClass = null;
    if (runtimeClassName != null && !isArray(runtimeClassName)) {
        Clazz c = config.getClazzes().load(runtimeClassName);
        if (c == null) {
            // just return true here.
            return true;
        }
        runtimeClass = c.getSootClass();
    }
    if (Access.checkMemberAccessible(member, caller, target, runtimeClass)) {
        return true;
    }
    if (member instanceof SootMethod) {
        SootMethod method = (SootMethod) member;
        throwIllegalAccessError(f, ILLEGAL_ACCESS_ERROR_METHOD, method.getDeclaringClass(), method.getName(), getDescriptor(method), caller.getSootClass());
    } else {
        SootField field = (SootField) member;
        throwIllegalAccessError(f, ILLEGAL_ACCESS_ERROR_FIELD, field.getDeclaringClass(), field.getName(), caller.getSootClass());
    }
    f.add(new Unreachable());
    return false;
}
Also used : GetField(org.robovm.compiler.trampoline.GetField) Unreachable(org.robovm.compiler.llvm.Unreachable) SootMethod(soot.SootMethod) Clazz(org.robovm.compiler.clazz.Clazz) SootField(soot.SootField) SootClass(soot.SootClass) Invokevirtual(org.robovm.compiler.trampoline.Invokevirtual) Invokespecial(org.robovm.compiler.trampoline.Invokespecial) PutField(org.robovm.compiler.trampoline.PutField)

Aggregations

Invokevirtual (org.robovm.compiler.trampoline.Invokevirtual)3 SootMethod (soot.SootMethod)3 ArrayList (java.util.ArrayList)2 List (java.util.List)2 FunctionRef (org.robovm.compiler.llvm.FunctionRef)2 Invokeinterface (org.robovm.compiler.trampoline.Invokeinterface)2 Invokespecial (org.robovm.compiler.trampoline.Invokespecial)2 Trampoline (org.robovm.compiler.trampoline.Trampoline)2 SootClass (soot.SootClass)2 SootField (soot.SootField)2 OutputStreamWriter (java.io.OutputStreamWriter)1 HashSet (java.util.HashSet)1 Set (java.util.Set)1 Triple (org.apache.commons.lang3.tuple.Triple)1 Clazz (org.robovm.compiler.clazz.Clazz)1 ClazzInfo (org.robovm.compiler.clazz.ClazzInfo)1 MethodInfo (org.robovm.compiler.clazz.MethodInfo)1 Alias (org.robovm.compiler.llvm.Alias)1 ConstantBitcast (org.robovm.compiler.llvm.ConstantBitcast)1 Function (org.robovm.compiler.llvm.Function)1