All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.compiler.BridgeMethodCompiler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 RoboVM AB
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.robovm.compiler;

import static org.robovm.compiler.Annotations.*;
import static org.robovm.compiler.Bro.*;
import static org.robovm.compiler.Functions.*;
import static org.robovm.compiler.Types.*;
import static org.robovm.compiler.llvm.Linkage.*;
import static org.robovm.compiler.llvm.Type.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.robovm.compiler.Bro.MarshalerFlags;
import org.robovm.compiler.MarshalerLookup.MarshalSite;
import org.robovm.compiler.MarshalerLookup.MarshalerMethod;
import org.robovm.compiler.MarshalerLookup.PointerMarshalerMethod;
import org.robovm.compiler.config.Arch;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.config.OS.Family;
import org.robovm.compiler.llvm.Alloca;
import org.robovm.compiler.llvm.Argument;
import org.robovm.compiler.llvm.BasicBlockRef;
import org.robovm.compiler.llvm.Bitcast;
import org.robovm.compiler.llvm.Br;
import org.robovm.compiler.llvm.DataLayout;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.FunctionDeclaration;
import org.robovm.compiler.llvm.FunctionRef;
import org.robovm.compiler.llvm.FunctionType;
import org.robovm.compiler.llvm.Global;
import org.robovm.compiler.llvm.Icmp;
import org.robovm.compiler.llvm.Icmp.Condition;
import org.robovm.compiler.llvm.IntegerConstant;
import org.robovm.compiler.llvm.Inttoptr;
import org.robovm.compiler.llvm.Label;
import org.robovm.compiler.llvm.Load;
import org.robovm.compiler.llvm.NullConstant;
import org.robovm.compiler.llvm.ParameterAttribute;
import org.robovm.compiler.llvm.PointerType;
import org.robovm.compiler.llvm.PrimitiveType;
import org.robovm.compiler.llvm.Ret;
import org.robovm.compiler.llvm.StructureType;
import org.robovm.compiler.llvm.Type;
import org.robovm.compiler.llvm.Unreachable;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.llvm.Variable;
import org.robovm.compiler.llvm.VariableRef;
import org.robovm.compiler.trampoline.Invokestatic;

import soot.LongType;
import soot.SootMethod;
import soot.tagkit.AnnotationTag;

/**
 * @author niklas
 *
 */
public class BridgeMethodCompiler extends BroMethodCompiler {

    public BridgeMethodCompiler(Config config) {
        super(config);
    }

    private void validateBridgeMethod(SootMethod method) {
        if (!method.isNative()) {
            throw new IllegalArgumentException("@Bridge annotated method " 
                    + method + " must be native");
        }
        AnnotationTag bridgeAnnotation = getAnnotation(method, BRIDGE);
        if (readBooleanElem(bridgeAnnotation, "dynamic", false)) {
            if (!method.isStatic() || method.getParameterCount() == 0
                    || method.getParameterType(0) != LongType.v()
                    || !hasParameterAnnotation(method, 0, POINTER)) {
                throw new IllegalArgumentException("Dynamic @Bridge annotated method " 
                        + method + " must be static and take a @Pointer long as first parameter");
            }
        }
        if (hasVariadicAnnotation(method)) {
            int first = getVariadicParameterIndex(method);
            if (first < 0) {
                throw new IllegalArgumentException("Invalid @Variadic index on @Bridge annotated method " 
                        + method + ". Index must be 0 or greater.");
            }
            if (method.isStatic() && first == 0) {
                throw new IllegalArgumentException("At least 1 parameter must come " 
                        + "before the first va_arg parameter on static @Bridge annotated method " 
                        + method);
            }
            for (int i = first; i < method.getParameterCount(); i++) {
                if (isPassByValue(method, i)) {
                    throw new IllegalArgumentException("Parameter " + (i + 1) + " of @Bridge annotated method " 
                        + method + " cannot be passed @ByVal when part of a va_arg parameter list");
                }
            }
        }
    }

