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

org.robovm.compiler.CallbackMethodCompiler 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.Bro.*;
import static org.robovm.compiler.Functions.*;
import static org.robovm.compiler.Types.*;
import static org.robovm.compiler.llvm.Type.*;

import java.util.ArrayList;
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.Config;
import org.robovm.compiler.llvm.Alias;
import org.robovm.compiler.llvm.BasicBlockRef;
import org.robovm.compiler.llvm.ConstantBitcast;
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.IntegerConstant;
import org.robovm.compiler.llvm.Label;
import org.robovm.compiler.llvm.Linkage;
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.trampoline.Invokestatic;

import soot.SootMethod;


/**
 * 
 */
public class CallbackMethodCompiler extends BroMethodCompiler {

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

    @Override
    protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) {
        return compileCallback(moduleBuilder, method);
    }
    
    protected FunctionRef getCallbackCWrapperRef(SootMethod method, String name) {
        return new FunctionRef(name, getCallbackFunctionType(method, false));
    }
    
    protected static String createCallbackCWrapper(FunctionType functionType, String name, String innerName) {
        // 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());
        
        StringBuilder hiSignature = new StringBuilder();
        hiSignature
            .append(getHiType(functionType.getReturnType()))
            .append(' ')
            .append(innerName)
            .append('(');

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

            String hiParamType = getHiType(functionType.getParameterTypes()[i]);
            hiSignature.append(hiParamType);

            String loParamType = getLoType(functionType.getParameterTypes()[i], name, i + 1, typedefs);
            loSignature.append(loParamType).append(' ').append(arg);

            if (functionType.getParameterTypes()[i] instanceof StructureType) {
                args.append("(void*) &").append(arg);
            } else {
                args.append(arg);
            }
        }
        if (functionType.getParameterTypes().length == 0) {
            hiSignature.append("void");
            loSignature.append("void");
        }
        hiSignature.append(')');
        loSignature.append(')');
        
        StringBuilder header = new StringBuilder();
        for (String typedef : typedefs.values()) {
            header.append(typedef + "\n");
        }
        header.append(hiSignature + ";\n");
        
        if (functionType.getReturnType() instanceof StructureType) {
            body.append("    return *((" + loReturnType + "*) " + innerName + "(" + args + "));\n");
        } else if (functionType.getReturnType() != Type.VOID) {
            body.append("    return " + innerName + "(" + args + ");\n");
        } else {
            body.append("    " + innerName + "(" + args + ");\n");
        }
        body.append("}\n");

        return header.toString() + loSignature.toString() + body.toString();
    }
    
    private Function compileCallback(ModuleBuilder moduleBuilder, SootMethod method) {
        
        Function callbackFn = null;
        FunctionType nativeFnType = null;
        boolean useCWrapper = requiresCWrapper(method);
        if (useCWrapper) {
            // The C wrapper is the function which is called by native code. It
            // handles structs passed/returned by value. It calls an LLVM function 
            // which has the same signature but all structs passed/returned by value
            // replaced by pointers (i8*).
            FunctionRef callbackCWrapperRef = getCallbackCWrapperRef(method, Symbols.callbackCSymbol(method));
            getCWrapperFunctions().add(createCallbackCWrapper(callbackCWrapperRef.getType(), 
                    callbackCWrapperRef.getName(), Symbols.callbackInnerCSymbol(method)));
            moduleBuilder.addFunctionDeclaration(new FunctionDeclaration(callbackCWrapperRef));
            Type callbackRetType = callbackCWrapperRef.getType().getReturnType() instanceof StructureType 
                    ? I8_PTR : callbackCWrapperRef.getType().getReturnType();
            Type[] callbackParamTypes = new Type[callbackCWrapperRef.getType().getParameterTypes().length];
            for (int i = 0; i < callbackParamTypes.length; i++) {
                Type t = callbackCWrapperRef.getType().getParameterTypes()[i];
                if (t instanceof StructureType) {
                    t = I8_PTR;
                }
                callbackParamTypes[i] = t;
            }
            moduleBuilder.addAlias(new Alias(Symbols.callbackPtrSymbol(method), 
                    Linkage._private, new ConstantBitcast(callbackCWrapperRef, I8_PTR)));
            callbackFn = new FunctionBuilder(Symbols.callbackInnerCSymbol(method), 
                    new FunctionType(callbackRetType, callbackParamTypes)).build();
            nativeFnType = callbackCWrapperRef.getType();
        } else {
            FunctionType callbackFnType = getCallbackFunctionType(method, false);
            callbackFn = FunctionBuilder.callback(method, callbackFnType);
            moduleBuilder.addAlias(new Alias(Symbols.callbackPtrSymbol(method), 
                    Linkage._private, new ConstantBitcast(callbackFn.ref(), I8_PTR)));
            nativeFnType = callbackFnType;
        }

        moduleBuilder.addFunction(callbackFn);

        String targetName = method.isSynchronized() 
                ? Symbols.synchronizedWrapperSymbol(method) 
                : Symbols.methodSymbol(method);
        FunctionRef targetFn = new FunctionRef(targetName, getFunctionType(method));
        
        // Increase the attach count for the current thread (attaches the thread
        // if not attached)
        Value env = call(callbackFn, BC_ATTACH_THREAD_FROM_CALLBACK);

        BasicBlockRef bbSuccess = callbackFn.newBasicBlockRef(new Label("success"));
        BasicBlockRef bbFailure = callbackFn.newBasicBlockRef(new Label("failure"));
        pushCallbackFrame(callbackFn, env);
        trycatchAllEnter(callbackFn, env, bbSuccess, bbFailure);
        callbackFn.newBasicBlock(bbSuccess.getLabel());

        List marshaledArgs = new ArrayList();
        
        ArrayList args = new ArrayList();
        args.add(env);
        
        if (!method.isStatic()) {
            MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, MarshalSite.RECEIVER));
            MarshaledArg marshaledArg = new MarshaledArg();
            marshaledArg.paramIndex = MarshalSite.RECEIVER;
            marshaledArgs.add(marshaledArg);
            Value arg = callbackFn.getParameterRef(0);
            String targetClassName = getInternalName(method.getDeclaringClass());
            arg = marshalNativeToObject(callbackFn, marshalerMethod, marshaledArg, env, targetClassName, arg,
                    MarshalerFlags.CALL_TYPE_CALLBACK);
            args.add(arg);
        }
        
        for (int i = 0, argIdx = 0; i < method.getParameterCount(); i++, argIdx++) {
            if (!method.isStatic() && argIdx == 0) {
                argIdx++;
            }
            Value arg = callbackFn.getParameterRef(argIdx);
            soot.Type type = method.getParameterType(i);
            
            if (needsMarshaler(type)) {
                MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, i));
                String targetClassName = getInternalName(type);

                if (arg.getType() instanceof PrimitiveType) {
                    arg = marshalNativeToValueObject(callbackFn, marshalerMethod, env, targetClassName, arg,
                            MarshalerFlags.CALL_TYPE_CALLBACK);
                } else {
                    MarshaledArg marshaledArg = new MarshaledArg();
                    marshaledArg.paramIndex = i;
                    marshaledArgs.add(marshaledArg);
                    
                    Type nativeType = nativeFnType.getParameterTypes()[argIdx];
                    if (nativeType instanceof StructureType) {
                        // Struct passed by value on the stack. Make a heap copy of the data and marshal that.
                        DataLayout dataLayout = config.getDataLayout();
                        Value heapCopy = call(callbackFn, BC_COPY_STRUCT, env, arg, 
                                new IntegerConstant(dataLayout.getAllocSize(nativeType)));
                        arg = marshalNativeToObject(callbackFn, marshalerMethod, marshaledArg, env, targetClassName, heapCopy,
                                MarshalerFlags.CALL_TYPE_CALLBACK);
                    } else {
                        arg = marshalNativeToObject(callbackFn, marshalerMethod, marshaledArg, env, targetClassName, arg,
                                MarshalerFlags.CALL_TYPE_CALLBACK);
                    }
                }
            } else {
                arg = marshalNativeToPrimitive(callbackFn, method, i, arg);
            } 
            args.add(arg);
        }
        
        Value result = call(callbackFn, targetFn, args);
        
        // Call Marshaler.updateNative() for each object that was marshaled before
        // the call.
        updateNative(method, callbackFn, env, MarshalerFlags.CALL_TYPE_CALLBACK, marshaledArgs);
        
        // Marshal the returned value to a native value before returning
        if (needsMarshaler(method.getReturnType())) {
            MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method));
            Type nativeType = callbackFn.getType().getReturnType();
            
            if (nativeType instanceof PrimitiveType) {
                result = marshalValueObjectToNative(callbackFn, marshalerMethod, nativeType, env, result,
                        MarshalerFlags.CALL_TYPE_CALLBACK);
            } else {
                result = marshalObjectToNative(callbackFn, marshalerMethod, null, nativeType, env, result,
                        MarshalerFlags.CALL_TYPE_CALLBACK);
            }
        } else {
            result = marshalPrimitiveToNative(callbackFn, method, result);
        }
        
        trycatchLeave(callbackFn, env);
        popCallbackFrame(callbackFn, env);
        call(callbackFn, BC_DETACH_THREAD_FROM_CALLBACK, env);
        callbackFn.add(new Ret(result));

        callbackFn.newBasicBlock(bbFailure.getLabel());
        trycatchLeave(callbackFn, env);
        popCallbackFrame(callbackFn, env);
        Value ex = call(callbackFn, BC_EXCEPTION_CLEAR, env);
        // Call Marshaler.updateNative() for each object that was marshaled before
        // the call.
        updateNative(method, callbackFn, env, MarshalerFlags.CALL_TYPE_CALLBACK, marshaledArgs);
        call(callbackFn, BC_DETACH_THREAD_FROM_CALLBACK, env);
        call(callbackFn, BC_THROW, env, ex);
        callbackFn.add(new Unreachable());
        
        return callbackFn;
    }

    private void updateNative(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).getAfterCallbackCallMethod();
            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.handle, value.object, 
                        new IntegerConstant(flags));
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy