예제 #1
0
 public override TypeReference GetNativeType(MarshalCodeContext context)
 {
     // TODO: Push native emitter?
     return(new PointerType(elementMarshaller.GetNativeType(context)));
 }
예제 #2
0
        public void Process(MethodDefinition methodDefinition)
        {
            var parameters = methodDefinition.Parameters.Select(CreateMarshalledParameter).ToArray();

            // If everything ended up being a BlittableMarshaller, that means we don't need to do any Marshalling
            if (parameters.All(x => x.Marshaller is BlittableMarshaller))
            {
                return;
            }

            var pinvokeMethod = new MethodDefinition(methodDefinition.Name, methodDefinition.Attributes, methodDefinition.ReturnType);

            // Move PInvokeInfo to underlying native method
            pinvokeMethod.PInvokeInfo       = methodDefinition.PInvokeInfo;
            pinvokeMethod.ImplAttributes    = methodDefinition.ImplAttributes;
            methodDefinition.PInvokeInfo    = null;
            methodDefinition.IsPInvokeImpl  = false;
            methodDefinition.ImplAttributes = MethodImplAttributes.IL;

            var context = new MarshalCodeContext(assemblyDefinition, methodDefinition, true);

            // Build method signature
            foreach (var parameter in parameters)
            {
                // Push context
                context.ManagedEmitters.Push(new ParameterMarshalledObjectEmitter(parameter.Parameter));
                if (parameter.IsByReference)
                {
                    context.ManagedEmitters.Push(new ByReferenceMarshalledObjectEmitter());
                }

                // Compute native type
                var nativeType = parameter.Marshaller.GetNativeType(context);
                if (parameter.IsByReference)
                {
                    nativeType = new ByReferenceType(nativeType);
                }

                // Add native parameter to pinvoke method
                parameter.NativeParameter = new ParameterDefinition(parameter.Parameter.Name, parameter.Parameter.Attributes, context.Assembly.MainModule.Import(nativeType));
                pinvokeMethod.Parameters.Add(parameter.NativeParameter);

                // Pop context
                if (parameter.IsByReference)
                {
                    context.ManagedEmitters.Pop();
                }
                context.ManagedEmitters.Pop();
            }

            // First, process marshallers which expect an empty stack (due to loop) and make sure they are stored in a local variable
            foreach (var parameter in parameters.Where(x => x.Marshaller.ContainsLoops || (!(x.Marshaller is BlittableMarshaller) && x.IsByReference)))
            {
                // Store in a local (if we didn't do that, we could end up having loops with things on the stack)
                var variableType = parameter.NativeParameterType;
                parameter.Variable = new VariableDefinition(variableType);
                methodDefinition.Body.Variables.Add(parameter.Variable);

                // Out-only parameter? Nothing to do...
                if (parameter.Parameter.IsOut && !parameter.Parameter.IsIn)
                {
                    continue;
                }

                // Push context
                context.ManagedEmitters.Push(new ParameterMarshalledObjectEmitter(parameter.Parameter));
                if (parameter.IsByReference)
                {
                    context.ManagedEmitters.Push(new ByReferenceMarshalledObjectEmitter());
                }
                context.NativeEmitters.Push(new VariableMarshalledObjectEmitter(parameter.Variable));

                // Convert parameter
                parameter.Marshaller.EmitStoreManagedToNative(context);

                // Pop context
                context.NativeEmitters.Pop();
                if (parameter.IsByReference)
                {
                    context.ManagedEmitters.Pop();
                }
                context.ManagedEmitters.Pop();
            }

            // Each marshaller is responsible for pushing one parameter to the stack
            foreach (var parameter in parameters)
            {
                if (parameter.Variable != null)
                {
                    // Already processed before and stored in a variable
                    context.ILProcessor.Emit(parameter.IsByReference ? OpCodes.Ldloca : OpCodes.Ldloc, parameter.Variable);
                }
                else
                {
                    // Just process and keep it on the stack
                    context.ManagedEmitters.Push(new ParameterMarshalledObjectEmitter(parameter.Parameter));
                    parameter.Marshaller.EmitStoreManagedToNative(context);
                    context.ManagedEmitters.Pop();
                }
            }

            // Emit call
            context.ILProcessor.Emit(OpCodes.Call, pinvokeMethod);

            VariableDefinition returnValue      = null;
            Marshaller         returnMarshaller = null;

            if (methodDefinition.ReturnType.MetadataType != MetadataType.Void)
            {
                returnMarshaller = Marshaller.FindMarshallerForType(methodDefinition.ReturnType, null);

                // Find native return type
                context.ManagedEmitters.Push(new FakeMarshalledObjectEmitter(methodDefinition.ReturnType));
                var nativeReturnType = returnMarshaller.GetNativeType(context);
                context.ManagedEmitters.Pop();

                // Change return type to native one
                pinvokeMethod.ReturnType = nativeReturnType;

                // Store return value in local variable
                returnValue = new VariableDefinition("returnValue", nativeReturnType);
                methodDefinition.Body.Variables.Add(returnValue);

                context.ILProcessor.Emit(OpCodes.Stloc, returnValue);
            }

            // Emit setup
            // TODO: Force Out parameter to have local variables? (source)
            foreach (var parameter in parameters)
            {
                if (parameter.Parameter.IsOut && parameter.Variable != null)
                {
                    context.NativeEmitters.Push(new VariableMarshalledObjectEmitter(parameter.Variable));
                    context.ManagedEmitters.Push(new ParameterMarshalledObjectEmitter(parameter.Parameter));
                    parameter.Marshaller.EmitStoreNativeToManaged(context);
                    context.ManagedEmitters.Pop();
                    context.NativeEmitters.Pop();
                }
            }

            // TODO: Cleanup

            // Emit return value
            if (returnValue != null)
            {
                // Convert return value to managed type
                context.NativeEmitters.Push(new VariableMarshalledObjectEmitter(returnValue));
                returnMarshaller.EmitStoreNativeToManaged(context);
                context.NativeEmitters.Pop();
            }
            context.ILProcessor.Emit(OpCodes.Ret);

            // Add method to type
            methodDefinition.DeclaringType.Methods.Add(pinvokeMethod);

            methodDefinition.Body.UpdateInstructionOffsets();
        }