    protected static FunctionRef getBridgeCWrapperRef(FunctionType functionType, String name) {
        Type returnType = functionType.getReturnType();
        Type wrapperReturnType = returnType instanceof StructureType ? VOID : returnType;
        List wrapperParamTypes = new ArrayList<>();
        wrapperParamTypes.add(I8_PTR);
        if (returnType instanceof StructureType) {
            wrapperParamTypes.add(I8_PTR);
        }
        for (Type t : functionType.getParameterTypes()) {
            if (t instanceof StructureType || t instanceof PointerType) {
                wrapperParamTypes.add(I8_PTR);
            } else {
                wrapperParamTypes.add(t);
            }
        }
        FunctionType wrapperFnType = new FunctionType(wrapperReturnType,
                wrapperParamTypes.toArray(new Type[wrapperParamTypes.size()]));
        return new FunctionRef(name, wrapperFnType);
    }
    protected static String createBridgeCWrapper(Type returnType, Type[] hiParameterTypes, Type[] loParameterTypes, String name) {
        if (hiParameterTypes.length != loParameterTypes.length) {
            // Varargs
            if (hiParameterTypes.length < loParameterTypes.length) {
                throw new IllegalArgumentException("For va_arg functions lo's types parameter types must be a prefix of the hi type's parameters");
            }
            if (!Arrays.asList(hiParameterTypes).subList(0, loParameterTypes.length).equals(Arrays.asList(loParameterTypes))) {
                throw new IllegalArgumentException("For va_arg functions lo's types parameter types must be a prefix of the hi type's parameters");
            }
        } else {
            if (!Arrays.equals(hiParameterTypes, loParameterTypes)) {
                throw new IllegalArgumentException("hi and lo parameter types must be equal");
            }
        }
        
        // We order structs by name in reverse order. The names are constructed
        // such that nested structs get names which are naturally ordered after
        // their parent struct.
        Map typedefs = new TreeMap(Collections.reverseOrder());
        
        String hiReturnType = returnType instanceof StructureType 
                ? "void" : getHiType(returnType);

        StringBuilder hiSignature = new StringBuilder();
        hiSignature
            .append(hiReturnType)
            .append(' ')
            .append(name)
            .append("(void* target");
        if (returnType instanceof StructureType) {
            hiSignature.append(", void* ret");
        }

        StringBuilder loSignature = new StringBuilder();
        String loReturnType = getLoType(returnType, name, 0, typedefs);
        loSignature
            .append(loReturnType)
            .append(' ')
            .append("(*)(");
        
        StringBuilder body = new StringBuilder(" {\n");
        StringBuilder args = new StringBuilder();
        
        for (int i = 0; i < hiParameterTypes.length; i++) {
            String arg = "p" + i;
            if (i > 0) {
                args.append(", ");
            }

            String hiParamType = getHiType(hiParameterTypes[i]);
            hiSignature.append(", ");
            hiSignature.append(hiParamType).append(' ').append(arg);

            if (i < loParameterTypes.length) {
                String loParamType = getLoType(hiParameterTypes[i], name, i + 1, typedefs);
                if (i > 0) {
                    loSignature.append(", ");
                }
                loSignature.append(loParamType);
    
                if (hiParameterTypes[i] instanceof StructureType) {
                    args.append("*((").append(loParamType).append("*)").append(arg).append(')');
                } else {
                    args.append(arg);
                }
            } else {
                args.append(arg);
            }
        }
        hiSignature.append(')');
        if (loParameterTypes.length == 0) {
            loSignature.append("void");
        } else if (loParameterTypes.length < hiParameterTypes.length) {
            loSignature.append(", ...");
        }
        loSignature.append(')');

        final String intend = "    ";
        for (String typedef : typedefs.values()) {
            body.append(intend + typedef.replace("\n", "\n" + intend) + "\n");
        }
        
        if (returnType instanceof StructureType) {
            body.append("    *((" + loReturnType + "*)ret) = ((" + loSignature + ") target)(" + args + ");\n");
        } else if (returnType != Type.VOID) {
            body.append("    return ((" + loSignature + ") target)(" + args + ");\n");
        } else {
            body.append("    ((" + loSignature + ") target)(" + args + ");\n");
        }
        body.append("}\n");

        return hiSignature.toString() + body.toString();
    }
    
    protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) {
        validateBridgeMethod(method);

        AnnotationTag bridgeAnnotation = getAnnotation(method, BRIDGE);
        boolean dynamic = readBooleanElem(bridgeAnnotation, "dynamic", false);
        boolean optional = readBooleanElem(bridgeAnnotation, "optional", false);
        boolean useCWrapper = requiresCWrapper(method);
        
        Function fn = createMethodFunction(method);
        moduleBuilder.addFunction(fn);
        
        Type[] parameterTypes = fn.getType().getParameterTypes();
        String[] parameterNames = fn.getParameterNames();
        ArrayList args = new ArrayList();
        for (int i = 0; i < parameterTypes.length; i++) {
            args.add(new Argument(new VariableRef(parameterNames[i], parameterTypes[i])));
        }
        
        VariableRef env = fn.getParameterRef(0);
        
        // Load the address of the resolved @Bridge method
        Variable targetFn = fn.newVariable(I8_PTR);
        if (!dynamic) {
            Global targetFnPtr = new Global(Symbols.bridgePtrSymbol(method), 
                    _private, new NullConstant(I8_PTR));
            moduleBuilder.addGlobal(targetFnPtr);
            fn.add(new Load(targetFn, targetFnPtr.ref()));
    
            Label nullLabel = new Label();
            Label notNullLabel = new Label();
            Variable nullCheck = fn.newVariable(I1);
            fn.add(new Icmp(nullCheck, Condition.eq, targetFn.ref(), new NullConstant(I8_PTR)));
            fn.add(new Br(nullCheck.ref(), fn.newBasicBlockRef(nullLabel), fn.newBasicBlockRef(notNullLabel)));
            fn.newBasicBlock(nullLabel);
            call(fn, optional ? BC_THROW_UNSATISIFED_LINK_ERROR_OPTIONAL_BRIDGE_NOT_BOUND
                    : BC_THROW_UNSATISIFED_LINK_ERROR_BRIDGE_NOT_BOUND, env,
                    moduleBuilder.getString(className), moduleBuilder.getString(method.getName()),
                    moduleBuilder.getString(getDescriptor(method)));
            fn.add(new Unreachable());
            fn.newBasicBlock(notNullLabel);
        } else {
            // Dynamic @Bridge methods pass the target function pointer as a
            // long in the first parameter.
            fn.add(new Inttoptr(targetFn, fn.getParameterRef(1), targetFn.getType()));
            args.remove(1);
        }
        
        // Marshal args
        
        // Remove Env* from args
        args.remove(0);

        // Save the Object->handle mapping for each marshaled object. We need it
        // after the native call to call updateObject() on the marshaler for 
        // each value. Since the LLVM variables that store these values are used 
        // after the native call we get the nice side effect that neither the
        // Java objects nor the handles can be garbage collected while we're in
        // native code.
        List marshaledArgs = new ArrayList();
        
        FunctionType targetFnType = getBridgeFunctionType(method, dynamic, false);
        Type[] targetParameterTypes = targetFnType.getParameterTypes();
        
        if (!method.isStatic()) {
            MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, MarshalSite.RECEIVER));
            Type nativeType = targetParameterTypes[0];
            // in case of EnumValue native type will be primitive, handle it as primitive type with marshaller
            if (nativeType instanceof PrimitiveType) {
                Value nativeValue = marshalValueObjectToNative(fn, marshalerMethod, nativeType, env,
                        args.get(0).getValue(), MarshalerFlags.CALL_TYPE_BRIDGE);
                args.set(0, new Argument(nativeValue));
            } else {
                MarshaledArg marshaledArg = new MarshaledArg();
                marshaledArg.paramIndex = MarshalSite.RECEIVER;
                marshaledArgs.add(marshaledArg);
                Value nativeValue = marshalObjectToNative(fn, marshalerMethod, marshaledArg,
                        useCWrapper ? I8_PTR : nativeType, env, args.get(0).getValue(),
                        MarshalerFlags.CALL_TYPE_BRIDGE);
                args.set(0, new Argument(nativeValue));
            }
        }
        
        for (int i = 0, argIdx = 0; i < method.getParameterCount(); i++) {
            if (dynamic && i == 0) {
                // Skip the target function pointer for dynamic bridge methods.
                continue;
            }
            if (!method.isStatic() && argIdx == 0) {
                // Skip the receiver in args. It doesn't correspond to a parameter.
                argIdx++;
            }
            soot.Type type = method.getParameterType(i);
            if (needsMarshaler(type)) {

                MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, i));
                
                // The return type of the marshaler's toNative() method is derived from the target function type.
                Type nativeType = targetParameterTypes[argIdx];

                if (nativeType instanceof PrimitiveType) {
                    Value nativeValue = marshalValueObjectToNative(fn, marshalerMethod, nativeType, env, 
                            args.get(argIdx).getValue(), MarshalerFlags.CALL_TYPE_BRIDGE);
                    args.set(argIdx, new Argument(nativeValue));
                } else {
                    ParameterAttribute[] parameterAttributes = new ParameterAttribute[0];
                    if (isPassByValue(method, i) || isStructRet(method, i)) {
                        // The parameter must not be null. We assume that Structs 
                        // never have a NULL handle so we just check that the Java
                        // Object isn't null.
                        call(fn, CHECK_NULL, env, args.get(argIdx).getValue());
                    }
            
                    MarshaledArg marshaledArg = new MarshaledArg();
                    marshaledArg.paramIndex = i;
                    marshaledArgs.add(marshaledArg);
                    Value nativeValue = marshalObjectToNative(fn, marshalerMethod, marshaledArg, 
                            useCWrapper ? I8_PTR : nativeType, env, args.get(argIdx).getValue(),
                            MarshalerFlags.CALL_TYPE_BRIDGE);
                    args.set(argIdx, new Argument(nativeValue, parameterAttributes));
                }
                
            } else {
                args.set(argIdx, new Argument(marshalPrimitiveToNative(fn, method, i, args.get(argIdx).getValue())));                    
            }
            
            argIdx++;
        }        
        
        Variable structResult = null;
        Value targetFnRef = null;
        
        if (useCWrapper) {
            args.add(0, new Argument(targetFn.ref()));

            if (targetFnType.getReturnType() instanceof StructureType) {
                // Allocate space on the stack big enough to hold the returned struct
                Variable tmp = fn.newVariable(new PointerType(targetFnType.getReturnType()));
                fn.add(new Alloca(tmp, targetFnType.getReturnType()));
                structResult = fn.newVariable(I8_PTR);
                fn.add(new Bitcast(structResult, tmp.ref(), I8_PTR));
                args.add(1, new Argument(structResult.ref()));
            }

            String wrapperName = Symbols.bridgeCSymbol(method);
            FunctionType wrapperFnType = getBridgeFunctionType(method, dynamic, true);
            getCWrapperFunctions().add(createBridgeCWrapper(targetFnType.getReturnType(), 
                    targetFnType.getParameterTypes(), wrapperFnType.getParameterTypes(), wrapperName));
            FunctionRef wrapperFnRef = getBridgeCWrapperRef(targetFnType, wrapperName);
            moduleBuilder.addFunctionDeclaration(new FunctionDeclaration(wrapperFnRef));
            targetFnRef = wrapperFnRef;
        } else {
            Variable tmp = fn.newVariable(targetFnType);
            fn.add(new Bitcast(tmp, targetFn.ref(), targetFnType));
            targetFnRef = tmp.ref();
        }
        
        // Execute the call to native code
        BasicBlockRef bbSuccess = fn.newBasicBlockRef(new Label("success"));
        BasicBlockRef bbFailure = fn.newBasicBlockRef(new Label("failure"));
        pushNativeFrame(fn);
        trycatchAllEnter(fn, env, bbSuccess, bbFailure);
        fn.newBasicBlock(bbSuccess.getLabel());
        Value result = callWithArguments(fn, targetFnRef, args);
        trycatchLeave(fn, env);
        popNativeFrame(fn);

        updateObject(method, fn, env, MarshalerFlags.CALL_TYPE_BRIDGE, marshaledArgs);
        
        // Marshal the return value
        if (needsMarshaler(method.getReturnType())) {
            MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method));
            String targetClassName = getInternalName(method.getReturnType());
            
            if (structResult != null) {
                // Copy to the heap.
                DataLayout dataLayout = config.getDataLayout();
                Value heapCopy = call(fn, BC_COPY_STRUCT, env, structResult.ref(), 
                        new IntegerConstant(dataLayout.getAllocSize(targetFnType.getReturnType())));
                result = marshalNativeToObject(fn, marshalerMethod, null, env, 
                        targetClassName, heapCopy, MarshalerFlags.CALL_TYPE_BRIDGE);
            } else if (targetFnType.getReturnType() instanceof PrimitiveType) {
                result = marshalNativeToValueObject(fn, marshalerMethod, env, 
                        targetClassName, result, MarshalerFlags.CALL_TYPE_BRIDGE);
            } else {
                result = marshalNativeToObject(fn, marshalerMethod, null, env, 
                        targetClassName, result, MarshalerFlags.CALL_TYPE_BRIDGE);
            }
        } else {
            result = marshalNativeToPrimitive(fn, method, result);
        }
        
        fn.add(new Ret(result));
        
        fn.newBasicBlock(bbFailure.getLabel());
        trycatchLeave(fn, env);
        popNativeFrame(fn);
        
        Value ex = call(fn, BC_EXCEPTION_CLEAR, env);
        
        // Call Marshaler.updateObject() for each object that was marshaled before
        // the call.
        updateObject(method, fn, env, MarshalerFlags.CALL_TYPE_BRIDGE, marshaledArgs);
        
        call(fn, BC_THROW, env, ex);
        fn.add(new Unreachable());
        
        return fn;
    }

    private void updateObject(SootMethod method, Function fn, Value env, long flags, List marshaledArgs) {
        for (MarshaledArg value : marshaledArgs) {
            MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, value.paramIndex));
            SootMethod afterMethod = ((PointerMarshalerMethod) marshalerMethod).getAfterBridgeCallMethod();
            if (afterMethod != null) {
                Invokestatic invokestatic = new Invokestatic(
                        getInternalName(method.getDeclaringClass()),
                        getInternalName(afterMethod.getDeclaringClass()), 
                        afterMethod.getName(),
                        getDescriptor(afterMethod));
                trampolines.add(invokestatic);
                call(fn, invokestatic.getFunctionRef(), 
                        env, value.object, value.handle, 
                        new IntegerConstant(flags));
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy