// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Buffers.Binary; using Internal.IL.Stubs; using Internal.IL; using Debug = System.Diagnostics.Debug; using ILLocalVariable = Internal.IL.Stubs.ILLocalVariable; using Internal.TypeSystem.Ecma; using System.Reflection.Metadata; namespace Internal.TypeSystem.Interop { internal partial class Marshaller { protected static Marshaller CreateMarshaller(MarshallerKind kind) { switch (kind) { case MarshallerKind.Enum: case MarshallerKind.BlittableValue: case MarshallerKind.BlittableStruct: case MarshallerKind.UnicodeChar: return new BlittableValueMarshaller(); case MarshallerKind.BlittableStructPtr: return new BlittableStructPtrMarshaller(); case MarshallerKind.AnsiChar: return new AnsiCharMarshaller(); case MarshallerKind.Array: return new ArrayMarshaller(); case MarshallerKind.BlittableArray: return new BlittableArrayMarshaller(); case MarshallerKind.Bool: case MarshallerKind.CBool: return new BooleanMarshaller(); case MarshallerKind.VariantBool: return new BooleanMarshaller((short)-1); case MarshallerKind.AnsiString: return new AnsiStringMarshaller(); case MarshallerKind.UTF8String: return new UTF8StringMarshaller(); case MarshallerKind.UnicodeString: return new UnicodeStringMarshaller(); case MarshallerKind.AnsiBSTRString: return new AnsiBSTRStringMarshaller(); case MarshallerKind.BSTRString: return new BSTRStringMarshaller(); case MarshallerKind.SafeHandle: return new SafeHandleMarshaller(); case MarshallerKind.UnicodeStringBuilder: return new StringBuilderMarshaller(isAnsi: false); case MarshallerKind.AnsiStringBuilder: return new StringBuilderMarshaller(isAnsi: true); case MarshallerKind.VoidReturn: return new VoidReturnMarshaller(); case MarshallerKind.FunctionPointer: return new DelegateMarshaller(); case MarshallerKind.Struct: case MarshallerKind.Decimal: return new StructMarshaller(); case MarshallerKind.ByValAnsiString: return new ByValAnsiStringMarshaller(); case MarshallerKind.ByValUnicodeString: return new ByValUnicodeStringMarshaller(); case MarshallerKind.ByValAnsiCharArray: case MarshallerKind.ByValArray: return new ByValArrayMarshaller(); case MarshallerKind.AnsiCharArray: return new AnsiCharArrayMarshaller(); case MarshallerKind.HandleRef: return new HandleRefMarshaller(); case MarshallerKind.LayoutClass: return new LayoutClassMarshaler(); case MarshallerKind.LayoutClassPtr: return new LayoutClassPtrMarshaller(); case MarshallerKind.AsAnyA: return new AsAnyMarshaller(isAnsi: true); case MarshallerKind.AsAnyW: return new AsAnyMarshaller(isAnsi: false); case MarshallerKind.ComInterface: return new ComInterfaceMarshaller(); case MarshallerKind.OleDateTime: return new OleDateTimeMarshaller(); case MarshallerKind.OleCurrency: return new OleCurrencyMarshaller(); case MarshallerKind.FailedTypeLoad: return new FailedTypeLoadMarshaller(); case MarshallerKind.Variant: return new VariantMarshaller(); case MarshallerKind.CustomMarshaler: return new CustomTypeMarshaller(); case MarshallerKind.BlittableValueClassByRefReturn: return new BlittableValueClassByRefReturn(); default: // ensures we don't throw during create marshaller. We will throw NSE // during EmitIL which will be handled and an Exception method body // will be emitted. return new NotSupportedMarshaller(); } } } internal sealed class AnsiCharArrayMarshaller : ArrayMarshaller { protected override void AllocManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "AllocMemoryForAnsiCharArray"u8); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "WideCharArrayToAnsiCharArray"u8); LoadManagedValue(codeStream); LoadNativeValue(codeStream); codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "AnsiCharArrayToWideCharArray"u8); LoadNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } } internal sealed class AnsiCharMarshaller : Marshaller { protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "WideCharToAnsiChar"u8); LoadManagedValue(codeStream); codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "AnsiCharToWideChar"u8); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } } internal sealed class StringBuilderMarshaller : Marshaller { private bool _isAnsi; public StringBuilderMarshaller(bool isAnsi) { _isAnsi = isAnsi; } internal override bool CleanupRequired { get { return true; } } internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) { codeStream.Emit(ILOpcode.call, emitter.NewToken( InteropTypes.GetMarshal(Context).GetKnownMethod("FreeCoTaskMem"u8, null))); } protected override void AllocNativeToManaged(ILCodeStream codeStream) { var emitter = _ilCodeStreams.Emitter; var lNull = emitter.NewCodeLabel(); // Check for null LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); codeStream.Emit(ILOpcode.newobj, emitter.NewToken( ManagedType.GetParameterlessConstructor())); StoreManagedValue(codeStream); codeStream.EmitLabel(lNull); } protected override void AllocManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ReadOnlySpan<byte> helperMethodName = _isAnsi ? "AllocMemoryForAnsiStringBuilder"u8 : "AllocMemoryForUnicodeStringBuilder"u8; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, helperMethodName); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ReadOnlySpan<byte> helperMethodName = _isAnsi ? "StringBuilderToAnsiString"u8 : "StringBuilderToUnicodeString"u8; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, helperMethodName); LoadManagedValue(codeStream); LoadNativeValue(codeStream); if (_isAnsi) { codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); } codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ReadOnlySpan<byte> helperMethodName = _isAnsi ? "AnsiStringToStringBuilder"u8 : "UnicodeStringToStringBuilder"u8; var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, helperMethodName); LoadNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken(helper)); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken( InteropTypes.GetMarshal(Context).GetKnownMethod("FreeCoTaskMem"u8, null))); } } internal sealed class HandleRefMarshaller : Marshaller { protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) { LoadManagedAddr(codeStream); codeStream.Emit(ILOpcode.ldfld, _ilCodeStreams.Emitter.NewToken(InteropTypes.GetHandleRef(Context).GetKnownField("_handle"u8))); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ThrowHelper.ThrowMarshalDirectiveException(); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ThrowHelper.ThrowMarshalDirectiveException(); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { LoadManagedAddr(codeStream); codeStream.Emit(ILOpcode.ldfld, _ilCodeStreams.Emitter.NewToken(InteropTypes.GetHandleRef(Context).GetKnownField("_wrapper"u8))); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken(InteropTypes.GetGC(Context).GetKnownMethod("KeepAlive"u8, null))); } } internal sealed class StructMarshaller : Marshaller { protected override void AllocManagedToNative(ILCodeStream codeStream) { LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.initobj, _ilCodeStreams.Emitter.NewToken(NativeType)); } protected override void TransformManagedToNative(ILCodeStream codeStream) { LoadManagedAddr(codeStream); LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingManagedToNativeThunk(ManagedType))); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { LoadNativeAddr(codeStream); LoadManagedAddr(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingNativeToManagedThunk(ManagedType))); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { // Only do cleanup if it is IN if (!In) { return; } LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingCleanupThunk(ManagedType))); } } internal class ByValArrayMarshaller : ArrayMarshaller { protected FieldDesc _managedField; protected FieldDesc _nativeField; public void EmitMarshallingIL(PInvokeILCodeStreams codeStreams, FieldDesc managedField, FieldDesc nativeField) { _managedField = managedField; _nativeField = nativeField; EmitMarshallingIL(codeStreams); } protected override void EmitElementCount(ILCodeStream codeStream, MarshalDirection direction) { ILEmitter emitter = _ilCodeStreams.Emitter; if (MarshalAsDescriptor == null || !MarshalAsDescriptor.SizeConst.HasValue) { throw new InvalidProgramException("SizeConst is required for ByValArray."); } if (direction == MarshalDirection.Forward) { // In forward direction ElementCount = Min(managed.length, SizeConst); var vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); var lSmaller = emitter.NewCodeLabel(); var lDone = emitter.NewCodeLabel(); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); var lNullCheck = emitter.NewCodeLabel(); codeStream.Emit(ILOpcode.brfalse, lNullCheck); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); codeStream.Emit(ILOpcode.ldlen); codeStream.Emit(ILOpcode.conv_i4); codeStream.EmitStLoc(vLength); codeStream.EmitLabel(lNullCheck); Debug.Assert(MarshalAsDescriptor.SizeConst.HasValue); int sizeConst = (int)MarshalAsDescriptor.SizeConst.Value; codeStream.EmitLdc(sizeConst); codeStream.EmitLdLoc(vLength); codeStream.Emit(ILOpcode.blt, lSmaller); codeStream.EmitLdLoc(vLength); codeStream.Emit(ILOpcode.br, lDone); codeStream.EmitLabel(lSmaller); codeStream.EmitLdc(sizeConst); codeStream.EmitLabel(lDone); } else { // In reverse direction ElementCount = SizeConst; Debug.Assert(MarshalAsDescriptor.SizeConst.HasValue); int sizeConst = (int)MarshalAsDescriptor.SizeConst.Value; codeStream.EmitLdc(sizeConst); } } protected override void EmitMarshalFieldManagedToNative() { // It generates the following code //if (ManagedArg.Field != null) //{ // // fixed (InlineArray* pUnsafe = &NativeArg.Field) // { // uint index = 0u; // while ((ulong)index < (ulong)((long)ManagedArg.Field.Length)) // { // NativeArg.s[index] = ManagedArg.Field[(int)index]; // index += 1u; // } // } //} ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeStream codeStream = _ilCodeStreams.MarshallingCodeStream; var nativeArrayType = NativeType as InlineArrayType; Debug.Assert(nativeArrayType != null); Debug.Assert(ManagedType is ArrayType); var managedElementType = ((ArrayType)ManagedType).ElementType; ILCodeLabel lDone = emitter.NewCodeLabel(); ILCodeLabel lRangeCheck = emitter.NewCodeLabel(); ILCodeLabel lLoopHeader = emitter.NewCodeLabel(); ILLocalVariable vIndex = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); ILLocalVariable vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); ILLocalVariable vNative = emitter.NewLocal(NativeType.MakeByRefType(), isPinned: true); // check if ManagedType == null, then return codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); codeStream.Emit(ILOpcode.brfalse, lDone); codeStream.EmitLdArg(1); codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); codeStream.EmitStLoc(vNative); EmitElementCount(codeStream, MarshalDirection.Forward); codeStream.EmitStLoc(vLength); codeStream.EmitLdc(0); codeStream.EmitStLoc(vIndex); codeStream.Emit(ILOpcode.br, lRangeCheck); codeStream.EmitLabel(lLoopHeader); codeStream.EmitLdArg(1); codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); codeStream.EmitLdLoc(vIndex); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); codeStream.EmitLdLoc(vIndex); codeStream.EmitLdElem(managedElementType); // generate marshalling IL for the element GetElementMarshaller(MarshalDirection.Forward) .EmitMarshallingIL(new PInvokeILCodeStreams(_ilCodeStreams.Emitter, codeStream)); codeStream.Emit(ILOpcode.call, emitter.NewToken( nativeArrayType.GetInlineArrayMethod(InlineArrayMethodKind.Setter))); codeStream.EmitLdLoc(vIndex); codeStream.EmitLdc(1); codeStream.Emit(ILOpcode.add); codeStream.EmitStLoc(vIndex); codeStream.EmitLabel(lRangeCheck); codeStream.EmitLdLoc(vIndex); codeStream.EmitLdLoc(vLength); codeStream.Emit(ILOpcode.blt, lLoopHeader); codeStream.EmitLabel(lDone); } protected override void EmitMarshalFieldNativeToManaged() { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeStream codeStream = _ilCodeStreams.UnmarshallingCodestream; // It generates the following IL: // ManagedArg.s = new ElementType[Length]; // // for (uint index = 0u; index < Length; index += 1u) // { // ManagedArg.s[index] = NativeArg.s[index]; // } // ILCodeLabel lRangeCheck = emitter.NewCodeLabel(); ILCodeLabel lLoopHeader = emitter.NewCodeLabel(); Debug.Assert(ManagedType is ArrayType); var nativeArrayType = NativeType as InlineArrayType; Debug.Assert(nativeArrayType != null); var managedElementType = ((ArrayType)ManagedType).ElementType; ILLocalVariable vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); codeStream.EmitLdArg(1); // load the length EmitElementCount(codeStream, MarshalDirection.Reverse); codeStream.EmitStLoc(vLength); codeStream.EmitLdLoc(vLength); codeStream.Emit(ILOpcode.newarr, emitter.NewToken(managedElementType)); codeStream.Emit(ILOpcode.stfld, emitter.NewToken(_managedField)); var vIndex = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); // index = 0 codeStream.EmitLdc(0); codeStream.EmitStLoc(vIndex); codeStream.Emit(ILOpcode.br, lRangeCheck); codeStream.EmitLabel(lLoopHeader); // load managed type codeStream.EmitLdArg(1); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); codeStream.EmitLdLoc(vIndex); // load native type codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); codeStream.EmitLdLoc(vIndex); codeStream.Emit(ILOpcode.call, emitter.NewToken( nativeArrayType.GetInlineArrayMethod(InlineArrayMethodKind.Getter))); // generate marshalling IL for the element GetElementMarshaller(MarshalDirection.Reverse) .EmitMarshallingIL(new PInvokeILCodeStreams(_ilCodeStreams.Emitter, codeStream)); codeStream.EmitStElem(managedElementType); codeStream.EmitLdLoc(vIndex); codeStream.EmitLdc(1); codeStream.Emit(ILOpcode.add); codeStream.EmitStLoc(vIndex); codeStream.EmitLabel(lRangeCheck); codeStream.EmitLdLoc(vIndex); codeStream.EmitLdLoc(vLength); codeStream.Emit(ILOpcode.blt, lLoopHeader); } } internal abstract class ByValStringMarshaller : ByValArrayMarshaller { protected override void EmitElementCount(ILCodeStream codeStream, MarshalDirection direction) { if (MarshalAsDescriptor == null || !MarshalAsDescriptor.SizeConst.HasValue) { throw new InvalidProgramException("SizeConst is required for ByValString."); } codeStream.EmitLdc((int)MarshalAsDescriptor.SizeConst.Value); } protected abstract bool IsAnsi { get; } protected abstract MethodDesc GetManagedToNativeHelper(); protected abstract MethodDesc GetNativeToManagedHelper(); protected override void EmitMarshalFieldManagedToNative() { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeStream codeStream = _ilCodeStreams.MarshallingCodeStream; var nativeArrayType = NativeType as InlineArrayType; Debug.Assert(nativeArrayType != null); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); codeStream.EmitLdArg(1); codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); codeStream.Emit(ILOpcode.conv_u); EmitElementCount(codeStream, MarshalDirection.Forward); if (IsAnsi) { codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); } codeStream.Emit(ILOpcode.call, emitter.NewToken(GetManagedToNativeHelper())); } protected override void EmitMarshalFieldNativeToManaged() { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeStream codeStream = _ilCodeStreams.UnmarshallingCodestream; codeStream.EmitLdArg(1); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); codeStream.Emit(ILOpcode.conv_u); EmitElementCount(codeStream, MarshalDirection.Reverse); codeStream.Emit(ILOpcode.call, emitter.NewToken(GetNativeToManagedHelper())); codeStream.Emit(ILOpcode.stfld, emitter.NewToken(_managedField)); } } internal sealed class ByValAnsiStringMarshaller : ByValStringMarshaller { protected override bool IsAnsi { get { return true; } } protected override MethodDesc GetManagedToNativeHelper() { return Context.GetHelperEntryPoint("InteropHelpers"u8, "StringToByValAnsiString"u8); } protected override MethodDesc GetNativeToManagedHelper() { return Context.GetHelperEntryPoint("InteropHelpers"u8, "ByValAnsiStringToString"u8); } } internal sealed class ByValUnicodeStringMarshaller : ByValStringMarshaller { protected override bool IsAnsi { get { return false; } } protected override MethodDesc GetManagedToNativeHelper() { return Context.GetHelperEntryPoint("InteropHelpers"u8, "StringToUnicodeFixedArray"u8); } protected override MethodDesc GetNativeToManagedHelper() { return Context.GetHelperEntryPoint("InteropHelpers"u8, "UnicodeToStringFixedArray"u8); } } internal sealed class LayoutClassMarshaler : Marshaller { protected override void AllocManagedToNative(ILCodeStream codeStream) { LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.initobj, _ilCodeStreams.Emitter.NewToken(NativeType)); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadManagedValue(codeStream); LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingManagedToNativeThunk(ManagedType))); codeStream.EmitLabel(lNull); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNonNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brtrue, lNonNull); codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(ManagedType)); codeStream.Emit(ILOpcode.call, emitter.NewToken(InteropTypes.GetType(Context).GetMethod("GetTypeFromHandle"u8, null))); codeStream.Emit(ILOpcode.call, emitter.NewToken(InteropTypes.GetRuntimeHelpers(Context).GetKnownMethod("GetUninitializedObject"u8, null))); StoreManagedValue(codeStream); codeStream.EmitLabel(lNonNull); LoadNativeAddr(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingNativeToManagedThunk(ManagedType))); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { // Only do cleanup if it is IN if (!In) { return; } LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingCleanupThunk(ManagedType))); } } internal sealed class LayoutClassPtrMarshaller : Marshaller { // Blittable layout classes passed by value CLR->native are marshalled by pinning the managed // object and passing an interior pointer to its data directly to native code, mirroring CoreCLR's // ILBlittablePtrMarshaller. This preserves pointer identity and gives implicit [In,Out] semantics // regardless of [In]/[Out]. Byref, reverse, return, field, and non-blittable scenarios instead copy // through the struct marshalling thunks. private bool MarshalViaPinning => MarshalDirection == MarshalDirection.Forward && !IsManagedByRef && !Return && MarshallerType == MarshallerType.Argument && MarshalUtils.IsBlittableType(ManagedType); protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) { if (!MarshalViaPinning) { base.AllocAndTransformManagedToNative(codeStream); return; } ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); // Default the native value to null so a null managed reference marshals as a null pointer. codeStream.EmitLdc(0); codeStream.Emit(ILOpcode.conv_i); StoreNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); // Pin the object and pass the address of its first field (the object's data) to native code. ILLocalVariable vPinnedObject = emitter.NewLocal(ManagedType, isPinned: true); LoadManagedValue(codeStream); codeStream.EmitStLoc(vPinnedObject); FieldDesc rawDataField = Context.SystemModule .GetKnownType("System.Runtime.CompilerServices"u8, "RawData"u8) .GetKnownField("Data"u8); codeStream.EmitLdLoc(vPinnedObject); codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(rawDataField)); codeStream.Emit(ILOpcode.conv_i); StoreNativeValue(codeStream); codeStream.EmitLabel(lNull); } protected override void AllocManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); codeStream.EmitLdc(0); codeStream.Emit(ILOpcode.conv_i); StoreNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); TypeDesc nativeStructType = InteropStateManager.GetStructMarshallingNativeType(ManagedType); ILLocalVariable lNativeType = emitter.NewLocal(nativeStructType); codeStream.EmitLdLoca(lNativeType); codeStream.Emit(ILOpcode.initobj, emitter.NewToken(nativeStructType)); codeStream.EmitLdLoca(lNativeType); StoreNativeValue(codeStream); codeStream.EmitLabel(lNull); } protected override void AllocNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(ManagedType)); codeStream.Emit(ILOpcode.call, emitter.NewToken(InteropTypes.GetType(Context).GetMethod("GetTypeFromHandle"u8, null))); codeStream.Emit(ILOpcode.call, emitter.NewToken(InteropTypes.GetRuntimeHelpers(Context).GetKnownMethod("GetUninitializedObject"u8, null))); StoreManagedValue(codeStream); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadManagedValue(codeStream); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingManagedToNativeThunk(ManagedType))); codeStream.EmitLabel(lNull); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { // When marshalling via pinning the native code operates directly on the managed object's // memory, so there is nothing to copy back. if (MarshalViaPinning) { return; } ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( InteropStateManager.GetStructMarshallingNativeToManagedThunk(ManagedType))); codeStream.EmitLabel(lNull); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { // Pinning does not allocate any native resources that require cleanup. if (MarshalViaPinning) { return; } // Only do cleanup if it is IN if (!In) { return; } ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken( InteropStateManager.GetStructMarshallingCleanupThunk(ManagedType))); codeStream.EmitLabel(lNull); } } internal sealed class AsAnyMarshaller : Marshaller { // This flag affects encoding of string, StringBuilder and Char array marshalling. // It does not affect LayoutClass marshalling. // Note that the CoreLib portion of the marshaller currently only supports LayoutClass. private readonly bool _isAnsi; public AsAnyMarshaller(bool isAnsi) { _isAnsi = isAnsi; } protected override void AllocManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); ILLocalVariable lSize = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); MethodDesc getNativeSizeHelper = Context.GetHelperEntryPoint("InteropHelpers"u8, "AsAnyGetNativeSize"u8); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken(getNativeSizeHelper)); codeStream.Emit(ILOpcode.dup); codeStream.EmitStLoc(lSize); codeStream.Emit(ILOpcode.localloc); codeStream.Emit(ILOpcode.dup); StoreNativeValue(codeStream); codeStream.EmitLdc(0); codeStream.EmitLdLoc(lSize); codeStream.Emit(ILOpcode.initblk); codeStream.EmitLabel(lNull); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadManagedValue(codeStream); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( Context.GetHelperEntryPoint("InteropHelpers"u8, "AsAnyMarshalManagedToNative"u8))); codeStream.EmitLabel(lNull); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( Context.GetHelperEntryPoint("InteropHelpers"u8, "AsAnyMarshalNativeToManaged"u8))); codeStream.EmitLabel(lNull); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { // Only do cleanup if it is IN if (!In) { return; } ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); LoadNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken( Context.GetHelperEntryPoint("InteropHelpers"u8, "AsAnyCleanupNative"u8))); codeStream.EmitLabel(lNull); } } internal sealed class ComInterfaceMarshaller : Marshaller { protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadManagedValue(codeStream); var parameterType = this.ManagedParameterType; if (parameterType.IsByRef) { parameterType = ((ByRefType)this.ManagedParameterType).ParameterType; } CustomAttributeValue<TypeDesc>? guidAttributeValue = (parameterType as EcmaType)? .GetDecodedCustomAttribute("System.Runtime.InteropServices", "GuidAttribute"); if (guidAttributeValue != null) { var guidValue = (string)guidAttributeValue.Value.FixedArguments[0].Value; Span<byte> bytes = Guid.Parse(guidValue).ToByteArray(); codeStream.EmitLdc(BinaryPrimitives.ReadInt32LittleEndian(bytes)); codeStream.EmitLdc(BinaryPrimitives.ReadInt16LittleEndian(bytes.Slice(4))); codeStream.EmitLdc(BinaryPrimitives.ReadInt16LittleEndian(bytes.Slice(6))); for (int i = 8; i < 16; i++) codeStream.EmitLdc(bytes[i]); MetadataType guidType = Context.SystemModule.GetKnownType("System"u8, "Guid"u8); var int32Type = Context.GetWellKnownType(WellKnownType.Int32); var int16Type = Context.GetWellKnownType(WellKnownType.Int16); var byteType = Context.GetWellKnownType(WellKnownType.Byte); var sig = new MethodSignature( MethodSignatureFlags.None, genericParameterCount: 0, returnType: Context.GetWellKnownType(WellKnownType.Void), parameters: new TypeDesc[] { int32Type, int16Type, int16Type, byteType, byteType, byteType, byteType, byteType, byteType, byteType, byteType }); MethodDesc guidCtorHandleMethod = guidType.GetKnownMethod(".ctor"u8, sig); codeStream.Emit(ILOpcode.newobj, emitter.NewToken(guidCtorHandleMethod)); MethodDesc helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "ConvertManagedComInterfaceToNative"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } else { if (!parameterType.IsObject) { throw new NotSupportedException(); } MethodDesc helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "ConvertManagedComInterfaceToIUnknown"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeValue(codeStream); MethodDesc helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "ConvertNativeComInterfaceToManaged"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } protected override void TransformManagedToNative(ILCodeStream codeStream) { throw new NotSupportedException(); } } internal sealed class AnsiBSTRStringMarshaller : Marshaller { internal override bool CleanupRequired => true; internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) { var helper = InteropTypes.GetMarshal(Context).GetKnownMethod("FreeBSTR"u8, null); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadManagedValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "StringToAnsiBstrBuffer"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "AnsiBstrBufferToString"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } } internal sealed class BSTRStringMarshaller : Marshaller { internal override bool CleanupRequired => true; internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) { var helper = InteropTypes.GetMarshal(Context).GetKnownMethod("FreeBSTR"u8, null); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadManagedValue(codeStream); var helper = InteropTypes.GetMarshal(Context).GetKnownMethod("StringToBSTR"u8, null); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "BstrBufferToString"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } } internal sealed class OleDateTimeMarshaller : Marshaller { protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadManagedValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "DateTimeToOleDateTime"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "OleDateTimeToDateTime"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } } internal sealed class OleCurrencyMarshaller : Marshaller { protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadManagedValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "DecimalToOleCurrency"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeValue(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "OleCurrencyToDecimal"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } } internal sealed class FailedTypeLoadMarshaller : Marshaller { protected override void TransformManagedToNative(ILCodeStream codeStream) { ThrowHelper.ThrowTypeLoadException(ManagedType); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ThrowHelper.ThrowTypeLoadException(ManagedType); } } internal sealed class VariantMarshaller : Marshaller { protected override void AllocManagedToNative(ILCodeStream codeStream) { LoadNativeAddr(codeStream); codeStream.Emit(ILOpcode.initobj, _ilCodeStreams.Emitter.NewToken(NativeType)); } protected override void TransformManagedToNative(ILCodeStream codeStream) { if (this.MarshalDirection == MarshalDirection.Reverse) { throw new NotSupportedException(); } ILEmitter emitter = _ilCodeStreams.Emitter; LoadManagedValue(codeStream); LoadNativeAddr(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "ConvertObjectToVariant"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { if (this.MarshalDirection == MarshalDirection.Reverse) { throw new NotSupportedException(); } ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeAddr(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "VariantToObject"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreManagedValue(codeStream); } protected override void EmitCleanupManaged(ILCodeStream codeStream) { // Only do cleanup if it is IN if (!In) { return; } ILEmitter emitter = _ilCodeStreams.Emitter; LoadNativeAddr(codeStream); var helper = Context.GetHelperEntryPoint("InteropHelpers"u8, "CleanupVariant"u8); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); } } #pragma warning disable CA1852 internal class CustomTypeMarshaller : Marshaller #pragma warning restore CA1852 { private ILLocalVariable MarshallerLocalVariable = (ILLocalVariable)(-1); private ILLocalVariable InitializeMarshallerVariable() { if (MarshallerLocalVariable != (ILLocalVariable)(-1)) { return MarshallerLocalVariable; } var marshallerType = MarshalAsDescriptor.MarshallerType; if (marshallerType.IsGenericDefinition) { ThrowHelper.ThrowTypeLoadException(marshallerType); } var customMarshallerType = Context.SystemModule.GetKnownType("System.Runtime.InteropServices"u8, "ICustomMarshaler"u8); var getInstanceMethod = marshallerType.GetMethod( "GetInstance"u8, new MethodSignature(MethodSignatureFlags.Static, 0, customMarshallerType, [ Context.GetWellKnownType(WellKnownType.String) ])); if (ManagedType.IsValueType || ManagedType.IsPointer || ManagedType.IsFunctionPointer) { ThrowHelper.ThrowMarshalDirectiveException(); } var initializeCustomMarshallerMethod = Context.GetHelperEntryPoint("InteropHelpers"u8, "InitializeCustomMarshaller"u8); ILEmitter emitter = _ilCodeStreams.Emitter; MarshallerLocalVariable = emitter.NewLocal(customMarshallerType); var cookie = MarshalAsDescriptor.Cookie; // Custom marshaller initialization should not be caught, so initialize early ILCodeStream fnptrLoadStream = _ilCodeStreams.FunctionPointerLoadStream; fnptrLoadStream.Emit(ILOpcode.ldtoken, emitter.NewToken(ManagedType)); fnptrLoadStream.Emit(ILOpcode.ldtoken, emitter.NewToken(marshallerType)); fnptrLoadStream.Emit(ILOpcode.ldstr, emitter.NewToken(cookie)); if (getInstanceMethod != null) { fnptrLoadStream.Emit(ILOpcode.ldftn, emitter.NewToken(getInstanceMethod)); } else { fnptrLoadStream.EmitLdc(0); fnptrLoadStream.Emit(ILOpcode.conv_i); } fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken(initializeCustomMarshallerMethod)); fnptrLoadStream.EmitStLoc(MarshallerLocalVariable); return MarshallerLocalVariable; } protected override void TransformManagedToNative(ILCodeStream codeStream) { var lMarshaller = InitializeMarshallerVariable(); var customMarshallerType = Context.SystemModule.GetKnownType("System.Runtime.InteropServices"u8, "ICustomMarshaler"u8); ILEmitter emitter = _ilCodeStreams.Emitter; var manageToNativeMethod = customMarshallerType.GetKnownMethod( "MarshalManagedToNative"u8, new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.IntPtr), [ Context.GetWellKnownType(WellKnownType.Object) ])); codeStream.EmitLdLoc(lMarshaller); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(manageToNativeMethod)); StoreNativeValue(codeStream); if (MarshalDirection == MarshalDirection.Forward) { if (In && Out) { EmitCleanUpManagedData(codeStream); } EmitCleanUpNativeData(_ilCodeStreams.CleanupCodeStream); } else { EmitCleanUpManagedData(codeStream); } } protected override void TransformNativeToManaged(ILCodeStream codeStream) { var lMarshaller = InitializeMarshallerVariable(); var customMarshallerType = Context.SystemModule.GetKnownType("System.Runtime.InteropServices"u8, "ICustomMarshaler"u8); ILEmitter emitter = _ilCodeStreams.Emitter; var marshalNativeToManagedMethod = customMarshallerType.GetKnownMethod( "MarshalNativeToManaged"u8, new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Object), [ Context.GetWellKnownType(WellKnownType.IntPtr) ])); codeStream.EmitLdLoc(lMarshaller); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(marshalNativeToManagedMethod)); StoreManagedValue(codeStream); } protected void EmitCleanUpManagedData(ILCodeStream codeStream) { var lMarshaller = InitializeMarshallerVariable(); var customMarshallerType = Context.SystemModule.GetKnownType("System.Runtime.InteropServices"u8, "ICustomMarshaler"u8); ILEmitter emitter = _ilCodeStreams.Emitter; // Call CleanUpManagedData on cleanup code stream. var cleanupManagedDataMethod = customMarshallerType.GetKnownMethod( "CleanUpManagedData"u8, new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Void), [ Context.GetWellKnownType(WellKnownType.Object) ])); codeStream.EmitLdLoc(lMarshaller); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(cleanupManagedDataMethod)); } protected void EmitCleanUpNativeData(ILCodeStream codeStream) { var lMarshaller = InitializeMarshallerVariable(); var customMarshallerType = Context.SystemModule.GetKnownType("System.Runtime.InteropServices"u8, "ICustomMarshaler"u8); ILEmitter emitter = _ilCodeStreams.Emitter; // Call CleanUpNativeData on cleanup code stream. var cleanupNativeDataMethod = customMarshallerType.GetKnownMethod( "CleanUpNativeData"u8, new MethodSignature(MethodSignatureFlags.None, 0, Context.GetWellKnownType(WellKnownType.Void), [ Context.GetWellKnownType(WellKnownType.IntPtr) ])); codeStream.EmitLdLoc(lMarshaller); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(cleanupNativeDataMethod)); } } internal sealed class BlittableValueClassByRefReturn : Marshaller { protected override void SetupArgumentsForReturnValueMarshalling() { ILEmitter emitter = _ilCodeStreams.Emitter; _managedHome = new Home(emitter.NewLocal(ManagedParameterType), ManagedParameterType, isByRef: false); _nativeHome = new Home(emitter.NewLocal(NativeType), NativeType, isByRef: false); } } }