Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2012 Trillian Mobile 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 <http://www.gnu.org/licenses/gpl-2.0.html>.
  */
 package org.robovm.compiler;
 
 import static org.robovm.compiler.Bro.*;
 import static org.robovm.compiler.Functions.*;
 import static org.robovm.compiler.Mangler.*;
 import static org.robovm.compiler.Types.*;
 import static org.robovm.compiler.llvm.Linkage.*;
 import static org.robovm.compiler.llvm.ParameterAttribute.*;
 import static org.robovm.compiler.llvm.Type.*;
 import static org.robovm.compiler.Annotations.*;
 
 import java.util.List;
 
 
 import soot.LongType;

Author(s):
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);
         if (readBooleanElem(bridgeAnnotation"dynamic"false)) {
             if (!method.isStatic() || method.getParameterCount() == 0
                     || method.getParameterType(0) != LongType.v()
                     || !hasParameterAnnotation(method, 0, )) {
                 throw new IllegalArgumentException("Dynamic @Bridge annotated method " 
                         + method + " must be static and take a @Pointer long as first parameter");
             }
         }
     }
 
     protected Function doCompile(ModuleBuilder moduleBuilderSootMethod method) {
         validateBridgeMethod(method);
 
         AnnotationTag bridgeAnnotation = getAnnotation(method);
        boolean dynamic = readBooleanElem(bridgeAnnotation"dynamic"false);
        boolean optional = readBooleanElem(bridgeAnnotation"optional"false);
        
        Function fn = FunctionBuilder.method(method);
        moduleBuilder.addFunction(fn);
        
        Type[] parameterTypes = fn.getType().getParameterTypes();
        String[] parameterNames = fn.getParameterNames();
        ArrayList<Argumentargs = new ArrayList<Argument>();
        for (int i = 0; i < parameterTypes.lengthi++) {
            args.add(new Argument(new VariableRef(parameterNames[i], parameterTypes[i])));
        }
        
        SootMethod originalMethod = method;
        Value structObj = null;
        boolean passByValue = isPassByValue(originalMethod);
        DataLayout dataLayout = .getDataLayout();
        if (passByValue) {
            // The method returns a struct by value. Determine whether that struct
            // is small enough to be passed in a register or has to be returned
            // using a @StructRet parameter.
            
            Arch arch = .getArch();
            OS os = .getOs();
            int size = dataLayout.getAllocSize(getStructType(originalMethod.getReturnType()));
            if (!os.isReturnedInRegisters(archsize)) {
                method = createFakeStructRetMethod(method);
                
                // Call Struct.allocate(<returnType>) to allocate a struct instance
                // which will be used as return value.
                VariableRef env = fn.getParameterRef(0);
                LdcClass ldcClass = new LdcClass(getInternalName(method.getDeclaringClass()), 
                        getInternalName(originalMethod.getReturnType()));
                .add(ldcClass);
                Value cls = call(fnldcClass.getFunctionRef(), env);
                Invokestatic invokestatic = new Invokestatic(
                        getInternalName(method.getDeclaringClass()), "org/robovm/rt/bro/Struct"
                        "allocate""(Ljava/lang/Class;)Lorg/robovm/rt/bro/Struct;");
                .add(invokestatic);
                structObj = call(fninvokestatic.getFunctionRef(), envcls);
    
                // Insert the allocated struct as arg 1 (first arg is always the Env*)
                args.add(1, new Argument(structObj));
            }
        }
        
        FunctionType targetFnType = getBridgeFunctionType(methoddynamic);
        if (method == originalMethod && passByValue) {
            // Returns a small struct. We need to change the return type to
            // i8/i16/i32/i64.
            int size = dataLayout.getAllocSize(targetFnType.getReturnType());
            Type t = size <= 1 ?  : (size <= 2 ?  : (size <= 4 ?  : ));
            targetFnType = new FunctionType(ttargetFnType.isVarargs(), targetFnType.getParameterTypes());
        }
        VariableRef env = fn.getParameterRef(0);
        
        // Load the address of the resolved @Bridge method
        Variable targetFn = fn.newVariable(targetFnType);
        if (!dynamic) {
            Global targetFnPtr = new Global(getTargetFnPtrName(originalMethod), 
                    new NullConstant());
            moduleBuilder.addGlobal(targetFnPtr);
            fn.add(new Load(targetFnnew ConstantBitcast(targetFnPtr.ref(), new PointerType(targetFnType))));
    
            Label nullLabel = new Label();
            Label notNullLabel = new Label();
            Variable nullCheck = fn.newVariable();
            fn.add(new Icmp(nullCheck.targetFn.ref(), new NullConstant(targetFnType)));
            fn.add(new Br(nullCheck.ref(), fn.newBasicBlockRef(nullLabel), fn.newBasicBlockRef(notNullLabel)));
            fn.newBasicBlock(nullLabel);
            call(fnenv,
                    moduleBuilder.getString(String.format((optional ? "Optional " : "")
                            + "@Bridge method %s.%s%s not bound",
                            originalMethod.getName(), getDescriptor(originalMethod))));
            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(targetFnfn.getParameterRef(1), targetFn.getType()));
            args.remove(originalMethod == method ? 1 : 2);
        }
        
        // 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<MarshaledArgmarshaledArgs = new ArrayList<MarshaledArg>();
        
        Type[] targetParameterTypes = targetFnType.getParameterTypes();
        
        int receiverIdx = -1;
        if (!method.isStatic()) {
            MarshalerMethod marshalerMethod = .getMarshalerLookup().findMarshalerMethod(new MarshalSite(method.));
            MarshaledArg marshaledArg = new MarshaledArg();
            marshaledArg.paramIndex = .;
            marshaledArgs.add(marshaledArg);
            // The receiver is either at index 0 or 1 in args depending on whether this method returns
            // a large struct by value or not.
            receiverIdx = method == originalMethod ? 0 : 1;
            Type nativeType = targetParameterTypes[receiverIdx];
            Value nativeValue = marshalObjectToNative(fnmarshalerMethodmarshaledArgnativeTypeenvargs.get(receiverIdx).getValue(),
                    .);
            args.set(receiverIdxnew Argument(nativeValue));
        }
        
        for (int i = 0, argIdx = 0; i < method.getParameterCount(); i++) {
            if (dynamic && (method == originalMethod && i == 0 || method != originalMethod && i == 1)) {
                // Skip the target function pointer for dynamic bridge methods.
                continue;
            }
            if (argIdx == receiverIdx) {
                // Skip the receiver in args. It doesn't correspond to a parameter.
                argIdx++;
            }
            soot.Type type = method.getParameterType(i);
            if (needsMarshaler(type)) {
                MarshalerMethod marshalerMethod = .getMarshalerLookup().findMarshalerMethod(new MarshalSite(methodi));
                
                // 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(fnmarshalerMethodnativeTypeenv
                            args.get(argIdx).getValue(), .);
                    args.set(argIdxnew Argument(nativeValue));
                } else {
                    ParameterAttribute[] parameterAttributes = new ParameterAttribute[0];
                    if (isPassByValue(methodi) || isStructRet(methodi)) {
                        // 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(fnenvargs.get(argIdx).getValue());
                        parameterAttributes = new ParameterAttribute[1];
                        if (isStructRet(methodi)) {
                            parameterAttributes[0] = ;
                        } else {
                            parameterAttributes[0] = ;
                        }
                    }
            
                    MarshaledArg marshaledArg = new MarshaledArg();
                    marshaledArg.paramIndex = i;
                    marshaledArgs.add(marshaledArg);
                    Value nativeValue = marshalObjectToNative(fnmarshalerMethodmarshaledArgnativeTypeenvargs.get(argIdx).getValue(),
                            .);
                    args.set(argIdxnew Argument(nativeValueparameterAttributes));
                }
                
            } else {
                args.set(argIdxnew Argument(marshalPrimitiveToNative(fnmethodiargs.get(argIdx).getValue())));                    
            }
            
            argIdx++;
        }        
        
        // Execute the call to native code
        BasicBlockRef bbSuccess = fn.newBasicBlockRef(new Label("success"));
        BasicBlockRef bbFailure = fn.newBasicBlockRef(new Label("failure"));
        pushNativeFrame(fn);
        trycatchAllEnter(fnenvbbSuccessbbFailure);
        fn.newBasicBlock(bbSuccess.getLabel());
        Value result = callWithArguments(fntargetFn.ref(), args);
        trycatchLeave(fnenv);
        popNativeFrame(fn);
        updateObject(methodfnenv.marshaledArgs);
        
        // Marshal the return value
        if (needsMarshaler(method.getReturnType())) {
            MarshalerMethod marshalerMethod = .getMarshalerLookup().findMarshalerMethod(new MarshalSite(method));
            String targetClassName = getInternalName(method.getReturnType());
            
            if (passByValue) {
                // Must be a small struct since larger structs are returned in 
                // the first parameter. Copy to the stack and then copy to the heap.
                Value stackCopy = createStackCopy(fnresult);
                Variable src = fn.newVariable();
                fn.add(new Bitcast(srcstackCopy));
                Value heapCopy = call(fnenvsrc.ref(), 
                        new IntegerConstant(dataLayout.getAllocSize(result.getType())));
                result = marshalNativeToObject(fnmarshalerMethodnullenv
                        targetClassNameheapCopy.);
            } else if (targetFnType.getReturnType() instanceof PrimitiveType) {
                result = marshalNativeToValueObject(fnmarshalerMethodenv
                        targetClassNameresult.);
            } else {
                result = marshalNativeToObject(fnmarshalerMethodnullenv
                        targetClassNameresult.);
            }
        } else {
            result = marshalNativeToPrimitive(fnmethodresult);
        }
        
        if (method != originalMethod) {
            fn.add(new Ret(structObj));
        } else {
            fn.add(new Ret(result));
        }
        
        fn.newBasicBlock(bbFailure.getLabel());
        trycatchLeave(fnenv);
        popNativeFrame(fn);
        
        Value ex = call(fnenv);
        
        // Call Marshaler.updateObject() for each object that was marshaled before
        // the call.
        updateObject(methodfnenv.marshaledArgs);
        
        call(fnenvex);
        fn.add(new Unreachable());
        
        return fn;
    }
    private void updateObject(SootMethod methodFunction fnValue envlong flagsList<MarshaledArgmarshaledArgs) {
        for (MarshaledArg value : marshaledArgs) {
            MarshalerMethod marshalerMethod = .getMarshalerLookup().findMarshalerMethod(new MarshalSite(methodvalue.paramIndex));
            SootMethod afterMethod = ((PointerMarshalerMethodmarshalerMethod).getAfterBridgeCallMethod();
            if (afterMethod != null) {
                Invokestatic invokestatic = new Invokestatic(
                        getInternalName(method.getDeclaringClass()),
                        getInternalName(afterMethod.getDeclaringClass()), 
                        afterMethod.getName(),
                        getDescriptor(afterMethod));
                .add(invokestatic);
                call(fninvokestatic.getFunctionRef(), 
                        envvalue.objectvalue.handle
                        new IntegerConstant(flags));
            }
        }
    }
    
    public static String getTargetFnPtrName(SootMethod method) {
        return "bridge_" + mangleMethod(method) + "_ptr";
    }
New to GrepCode? Check out our FAQ X