Search in sources :

Example 1 with Invoke

use of org.robovm.compiler.trampoline.Invoke 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 2 with Invoke

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

the class TrampolineCompiler method compile.

public void compile(ModuleBuilder mb, Clazz currentClass, Trampoline t, Set<String> dependencies, Set<Triple<String, String, String>> methodDependencies) {
    this.mb = mb;
    addDependencyIfNeeded(dependencies, currentClass, t);
    /*
         * Check if the target class exists and is accessible. Also check that
         * field accesses and method calls are compatible with the target 
         * field/method and that the field/method is accessible to the caller.
         * If any of the tests fail the weak trampoline function created by the
         * ClassCompiler will be overridden with a function which throws an
         * appropriate exception.
         */
    Function errorFn = new FunctionBuilder(t).linkage(external).build();
    if (!checkClassExists(errorFn, t) || !checkClassAccessible(errorFn, t)) {
        mb.addFunction(errorFn);
        return;
    }
    if (t instanceof New) {
        SootClass target = config.getClazzes().load(t.getTarget()).getSootClass();
        if (target.isAbstract() || target.isInterface()) {
            call(errorFn, BC_THROW_INSTANTIATION_ERROR, errorFn.getParameterRef(0), mb.getString(t.getTarget().replace('/', '.')));
            errorFn.add(new Unreachable());
            mb.addFunction(errorFn);
            return;
        }
        String fnName = Symbols.clinitWrapperSymbol(Symbols.allocatorSymbol(t.getTarget()));
        alias(t, fnName);
    } else if (t instanceof Instanceof) {
        if (isArray(t.getTarget())) {
            FunctionRef fnRef = createInstanceofArray((Instanceof) t);
            alias(t, fnRef.getName());
        } else {
            String fnName = Symbols.instanceofSymbol(t.getTarget());
            alias(t, fnName);
        }
    } else if (t instanceof Checkcast) {
        if (isArray(t.getTarget())) {
            FunctionRef fnRef = createCheckcastArray((Checkcast) t);
            alias(t, fnRef.getName());
        } else {
            String fnName = Symbols.checkcastSymbol(t.getTarget());
            alias(t, fnName);
        }
    } else if (t instanceof LdcClass) {
        if (isArray(t.getTarget())) {
            FunctionRef fnRef = createLdcArray((LdcClass) t);
            alias(t, fnRef.getName());
        } else {
            String fnName = Symbols.ldcExternalSymbol(t.getTarget());
            alias(t, fnName);
        }
    } else if (t instanceof Anewarray) {
        FunctionRef fnRef = createAnewarray((Anewarray) t);
        alias(t, fnRef.getName());
    } else if (t instanceof Multianewarray) {
        FunctionRef fnRef = createMultianewarray((Multianewarray) t);
        alias(t, fnRef.getName());
    } else if (t instanceof FieldAccessor) {
        SootField field = resolveField(errorFn, (FieldAccessor) t);
        if (field != null) {
            dependencies.add(getInternalName(field.getDeclaringClass()));
        }
        if (field == null || !checkMemberAccessible(errorFn, t, field)) {
            mb.addFunction(errorFn);
            return;
        }
        Clazz caller = config.getClazzes().load(t.getCallingClass());
        Clazz target = config.getClazzes().load(t.getTarget());
        if (!((FieldAccessor) t).isGetter() && field.isFinal() && caller != target) {
            // Only the class declaring a final field may write to it.
            // (Actually only <init>/<clinit> methods may write to it but we 
            // don't know which method is accessing the field at this point)
            throwIllegalAccessError(errorFn, ATTEMPT_TO_WRITE_TO_FINAL_FIELD, target, field.getName(), caller);
            mb.addFunction(errorFn);
            return;
        }
        if (!field.isStatic()) {
            createInlinedAccessorForInstanceField((FieldAccessor) t, field);
        } else {
            createTrampolineAliasForField((FieldAccessor) t, field);
        }
    } else if (t instanceof Invokeinterface) {
        SootMethod rm = resolveInterfaceMethod(errorFn, (Invokeinterface) t);
        if (rm != null) {
            methodDependencies.add(new ImmutableTriple<String, String, String>(getInternalName(rm.getDeclaringClass()), rm.getName(), getDescriptor(rm)));
        }
        if (rm == null || !checkMemberAccessible(errorFn, t, rm)) {
            mb.addFunction(errorFn);
            return;
        }
        createTrampolineAliasForMethod((Invoke) t, rm);
    } else if (t instanceof Invoke) {
        SootMethod method = resolveMethod(errorFn, (Invoke) t);
        if (method != null) {
            methodDependencies.add(new ImmutableTriple<String, String, String>(getInternalName(method.getDeclaringClass()), method.getName(), getDescriptor(method)));
        }
        if (method == null || !checkMemberAccessible(errorFn, t, method)) {
            mb.addFunction(errorFn);
            return;
        }
        if (t instanceof Invokespecial && method.isAbstract()) {
            call(errorFn, BC_THROW_ABSTRACT_METHOD_ERROR, errorFn.getParameterRef(0), mb.getString(String.format(NO_SUCH_METHOD_ERROR, method.getDeclaringClass(), method.getName(), getDescriptor(method))));
            errorFn.add(new Unreachable());
            mb.addFunction(errorFn);
            return;
        }
        createTrampolineAliasForMethod((Invoke) t, method);
    }
}
Also used : New(org.robovm.compiler.trampoline.New) ImmutableTriple(org.apache.commons.lang3.tuple.ImmutableTriple) Anewarray(org.robovm.compiler.trampoline.Anewarray) Multianewarray(org.robovm.compiler.trampoline.Multianewarray) SootClass(soot.SootClass) FieldAccessor(org.robovm.compiler.trampoline.FieldAccessor) Invokeinterface(org.robovm.compiler.trampoline.Invokeinterface) Invoke(org.robovm.compiler.trampoline.Invoke) Invokespecial(org.robovm.compiler.trampoline.Invokespecial) Function(org.robovm.compiler.llvm.Function) Unreachable(org.robovm.compiler.llvm.Unreachable) LdcClass(org.robovm.compiler.trampoline.LdcClass) Instanceof(org.robovm.compiler.trampoline.Instanceof) SootMethod(soot.SootMethod) SootField(soot.SootField) Clazz(org.robovm.compiler.clazz.Clazz) Checkcast(org.robovm.compiler.trampoline.Checkcast) FunctionRef(org.robovm.compiler.llvm.FunctionRef)

Aggregations

Function (org.robovm.compiler.llvm.Function)2 FunctionRef (org.robovm.compiler.llvm.FunctionRef)2 Checkcast (org.robovm.compiler.trampoline.Checkcast)2 Instanceof (org.robovm.compiler.trampoline.Instanceof)2 Invoke (org.robovm.compiler.trampoline.Invoke)2 Invokeinterface (org.robovm.compiler.trampoline.Invokeinterface)2 SootClass (soot.SootClass)2 SootField (soot.SootField)2 SootMethod (soot.SootMethod)2 OutputStreamWriter (java.io.OutputStreamWriter)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Set (java.util.Set)1 ImmutableTriple (org.apache.commons.lang3.tuple.ImmutableTriple)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