From bc485c27cd6217e0af8a9a22586812e477656c6d Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 8 Jul 2022 12:02:01 -0700 Subject: [PATCH 1/7] Update to string marshallers to new V2 shape. --- .../TypeSystem/Interop/IL/Marshaller.cs | 40 ++-- src/coreclr/vm/assemblynative.cpp | 3 - src/coreclr/vm/clsload.cpp | 55 +++++- src/coreclr/vm/corelib.h | 23 ++- src/coreclr/vm/ilmarshalers.cpp | 33 ++-- src/coreclr/vm/metasig.h | 5 +- .../src/ILLink/ILLink.Descriptors.Shared.xml | 9 + .../Marshalling/AnsiStringMarshaller.cs | 149 +++++++------- .../Marshalling/BStrStringMarshaller.cs | 182 ++++++++++-------- .../Marshalling/Utf16StringMarshaller.cs | 55 ++---- .../Marshalling/Utf8StringMarshaller.cs | 153 ++++++++------- .../ICustomNativeTypeMarshallingStrategy.cs | 6 +- .../StatelessMarshallingStrategy.cs | 6 +- .../ref/System.Runtime.InteropServices.cs | 119 +++++++----- 14 files changed, 476 insertions(+), 362 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs index 0e38a100098b7a..d4bec01421e75d 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs @@ -1654,7 +1654,9 @@ class UTF8StringMarshaller : Marshaller private ILLocalVariable? _marshallerInstance = null; - private TypeDesc Marshaller => Context.SystemModule.GetKnownType("System.Runtime.InteropServices.Marshalling", "Utf8StringMarshaller"); + private MetadataType Marshaller => Context.SystemModule.GetKnownType("System.Runtime.InteropServices.Marshalling", "Utf8StringMarshaller"); + + private MetadataType MarshallerIn => Marshaller.GetNestedType("ManagedToUnmanagedIn"); internal override bool CleanupRequired => true; @@ -1669,10 +1671,10 @@ internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emi protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; - TypeDesc marshaller = Marshaller; + TypeDesc marshallerIn = MarshallerIn; if (_marshallerInstance == null) - _marshallerInstance = emitter.NewLocal(marshaller); + _marshallerInstance = emitter.NewLocal(marshallerIn); if (In && !Out && !IsManagedByRef) { @@ -1681,6 +1683,8 @@ protected override void TransformManagedToNative(ILCodeStream codeStream) codeStream.Emit(ILOpcode.localloc); codeStream.EmitStLoc(vBuffer); + codeStream.EmitLdLoca(_marshallerInstance.Value); + LoadManagedValue(codeStream); // Create ReadOnlySpan from the stack-allocated buffer @@ -1693,40 +1697,26 @@ protected override void TransformManagedToNative(ILCodeStream codeStream) codeStream.Emit(ILOpcode.newobj, emitter.NewToken(spanOfByte.GetKnownMethod(".ctor", new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void), new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Void).MakePointerType(), Context.GetWellKnownType(WellKnownType.Int32) })))); + codeStream.Emit(ILOpcode.call, emitter.NewToken(marshallerIn.GetKnownMethod("FromManaged", null))); - codeStream.Emit(ILOpcode.newobj, emitter.NewToken(marshaller.GetKnownMethod(".ctor", - new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void), - new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String), spanOfByte })))); - codeStream.EmitStLoc(_marshallerInstance.Value); + codeStream.EmitLdLoca(_marshallerInstance.Value); + codeStream.Emit(ILOpcode.call, emitter.NewToken(marshallerIn.GetKnownMethod("ToUnmanaged", null))); } else { LoadManagedValue(codeStream); - codeStream.Emit(ILOpcode.newobj, emitter.NewToken(marshaller.GetKnownMethod(".ctor", - new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void), - new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String) })))); - codeStream.EmitStLoc(_marshallerInstance.Value); + codeStream.Emit(ILOpcode.call, emitter.NewToken(Marshaller.GetKnownMethod("ConvertToUnmanaged", null))); } - codeStream.EmitLdLoca(_marshallerInstance.Value); - codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("ToNativeValue", null))); StoreNativeValue(codeStream); } protected override void TransformNativeToManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; - TypeDesc marshaller = Marshaller; - - if (_marshallerInstance == null) - _marshallerInstance = emitter.NewLocal(marshaller); - codeStream.EmitLdLoca(_marshallerInstance.Value); LoadNativeValue(codeStream); - codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("FromNativeValue", null))); - - codeStream.EmitLdLoca(_marshallerInstance.Value); - codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("ToManaged", null))); + codeStream.Emit(ILOpcode.call, emitter.NewToken(Marshaller.GetKnownMethod("ConvertToManaged", null))); StoreManagedValue(codeStream); } @@ -1740,15 +1730,13 @@ protected override void EmitCleanupManaged(ILCodeStream codeStream) codeStream.EmitLdLoca(_marshallerInstance.Value); codeStream.Emit(ILOpcode.call, emitter.NewToken( - Marshaller.GetKnownMethod("FreeNative", null))); + MarshallerIn.GetKnownMethod("Free", null))); } else { - // The marshaller instance is not guaranteed to be initialized with the latest native value. - // Free the native value directly. LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken( - InteropTypes.GetMarshal(Context).GetKnownMethod("FreeCoTaskMem", null))); + Marshaller.GetKnownMethod("Free", null))); } } } diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 4dd0a823930169..fa30855cbbc3cb 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -355,9 +355,6 @@ extern "C" void QCALLTYPE AssemblyNative_GetType(QCall::AssemblyHandle pAssembly BEGIN_QCALL; - if (!wszName) - COMPlusThrowArgumentNull(W("name"), W("ArgumentNull_String")); - BOOL prohibitAsmQualifiedName = TRUE; AssemblyBinder * pBinder = NULL; diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index daebf64ea9f30e..1b2830e28e4699 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -275,6 +275,28 @@ BOOL ClassLoader::IsTypicalInstantiation(Module *pModule, mdToken token, Instant return TRUE; } +namespace +{ + TypeHandle LoadTypeByNameThrowingWorker( + ClassLoader* classLoader, + NameHandle* nameHandle, + ClassLoader::NotFoundAction fNotFound, + ClassLoader::LoadTypesFlag fLoadTypes, + ClassLoadLevel level) + { + WRAPPER_NO_CONTRACT; // The contract is enforced in caller. + _ASSERTE(classLoader != NULL); + _ASSERTE(nameHandle != NULL); + + if (fLoadTypes == ClassLoader::DontLoadTypes) + nameHandle->SetTokenNotToLoad(tdAllTypes); + if (fNotFound == ClassLoader::ThrowIfNotFound) + return classLoader->LoadTypeHandleThrowIfFailed(nameHandle, level); + else + return classLoader->LoadTypeHandleThrowing(nameHandle, level); + } +} + // External class loader entry point: load a type by name /*static*/ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly, @@ -305,13 +327,32 @@ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly, } CONTRACT_END - NameHandle nameHandle(nameSpace, name); - if (fLoadTypes == DontLoadTypes) - nameHandle.SetTokenNotToLoad(tdAllTypes); - if (fNotFound == ThrowIfNotFound) - RETURN pAssembly->GetLoader()->LoadTypeHandleThrowIfFailed(&nameHandle, level); - else - RETURN pAssembly->GetLoader()->LoadTypeHandleThrowing(&nameHandle, level); + ClassLoader* classLoader = pAssembly->GetLoader(); + + NameHandle nameHandle; + LPCUTF8 nestedTypeMaybe = strchr(name, '+'); + if (nestedTypeMaybe == NULL) + { + nameHandle = NameHandle(nameSpace, name); + RETURN LoadTypeByNameThrowingWorker(classLoader, &nameHandle, fNotFound, fLoadTypes, level); + } + + // Handle the nested type scenario. + // The same NameHandle must be used to retain the scope to look for the nested type. + nameHandle = NameHandle(pAssembly->GetModule(), mdtBaseType); + + SString splitName(SString::Utf8, name, (COUNT_T)(nestedTypeMaybe - name)); + nameHandle.SetName(nameSpace, splitName.GetUTF8()); + + // The side-effect of updating the scope in the NameHandle is the point of the call. + (void)LoadTypeByNameThrowingWorker(classLoader, &nameHandle, fNotFound, fLoadTypes, level); + + // Now load the nested type. + nameHandle.SetName(NULL, nestedTypeMaybe + 1); + + // We don't support nested types in nested types. + _ASSERTE(strchr(nameHandle.GetName(), '+') == NULL); + RETURN LoadTypeByNameThrowingWorker(classLoader, &nameHandle, fNotFound, fLoadTypes, level); } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index c7ea9e3c40fedd..5d85eb47bd28dc 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -74,6 +74,8 @@ // See usage in this file itself and on the link (the assembly name for feature switch in this file will be System.Private.CoreLib), // https://github.com/dotnet/designs/blob/main/accepted/2020/feature-switch.md#generate-the-right-input-for-the-linker-in-sdk // +// The FOR_ILLINK define is set when this file is being processed for the IL linker. +// #ifndef BEGIN_ILLINK_FEATURE_SWITCH #define BEGIN_ILLINK_FEATURE_SWITCH(featureName, featureValue, featureDefault) #endif @@ -1188,12 +1190,21 @@ DEFINE_METHOD(ICASTABLEHELPERS, GETIMPLTYPE, GetImplType, SM_ICast #endif // FEATURE_ICASTABLE DEFINE_CLASS(UTF8STRINGMARSHALLER, Marshalling, Utf8StringMarshaller) -DEFINE_METHOD(UTF8STRINGMARSHALLER, CTOR, .ctor, IM_Str_RetVoid) -DEFINE_METHOD(UTF8STRINGMARSHALLER, CTOR_SPAN, .ctor, IM_Str_SpanOfByte_RetVoid) -DEFINE_METHOD(UTF8STRINGMARSHALLER, TO_NATIVE_VALUE, ToNativeValue, IM_RetPtrByte) -DEFINE_METHOD(UTF8STRINGMARSHALLER, FROM_NATIVE_VALUE, FromNativeValue, IM_PtrByte_RetVoid) -DEFINE_METHOD(UTF8STRINGMARSHALLER, TO_MANAGED, ToManaged, IM_RetStr) -DEFINE_METHOD(UTF8STRINGMARSHALLER, FREE_NATIVE, FreeNative, IM_RetVoid) +DEFINE_METHOD(UTF8STRINGMARSHALLER, CONVERT_TO_MANAGED, ConvertToManaged, SM_PtrByte_RetStr) +DEFINE_METHOD(UTF8STRINGMARSHALLER, CONVERT_TO_UNMANAGED, ConvertToUnmanaged, SM_Str_RetPtrByte) +DEFINE_METHOD(UTF8STRINGMARSHALLER, FREE, Free, SM_PtrByte_RetVoid) + +// The generator for the linker XML doesn't understand inner classes so generation +// needs to skip the following type. +// See https://github.com/dotnet/runtime/issues/71847 +#ifndef FOR_ILLINK +DEFINE_CLASS(UTF8STRINGMARSHALLER_IN, Marshalling, Utf8StringMarshaller+ManagedToUnmanagedIn) +DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FROM_MANAGED, FromManaged, IM_Str_SpanOfByte_RetVoid) +DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, TO_UNMANAGED, ToUnmanaged, IM_RetPtrByte) +DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FROM_UNMANAGED, FromUnmanaged, IM_PtrByte_RetVoid) +DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, TO_MANAGED, ToManaged, IM_RetStr) +DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FREE, Free, IM_RetVoid) +#endif // FOR_ILLINK DEFINE_CLASS(UTF8BUFFERMARSHALER, StubHelpers, UTF8BufferMarshaler) DEFINE_METHOD(UTF8BUFFERMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, NoSig) diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index 96914f90d31575..ba39c76c248dbc 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -1959,7 +1959,7 @@ void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit) STANDARD_VM_CONTRACT; if (m_dwInstance == LOCAL_NUM_UNUSED) - m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER))); + m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER_IN))); bool bPassByValueInOnly = IsIn(m_dwMarshalFlags) && !IsOut(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags); if (bPassByValueInOnly) @@ -1969,8 +1969,13 @@ void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit) pslILEmit->EmitLOCALLOC(); pslILEmit->EmitSTLOC(dwBuffer); + // Load the marshaller instance. + pslILEmit->EmitLDLOCA(m_dwInstance); + + // Argument 1 EmitLoadManagedValue(pslILEmit); + // Argument 2 // Create ReadOnlySpan from the stack-allocated buffer pslILEmit->EmitLDLOC(dwBuffer); pslILEmit->EmitLDC(LOCAL_BUFFER_LENGTH); @@ -1979,20 +1984,17 @@ void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit) TypeHandle(CoreLibBinder::GetClass(CLASS__SPAN)).Instantiate(Instantiation(&thByte, 1)).AsMethodTable(), FALSE, Instantiation(), FALSE); pslILEmit->EmitNEWOBJ(pslILEmit->GetToken(pSpanCtor), 2); + pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__FROM_MANAGED, 2, 0); - pslILEmit->EmitNEWOBJ(METHOD__UTF8STRINGMARSHALLER__CTOR_SPAN, 2); - pslILEmit->EmitSTLOC(m_dwInstance); - + pslILEmit->EmitLDLOCA(m_dwInstance); + pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__TO_UNMANAGED, 1, 1); } else { EmitLoadManagedValue(pslILEmit); - pslILEmit->EmitNEWOBJ(METHOD__UTF8STRINGMARSHALLER__CTOR, 1); - pslILEmit->EmitSTLOC(m_dwInstance); + pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__CONVERT_TO_UNMANAGED, 1, 1); } - pslILEmit->EmitLDLOCA(m_dwInstance); - pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__TO_NATIVE_VALUE, 1, 1); EmitStoreNativeValue(pslILEmit); } @@ -2000,15 +2002,8 @@ void ILCUTF8Marshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit) { STANDARD_VM_CONTRACT; - if (m_dwInstance == LOCAL_NUM_UNUSED) - m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER))); - - pslILEmit->EmitLDLOCA(m_dwInstance); EmitLoadNativeValue(pslILEmit); - pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__FROM_NATIVE_VALUE, 2, 0); - - pslILEmit->EmitLDLOCA(m_dwInstance); - pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__TO_MANAGED, 1, 1); + pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__CONVERT_TO_MANAGED, 1, 1); EmitStoreManagedValue(pslILEmit); } @@ -2022,14 +2017,12 @@ void ILCUTF8Marshaler::EmitClearNative(ILCodeStream* pslILEmit) _ASSERTE(m_dwInstance != LOCAL_NUM_UNUSED); pslILEmit->EmitLDLOCA(m_dwInstance); - pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__FREE_NATIVE, 1, 0); + pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__FREE, 0, 0); } else { - // The marshaller instance is not guaranteed to be initialized with the latest native value. - // Free the native value directly. EmitLoadNativeValue(pslILEmit); - pslILEmit->EmitCALL(METHOD__MARSHAL__FREE_CO_TASK_MEM, 1, 0); + pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__FREE, 1, 0); } } diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 5b5bfc2d42746d..2457759f104f9e 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -491,7 +491,6 @@ DEFINE_METASIG_T(SM(Str_CultureInfo_RetStr, s C(CULTURE_INFO), s)) DEFINE_METASIG_T(SM(Str_CultureInfo_RefBool_RetStr, s C(CULTURE_INFO) r(F), s)) DEFINE_METASIG(SM(PtrPtrChar_PtrPtrChar_Int_RetVoid, P(P(u)) P(P(u)) i, v)) DEFINE_METASIG(SM(PtrChar_Int_PtrPtrChar_RetArrStr, P(u) i P(P(u)), a(s))) -DEFINE_METASIG(IM(Str_RetVoid, s, v)) DEFINE_METASIG(SM(RefBool_RefBool_RetVoid, r(F) r(F), v)) DEFINE_METASIG_T(IM(Str_Exception_RetVoid, s C(EXCEPTION), v)) DEFINE_METASIG(IM(Str_Obj_RetVoid, s j, v)) @@ -615,6 +614,10 @@ DEFINE_METASIG_T(IM(Str_SpanOfByte_RetVoid, s GI(g(SPAN), 1, b), v)) DEFINE_METASIG(IM(RetPtrByte, , P(b))) DEFINE_METASIG(IM(VoidPtr_Int_RetVoid, P(v) i, v)) +DEFINE_METASIG(SM(PtrByte_RetStr, P(b), s)) +DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b))) +DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v)) + // Undefine macros in case we include the file again in the compilation unit #undef DEFINE_METASIG diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml index f04aeb1fa84586..13daabf6b16528 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml @@ -74,5 +74,14 @@ + + + + + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index 5baea94b9372d4..9fc202f135e924 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -9,90 +9,109 @@ namespace System.Runtime.InteropServices.Marshalling /// Marshaller for ANSI strings /// [CLSCompliant(false)] - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, - Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] - public unsafe ref struct AnsiStringMarshaller + [CustomMarshaller(typeof(string), MarshalMode.Default, typeof(AnsiStringMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] + public static unsafe class AnsiStringMarshaller { - private byte* _nativeValue; - private bool _allocated; + /// + /// Convert a string to an unmanaged version. + /// + /// A managed string + /// An unmanaged string + public static byte* ConvertToUnmanaged(string? managed) + => (byte*)Marshal.StringToCoTaskMemAnsi(managed); + + /// + /// Convert an unmanaged string to a managed version. + /// + /// An unmanaged string + /// A managed string + public static string? ConvertToManaged(byte* unmanaged) + => Marshal.PtrToStringAnsi((nint)unmanaged); /// - /// Initializes a new instance of the . + /// Free the memory for the unmanaged string. /// - /// The string to marshal. - public AnsiStringMarshaller(string? str) - : this(str, default) - { } + /// Memory allocated for the unmanaged string. + public static void Free(byte* unmanaged) + => Marshal.FreeCoTaskMem((nint)unmanaged); /// - /// Initializes a new instance of the . + /// Custom marshaller to marshal a managed string as a ANSI unmanaged string. /// - /// The string to marshal. - /// Buffer that may be used for marshalling. - /// - /// The must not be movable - that is, it should not be - /// on the managed heap or it should be pinned. - /// - /// - public AnsiStringMarshaller(string? str, Span buffer) + public ref struct ManagedToUnmanagedIn { - _allocated = false; + /// + /// Requested buffer size for optimized marshalling. + /// + public static int BufferSize { get; } = 0x100; - if (str is null) - { - _nativeValue = null; - return; - } + private byte* _unmanagedValue; + private bool _allocated; - // >= for null terminator - // Use the cast to long to avoid the checked operation - if ((long)Marshal.SystemMaxDBCSCharSize * str.Length >= buffer.Length) + /// + /// Initialize the marshaller with a managed string and requested buffer. + /// + /// The managed string + /// A request buffer of at least size, . + public void FromManaged(string? managed, Span buffer) { - // Calculate accurate byte count when the provided stack-allocated buffer is not sufficient - int exactByteCount = Marshal.GetAnsiStringByteCount(str); // Includes null terminator - if (exactByteCount > buffer.Length) + _allocated = false; + + if (managed is null) { - buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); - _allocated = true; + _unmanagedValue = null; + return; } - } - _nativeValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + // >= for null terminator + // Use the cast to long to avoid the checked operation + if ((long)Marshal.SystemMaxDBCSCharSize * managed.Length >= buffer.Length) + { + // Calculate accurate byte count when the provided stack-allocated buffer is not sufficient + int exactByteCount = Marshal.GetAnsiStringByteCount(managed); // Includes null terminator + if (exactByteCount > buffer.Length) + { + buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + _allocated = true; + } + } - Marshal.GetAnsiStringBytes(str, buffer); // Includes null terminator - } + _unmanagedValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); - /// - /// Returns the native value representing the string. - /// - /// - public byte* ToNativeValue() => _nativeValue; + Marshal.GetAnsiStringBytes(managed, buffer); // Includes null terminator + } - /// - /// Sets the native value representing the string. - /// - /// The native value. - /// - public void FromNativeValue(byte* value) - { - _nativeValue = value; - _allocated = true; - } + /// + /// Convert the current manage string to an unmanaged string. + /// + /// The unmanaged string + public byte* ToUnmanaged() => _unmanagedValue; - /// - /// Returns the managed string. - /// - /// - public string? ToManaged() => Marshal.PtrToStringAnsi((IntPtr)_nativeValue); + /// + /// Initialize the marshaller with an unmanaged string. + /// + /// An unmanaged string + public void FromUnmanaged(byte* unmanaged) + { + _unmanagedValue = unmanaged; + _allocated = true; + } - /// - /// Frees native resources. - /// - /// - public void FreeNative() - { - if (_allocated) - Marshal.FreeCoTaskMem((IntPtr)_nativeValue); + /// + /// Convert the current unmanage string to an managed string. + /// + /// A managed string + public string? ToManaged() => ConvertToManaged(_unmanagedValue); + + /// + /// Free any allocated unmanaged string. + /// + public void Free() + { + if (_allocated) + AnsiStringMarshaller.Free(_unmanagedValue); + } } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs index 4f246a26bc3265..89b92496758cce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs @@ -11,107 +11,125 @@ namespace System.Runtime.InteropServices.Marshalling /// Marshaller for BSTR strings /// [CLSCompliant(false)] - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, - Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] - public unsafe ref struct BStrStringMarshaller + [CustomMarshaller(typeof(string), MarshalMode.Default, typeof(BstrStringMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] + public static unsafe class BstrStringMarshaller { - private void* _ptrToFirstChar; - private bool _allocated; - /// - /// Initializes a new instance of the . + /// Convert a string to an unmanaged version. /// - /// The string to marshal. - public BStrStringMarshaller(string? str) - : this(str, default) - { } + /// A managed string + /// An unmanaged string + public static ushort* ConvertToUnmanaged(string? managed) + => (ushort*)Marshal.StringToBSTR(managed); /// - /// Initializes a new instance of the . + /// Convert an unmanaged string to a managed version. /// - /// The string to marshal. - /// Buffer that may be used for marshalling. - /// - /// The must not be movable - that is, it should not be - /// on the managed heap or it should be pinned. - /// - /// - public BStrStringMarshaller(string? str, Span buffer) + /// An unmanaged string + /// A managed string + public static string? ConvertToManaged(ushort* unmanaged) { - _allocated = false; - - if (str is null) - { - _ptrToFirstChar = null; - return; - } - - ushort* ptrToFirstChar; - int lengthInBytes = checked(sizeof(char) * str.Length); - - // A caller provided buffer must be at least (lengthInBytes + 6) bytes - // in order to be constructed manually. The 6 extra bytes are 4 for byte length and 2 for wide null. - int manualBstrNeeds = checked(lengthInBytes + 6); - if (manualBstrNeeds > buffer.Length) - { - // Use precise byte count when the provided stack-allocated buffer is not sufficient - ptrToFirstChar = (ushort*)Marshal.AllocBSTRByteLen((uint)lengthInBytes); - _allocated = true; - } - else - { - // Set length and update buffer target - byte* pBuffer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); - *((uint*)pBuffer) = (uint)lengthInBytes; - ptrToFirstChar = (ushort*)(pBuffer + sizeof(uint)); - } - - // Confirm the size is properly set for the allocated BSTR. - Debug.Assert(lengthInBytes == Marshal.SysStringByteLen((IntPtr)ptrToFirstChar)); + if (unmanaged is null) + return null; - // Copy characters from the managed string - str.CopyTo(new Span(ptrToFirstChar, str.Length)); - ptrToFirstChar[str.Length] = '\0'; // null-terminate - _ptrToFirstChar = ptrToFirstChar; + return Marshal.PtrToStringBSTR((nint)unmanaged); } /// - /// Returns the native value representing the string. + /// Free the memory for the unmanaged string. /// - /// - public void* ToNativeValue() => _ptrToFirstChar; + /// Memory allocated for the unmanaged string. + public static void Free(ushort* unmanaged) + => Marshal.FreeBSTR((nint)unmanaged); /// - /// Sets the native value representing the string. + /// Custom marshaller to marshal a managed string as a ANSI unmanaged string. /// - /// The native value. - /// - public void FromNativeValue(void* value) + public ref struct ManagedToUnmanagedIn { - _ptrToFirstChar = value; - _allocated = true; - } + /// + /// Requested buffer size for optimized marshalling. + /// + public static int BufferSize { get; } = 0x100; - /// - /// Returns the managed string. - /// - /// - public string? ToManaged() - { - if (_ptrToFirstChar is null) - return null; + private ushort* _ptrToFirstChar; + private bool _allocated; - return Marshal.PtrToStringBSTR((IntPtr)_ptrToFirstChar); - } + /// + /// Initialize the marshaller with a managed string and requested buffer. + /// + /// The managed string + /// A request buffer of at least size, . + public void FromManaged(string? managed, Span buffer) + { + _allocated = false; - /// - /// Frees native resources. - /// - /// - public void FreeNative() - { - if (_allocated) - Marshal.FreeBSTR((IntPtr)_ptrToFirstChar); + if (managed is null) + { + _ptrToFirstChar = null; + return; + } + + ushort* ptrToFirstChar; + int lengthInBytes = checked(sizeof(char) * managed.Length); + + // A caller provided buffer must be at least (lengthInBytes + 6) bytes + // in order to be constructed manually. The 6 extra bytes are 4 for byte length and 2 for wide null. + int manualBstrNeeds = checked(lengthInBytes + 6); + if (manualBstrNeeds > buffer.Length) + { + // Use precise byte count when the provided stack-allocated buffer is not sufficient + ptrToFirstChar = (ushort*)Marshal.AllocBSTRByteLen((uint)lengthInBytes); + _allocated = true; + } + else + { + // Set length and update buffer target + byte* pBuffer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + *((uint*)pBuffer) = (uint)lengthInBytes; + ptrToFirstChar = (ushort*)(pBuffer + sizeof(uint)); + } + + // Confirm the size is properly set for the allocated BSTR. + Debug.Assert(lengthInBytes == Marshal.SysStringByteLen((IntPtr)ptrToFirstChar)); + + // Copy characters from the managed string + managed.CopyTo(new Span(ptrToFirstChar, managed.Length)); + ptrToFirstChar[managed.Length] = '\0'; // null-terminate + _ptrToFirstChar = ptrToFirstChar; + } + + /// + /// Convert the current manage string to an unmanaged string. + /// + /// The unmanaged string + public ushort* ToUnmanaged() => _ptrToFirstChar; + + /// + /// Initialize the marshaller with an unmanaged string. + /// + /// An unmanaged string + public void FromUnmanaged(ushort* unmanaged) + { + _ptrToFirstChar = unmanaged; + _allocated = true; + } + + /// + /// Convert the current unmanage string to an managed string. + /// + /// A managed string + public string? ToManaged() => ConvertToManaged(_ptrToFirstChar); + + /// + /// Free any allocated unmanaged string. + /// + public void Free() + { + if (_allocated) + BstrStringMarshaller.Free(_ptrToFirstChar); + } } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs index 22d317a9cb17d2..bf42a121b0bf9c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs @@ -9,51 +9,38 @@ namespace System.Runtime.InteropServices.Marshalling /// Marshaller for UTF-16 strings /// [CLSCompliant(false)] - [CustomTypeMarshaller(typeof(string), - Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] - public unsafe ref struct Utf16StringMarshaller + [CustomMarshaller(typeof(string), MarshalMode.Default, typeof(Utf16StringMarshaller))] + public static unsafe class Utf16StringMarshaller { - private void* _nativeValue; - - /// - /// Initializes a new instance of the . - /// - /// - /// The caller allocated constructor option is not provided because - /// pinning should be preferred for UTF-16 scenarios. - /// - /// The string to marshal. - public Utf16StringMarshaller(string? str) - { - _nativeValue = (void*)Marshal.StringToCoTaskMemUni(str); - } - /// - /// Returns the native value representing the string. + /// Convert a string to an unmanaged version. /// - /// - public void* ToNativeValue() => _nativeValue; + /// A managed string + /// An unmanaged string + public static ushort* ConvertToUnmanaged(string? managed) + => (ushort*)Marshal.StringToCoTaskMemUni(managed); /// - /// Sets the native value representing the string. + /// Convert an unmanaged string to a managed version. /// - /// The native value. - /// - public void FromNativeValue(void* value) => _nativeValue = value; + /// An unmanaged string + /// A managed string + public static string? ConvertToManaged(ushort* unmanaged) + => Marshal.PtrToStringUni((nint)unmanaged); /// - /// Returns the managed string. + /// Free the memory for the unmanaged string. /// - /// - public string? ToManaged() => Marshal.PtrToStringUni((IntPtr)_nativeValue); + /// Memory allocated for the unmanaged string. + public static void Free(ushort* unmanaged) + => Marshal.FreeCoTaskMem((nint)unmanaged); /// - /// Frees native resources. + /// Get a pinnable reference for the string. /// - /// - public void FreeNative() - { - Marshal.FreeCoTaskMem((IntPtr)_nativeValue); - } + /// The string. + /// A pinnable reference. + public static ref readonly char GetPinnableReference(string? str) + => ref str is null ? ref *(char*)0 : ref str.GetPinnableReference(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index 955e6b7b62f70f..f4273560ec5063 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -10,93 +10,112 @@ namespace System.Runtime.InteropServices.Marshalling /// Marshaller for UTF-8 strings /// [CLSCompliant(false)] - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, - Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] - public unsafe ref struct Utf8StringMarshaller + [CustomMarshaller(typeof(string), MarshalMode.Default, typeof(Utf8StringMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] + public static unsafe class Utf8StringMarshaller { - private byte* _nativeValue; - private bool _allocated; + /// + /// Convert a string to an unmanaged version. + /// + /// A managed string + /// An unmanaged string + public static byte* ConvertToUnmanaged(string? managed) + => (byte*)Marshal.StringToCoTaskMemUTF8(managed); + + /// + /// Convert an unmanaged string to a managed version. + /// + /// An unmanaged string + /// A managed string + public static string? ConvertToManaged(byte* unmanaged) + => Marshal.PtrToStringUTF8((nint)unmanaged); /// - /// Initializes a new instance of the . + /// Free the memory for the unmanaged string. /// - /// The string to marshal. - public Utf8StringMarshaller(string? str) - : this(str, default) - { } + /// Memory allocated for the unmanaged string. + public static void Free(byte* unmanaged) + => Marshal.FreeCoTaskMem((nint)unmanaged); /// - /// Initializes a new instance of the . + /// Custom marshaller to marshal a managed string as a UTF-8 unmanaged string. /// - /// The string to marshal. - /// Buffer that may be used for marshalling. - /// - /// The must not be movable - that is, it should not be - /// on the managed heap or it should be pinned. - /// - /// - public Utf8StringMarshaller(string? str, Span buffer) + public ref struct ManagedToUnmanagedIn { - _allocated = false; + /// + /// Requested buffer size for optimized marshalling. + /// + public static int BufferSize { get; } = 0x100; + + private byte* _unmanagedValue; + private bool _allocated; - if (str is null) + /// + /// Initialize the marshaller with a managed string and requested buffer. + /// + /// The managed string + /// A request buffer of at least size, . + public void FromManaged(string? managed, Span buffer) { - _nativeValue = null; - return; - } + _allocated = false; - const int MaxUtf8BytesPerChar = 3; + if (managed is null) + { + _unmanagedValue = null; + return; + } - // >= for null terminator - // Use the cast to long to avoid the checked operation - if ((long)MaxUtf8BytesPerChar * str.Length >= buffer.Length) - { - // Calculate accurate byte count when the provided stack-allocated buffer is not sufficient - int exactByteCount = checked(Encoding.UTF8.GetByteCount(str) + 1); // + 1 for null terminator - if (exactByteCount > buffer.Length) + const int MaxUtf8BytesPerChar = 3; + + // >= for null terminator + // Use the cast to long to avoid the checked operation + if ((long)MaxUtf8BytesPerChar * managed.Length >= buffer.Length) { - buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); - _allocated = true; + // Calculate accurate byte count when the provided stack-allocated buffer is not sufficient + int exactByteCount = checked(Encoding.UTF8.GetByteCount(managed) + 1); // + 1 for null terminator + if (exactByteCount > buffer.Length) + { + buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + _allocated = true; + } } - } - _nativeValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + _unmanagedValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); - int byteCount = Encoding.UTF8.GetBytes(str, buffer); - buffer[byteCount] = 0; // null-terminate - } + int byteCount = Encoding.UTF8.GetBytes(managed, buffer); + buffer[byteCount] = 0; // null-terminate + } - /// - /// Returns the native value representing the string. - /// - /// - public byte* ToNativeValue() => _nativeValue; + /// + /// Convert the current manage string to an unmanaged string. + /// + /// The unmanaged string + public byte* ToUnmanaged() => _unmanagedValue; - /// - /// Sets the native value representing the string. - /// - /// The native value. - /// - public void FromNativeValue(byte* value) - { - _nativeValue = value; - _allocated = true; - } + /// + /// Initialize the marshaller with an unmanaged string. + /// + /// An unmanaged string + public void FromUnmanaged(byte* unmanaged) + { + _unmanagedValue = unmanaged; + _allocated = true; + } - /// - /// Returns the managed string. - /// - /// - public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)_nativeValue); + /// + /// Convert the current unmanage string to an managed string. + /// + /// A managed string + public string? ToManaged() => ConvertToManaged(_unmanagedValue); - /// - /// Frees native resources. - /// - /// - public void FreeNative() - { - if (_allocated) - Marshal.FreeCoTaskMem((IntPtr)_nativeValue); + /// + /// Free any allocated unmanaged string. + /// + public void Free() + { + if (_allocated) + Utf8StringMarshaller.Free(_unmanagedValue); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index cf2596270e21a3..b7699f4855990f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -1257,12 +1257,13 @@ public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax return node.WithRight( CastExpression(MarshallerHelpers.SystemIntPtrType, node.Right)); } + if (node.Right.ToString() == _nativeIdentifier) { return node.WithRight(CastExpression(_nativeType, node.Right)); } - return node; + return base.VisitAssignmentExpression(node); } public override SyntaxNode? VisitArgument(ArgumentSyntax node) @@ -1272,7 +1273,8 @@ public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax return node.WithExpression( CastExpression(_nativeType, node.Expression)); } - return node; + + return base.VisitArgument(node); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs index 827662fb742ba9..c72f605653fd2e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs @@ -926,12 +926,13 @@ public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax return node.WithRight( CastExpression(MarshallerHelpers.SystemIntPtrType, node.Right)); } + if (node.Right.ToString() == _nativeIdentifier) { return node.WithRight(CastExpression(_nativeType, node.Right)); } - return node; + return base.VisitAssignmentExpression(node); } public override SyntaxNode? VisitArgument(ArgumentSyntax node) @@ -941,7 +942,8 @@ public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax return node.WithExpression( CastExpression(_nativeType, node.Expression)); } - return node; + + return base.VisitArgument(node); } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index bbc7222170ab89..a50e7497deeff1 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -2121,18 +2121,27 @@ public enum MessageSendFunction namespace System.Runtime.InteropServices.Marshalling { [System.CLSCompliant(false)] - [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(string), BufferSize = 0x100, - Features = System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.UnmanagedResources - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.CallerAllocatedBuffer - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.TwoStageMarshalling )] - public unsafe ref struct AnsiStringMarshaller - { - public AnsiStringMarshaller(string? str) { } - public AnsiStringMarshaller(string? str, System.Span buffer) { } - public byte* ToNativeValue() { throw null; } - public void FromNativeValue(byte* value) { } - public string? ToManaged() { throw null; } - public void FreeNative() { } + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.Default, + typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedIn, + typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller.ManagedToUnmanagedIn))] + public static unsafe class AnsiStringMarshaller + { + public static byte* ConvertToUnmanaged(string? managed) { throw null; } + public static string? ConvertToManaged(byte* unmanaged) { throw null; } + public static void Free(byte* unmanaged) { throw null; } + + public ref struct ManagedToUnmanagedIn + { + public static int BufferSize { get { throw null; } } + public void FromManaged(string? managed, System.Span buffer) { throw null; } + public byte* ToUnmanaged() { throw null; } + public void FromUnmanaged(byte* unmanaged) { throw null; } + public string? ToManaged() { throw null; } + public void Free() { throw null; } + } } [System.CLSCompliantAttribute(false)] [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute.GenericPlaceholder[]), @@ -2156,20 +2165,28 @@ public void FromNativeValue(byte* value) { } public void FreeNative() { } } [System.CLSCompliant(false)] - [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(string), BufferSize = 0x100, - Features = System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.UnmanagedResources - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.CallerAllocatedBuffer - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.TwoStageMarshalling )] - public unsafe ref struct BStrStringMarshaller - { - public BStrStringMarshaller(string? str) { } - public BStrStringMarshaller(string? str, System.Span buffer) { } - public void* ToNativeValue() { throw null; } - public void FromNativeValue(void* value) { } - public string? ToManaged() { throw null; } - public void FreeNative() { } - } + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.Default, + typeof(System.Runtime.InteropServices.Marshalling.BstrStringMarshaller))] + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedIn, + typeof(System.Runtime.InteropServices.Marshalling.BstrStringMarshaller.ManagedToUnmanagedIn))] + public static unsafe class BstrStringMarshaller + { + public static ushort* ConvertToUnmanaged(string? managed) { throw null; } + public static string? ConvertToManaged(ushort* unmanaged) { throw null; } + public static void Free(ushort* unmanaged) { throw null; } + public ref struct ManagedToUnmanagedIn + { + public static int BufferSize { get { throw null; } } + public void FromManaged(string? managed, System.Span buffer) { throw null; } + public ushort* ToUnmanaged() { throw null; } + public void FromUnmanaged(ushort* unmanaged) { throw null; } + public string? ToManaged() { throw null; } + public void Free() { throw null; } + } + } [System.AttributeUsageAttribute(System.AttributeTargets.Struct | System.AttributeTargets.Class, AllowMultiple = true)] public sealed partial class CustomMarshallerAttribute : System.Attribute { @@ -2269,30 +2286,38 @@ public void FromNativeValue(byte* value) { } public void FreeNative() { } } [System.CLSCompliant(false)] - [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(string), BufferSize = 0x100, - Features = System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.UnmanagedResources - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.CallerAllocatedBuffer - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.TwoStageMarshalling )] - public unsafe ref struct Utf8StringMarshaller - { - public Utf8StringMarshaller(string? str) { } - public Utf8StringMarshaller(string? str, System.Span buffer) { } - public byte* ToNativeValue() { throw null; } - public void FromNativeValue(byte* value) { } - public string? ToManaged() { throw null; } - public void FreeNative() { } + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.Default, + typeof(System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller))] + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedIn, + typeof(System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn))] + public static unsafe class Utf8StringMarshaller + { + public static byte* ConvertToUnmanaged(string? managed) { throw null; } + public static string? ConvertToManaged(byte* unmanaged) { throw null; } + public static void Free(byte* unmanaged) { throw null; } + + public ref struct ManagedToUnmanagedIn + { + public static int BufferSize { get { throw null; } } + public void FromManaged(string? managed, System.Span buffer) { throw null; } + public byte* ToUnmanaged() { throw null; } + public void FromUnmanaged(byte* unmanaged) { throw null; } + public string? ToManaged() { throw null; } + public void Free() { throw null; } + } } [System.CLSCompliant(false)] - [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(string), - Features = System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.UnmanagedResources - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.TwoStageMarshalling )] - public unsafe ref struct Utf16StringMarshaller - { - public Utf16StringMarshaller(string? str) { } - public void* ToNativeValue() { throw null; } - public void FromNativeValue(void* value) { } - public string? ToManaged() { throw null; } - public void FreeNative() { } + [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), + System.Runtime.InteropServices.Marshalling.MarshalMode.Default, + typeof(System.Runtime.InteropServices.Marshalling.Utf16StringMarshaller))] + public static unsafe class Utf16StringMarshaller + { + public static ushort* ConvertToUnmanaged(string? managed) { throw null; } + public static string? ConvertToManaged(ushort* unmanaged) { throw null; } + public static void Free(ushort* unmanaged) { throw null; } + public static ref readonly char GetPinnableReference(string? str) { throw null; } } } namespace System.Security From a34d292780294389b1b559ed9084d84b974181e1 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 8 Jul 2022 15:29:13 -0700 Subject: [PATCH 2/7] Fix incorrect BStr casing. --- .../InteropServices/Marshalling/BStrStringMarshaller.cs | 6 +++--- .../ref/System.Runtime.InteropServices.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs index 89b92496758cce..e39a7aa5ddacec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs @@ -11,9 +11,9 @@ namespace System.Runtime.InteropServices.Marshalling /// Marshaller for BSTR strings /// [CLSCompliant(false)] - [CustomMarshaller(typeof(string), MarshalMode.Default, typeof(BstrStringMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.Default, typeof(BStrStringMarshaller))] [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] - public static unsafe class BstrStringMarshaller + public static unsafe class BStrStringMarshaller { /// /// Convert a string to an unmanaged version. @@ -128,7 +128,7 @@ public void FromUnmanaged(ushort* unmanaged) public void Free() { if (_allocated) - BstrStringMarshaller.Free(_ptrToFirstChar); + BStrStringMarshaller.Free(_ptrToFirstChar); } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 3c5a0035e63378..4d30ad0d071636 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -2170,11 +2170,11 @@ public void FreeNative() { } [System.CLSCompliant(false)] [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), System.Runtime.InteropServices.Marshalling.MarshalMode.Default, - typeof(System.Runtime.InteropServices.Marshalling.BstrStringMarshaller))] + typeof(System.Runtime.InteropServices.Marshalling.BStrStringMarshaller))] [System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(string), System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedIn, - typeof(System.Runtime.InteropServices.Marshalling.BstrStringMarshaller.ManagedToUnmanagedIn))] - public static unsafe class BstrStringMarshaller + typeof(System.Runtime.InteropServices.Marshalling.BStrStringMarshaller.ManagedToUnmanagedIn))] + public static unsafe class BStrStringMarshaller { public static ushort* ConvertToUnmanaged(string? managed) { throw null; } public static string? ConvertToManaged(ushort* unmanaged) { throw null; } From 3b560fbdac7ef0caf0d080af4bf3c33872867334 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 8 Jul 2022 17:28:09 -0700 Subject: [PATCH 3/7] Use NativeMemory.Alloc/Free where possible. Avoid initialization of marshaller when not used. Remove unneccessary unmanaged->managed APIs for In marshallers. --- .../TypeSystem/Interop/IL/Marshaller.cs | 15 ++++++------- src/coreclr/vm/corelib.h | 2 -- src/coreclr/vm/ilmarshalers.cpp | 11 ++++------ src/coreclr/vm/metasig.h | 1 - .../src/ILLink/ILLink.Descriptors.Shared.xml | 2 -- .../Marshalling/AnsiStringMarshaller.cs | 22 +++---------------- .../Marshalling/BStrStringMarshaller.cs | 18 +-------------- .../Marshalling/Utf8StringMarshaller.cs | 22 +++---------------- .../ref/System.Runtime.InteropServices.cs | 6 ----- 9 files changed, 18 insertions(+), 81 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs index d4bec01421e75d..518b9c8cf1f296 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs @@ -1665,19 +1665,20 @@ internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emi Debug.Assert(_marshallerInstance is null); codeStream.Emit(ILOpcode.call, emitter.NewToken( - InteropTypes.GetMarshal(Context).GetKnownMethod("FreeCoTaskMem", null))); + Marshaller.GetKnownMethod("Free", null))); } protected override void TransformManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; - TypeDesc marshallerIn = MarshallerIn; - - if (_marshallerInstance == null) - _marshallerInstance = emitter.NewLocal(marshallerIn); if (In && !Out && !IsManagedByRef) { + TypeDesc marshallerIn = MarshallerIn; + + if (_marshallerInstance == null) + _marshallerInstance = emitter.NewLocal(marshallerIn); + var vBuffer = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr)); codeStream.EmitLdc(LocalBufferLength); codeStream.Emit(ILOpcode.localloc); @@ -1724,10 +1725,8 @@ protected override void EmitCleanupManaged(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; - if (In && !Out && !IsManagedByRef) + if (_marshallerInstance != null) { - Debug.Assert(_marshallerInstance != null); - codeStream.EmitLdLoca(_marshallerInstance.Value); codeStream.Emit(ILOpcode.call, emitter.NewToken( MarshallerIn.GetKnownMethod("Free", null))); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 5d85eb47bd28dc..4b170dba019198 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1201,8 +1201,6 @@ DEFINE_METHOD(UTF8STRINGMARSHALLER, FREE, Free, SM_PtrByte_RetVoid) DEFINE_CLASS(UTF8STRINGMARSHALLER_IN, Marshalling, Utf8StringMarshaller+ManagedToUnmanagedIn) DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FROM_MANAGED, FromManaged, IM_Str_SpanOfByte_RetVoid) DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, TO_UNMANAGED, ToUnmanaged, IM_RetPtrByte) -DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FROM_UNMANAGED, FromUnmanaged, IM_PtrByte_RetVoid) -DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, TO_MANAGED, ToManaged, IM_RetStr) DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FREE, Free, IM_RetVoid) #endif // FOR_ILLINK diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index ba39c76c248dbc..66cf2137b3c9c5 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -1958,12 +1958,12 @@ void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit) { STANDARD_VM_CONTRACT; - if (m_dwInstance == LOCAL_NUM_UNUSED) - m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER_IN))); - bool bPassByValueInOnly = IsIn(m_dwMarshalFlags) && !IsOut(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags); if (bPassByValueInOnly) { + if (m_dwInstance == LOCAL_NUM_UNUSED) + m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER_IN))); + DWORD dwBuffer = pslILEmit->NewLocal(ELEMENT_TYPE_I); pslILEmit->EmitLDC(LOCAL_BUFFER_LENGTH); pslILEmit->EmitLOCALLOC(); @@ -2011,11 +2011,8 @@ void ILCUTF8Marshaler::EmitClearNative(ILCodeStream* pslILEmit) { STANDARD_VM_CONTRACT; - bool bPassByValueInOnly = IsIn(m_dwMarshalFlags) && !IsOut(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags); - if (bPassByValueInOnly) + if (m_dwInstance != LOCAL_NUM_UNUSED) { - _ASSERTE(m_dwInstance != LOCAL_NUM_UNUSED); - pslILEmit->EmitLDLOCA(m_dwInstance); pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__FREE, 0, 0); } diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 2457759f104f9e..f567d34be2f7da 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -426,7 +426,6 @@ DEFINE_METASIG_T(SM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s)) DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I)) DEFINE_METASIG(IM(ArrByte_Int_Int_RetVoid, a(b) i i, v)) -DEFINE_METASIG(IM(PtrByte_RetVoid, P(b), v)) DEFINE_METASIG(IM(Char_Char_RetStr, u u, s)) DEFINE_METASIG(IM(Char_Int_RetVoid, u i, v)) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml index 13daabf6b16528..0f577d8253d599 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml @@ -79,8 +79,6 @@ - - diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index 9fc202f135e924..86066d8a4befb3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -44,7 +44,7 @@ public ref struct ManagedToUnmanagedIn /// /// Requested buffer size for optimized marshalling. /// - public static int BufferSize { get; } = 0x100; + public static int BufferSize => 0x100; private byte* _unmanagedValue; private bool _allocated; @@ -72,7 +72,7 @@ public void FromManaged(string? managed, Span buffer) int exactByteCount = Marshal.GetAnsiStringByteCount(managed); // Includes null terminator if (exactByteCount > buffer.Length) { - buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + buffer = new Span((byte*)NativeMemory.Alloc((nuint)exactByteCount), exactByteCount); _allocated = true; } } @@ -88,29 +88,13 @@ public void FromManaged(string? managed, Span buffer) /// The unmanaged string public byte* ToUnmanaged() => _unmanagedValue; - /// - /// Initialize the marshaller with an unmanaged string. - /// - /// An unmanaged string - public void FromUnmanaged(byte* unmanaged) - { - _unmanagedValue = unmanaged; - _allocated = true; - } - - /// - /// Convert the current unmanage string to an managed string. - /// - /// A managed string - public string? ToManaged() => ConvertToManaged(_unmanagedValue); - /// /// Free any allocated unmanaged string. /// public void Free() { if (_allocated) - AnsiStringMarshaller.Free(_unmanagedValue); + NativeMemory.Free(_unmanagedValue); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs index e39a7aa5ddacec..052f09a646de71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs @@ -51,7 +51,7 @@ public ref struct ManagedToUnmanagedIn /// /// Requested buffer size for optimized marshalling. /// - public static int BufferSize { get; } = 0x100; + public static int BufferSize => 0x100; private ushort* _ptrToFirstChar; private bool _allocated; @@ -106,22 +106,6 @@ public void FromManaged(string? managed, Span buffer) /// The unmanaged string public ushort* ToUnmanaged() => _ptrToFirstChar; - /// - /// Initialize the marshaller with an unmanaged string. - /// - /// An unmanaged string - public void FromUnmanaged(ushort* unmanaged) - { - _ptrToFirstChar = unmanaged; - _allocated = true; - } - - /// - /// Convert the current unmanage string to an managed string. - /// - /// A managed string - public string? ToManaged() => ConvertToManaged(_ptrToFirstChar); - /// /// Free any allocated unmanaged string. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index f4273560ec5063..b4dc79ce09187b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -45,7 +45,7 @@ public ref struct ManagedToUnmanagedIn /// /// Requested buffer size for optimized marshalling. /// - public static int BufferSize { get; } = 0x100; + public static int BufferSize => 0x100; private byte* _unmanagedValue; private bool _allocated; @@ -75,7 +75,7 @@ public void FromManaged(string? managed, Span buffer) int exactByteCount = checked(Encoding.UTF8.GetByteCount(managed) + 1); // + 1 for null terminator if (exactByteCount > buffer.Length) { - buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + buffer = new Span((byte*)NativeMemory.Alloc((nuint)exactByteCount), exactByteCount); _allocated = true; } } @@ -92,29 +92,13 @@ public void FromManaged(string? managed, Span buffer) /// The unmanaged string public byte* ToUnmanaged() => _unmanagedValue; - /// - /// Initialize the marshaller with an unmanaged string. - /// - /// An unmanaged string - public void FromUnmanaged(byte* unmanaged) - { - _unmanagedValue = unmanaged; - _allocated = true; - } - - /// - /// Convert the current unmanage string to an managed string. - /// - /// A managed string - public string? ToManaged() => ConvertToManaged(_unmanagedValue); - /// /// Free any allocated unmanaged string. /// public void Free() { if (_allocated) - Utf8StringMarshaller.Free(_unmanagedValue); + NativeMemory.Free(_unmanagedValue); } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 4d30ad0d071636..99fdb8fe666231 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -2141,8 +2141,6 @@ public ref struct ManagedToUnmanagedIn public static int BufferSize { get { throw null; } } public void FromManaged(string? managed, System.Span buffer) { throw null; } public byte* ToUnmanaged() { throw null; } - public void FromUnmanaged(byte* unmanaged) { throw null; } - public string? ToManaged() { throw null; } public void Free() { throw null; } } } @@ -2185,8 +2183,6 @@ public ref struct ManagedToUnmanagedIn public static int BufferSize { get { throw null; } } public void FromManaged(string? managed, System.Span buffer) { throw null; } public ushort* ToUnmanaged() { throw null; } - public void FromUnmanaged(ushort* unmanaged) { throw null; } - public string? ToManaged() { throw null; } public void Free() { throw null; } } } @@ -2306,8 +2302,6 @@ public ref struct ManagedToUnmanagedIn public static int BufferSize { get { throw null; } } public void FromManaged(string? managed, System.Span buffer) { throw null; } public byte* ToUnmanaged() { throw null; } - public void FromUnmanaged(byte* unmanaged) { throw null; } - public string? ToManaged() { throw null; } public void Free() { throw null; } } } From 3d73a448833cec6baa916c788dff2b95899b7a14 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 8 Jul 2022 17:29:10 -0700 Subject: [PATCH 4/7] Add a ClassLoader::LoadTypeByNameThrowing overload that can be used to load nested classes. --- src/coreclr/vm/binder.cpp | 29 ++++++++++++++++- src/coreclr/vm/clsload.cpp | 66 +++++++++++--------------------------- src/coreclr/vm/clsload.hpp | 11 ++++++- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/coreclr/vm/binder.cpp b/src/coreclr/vm/binder.cpp index cba59f246cfc4f..18f0f0b7009015 100644 --- a/src/coreclr/vm/binder.cpp +++ b/src/coreclr/vm/binder.cpp @@ -62,7 +62,34 @@ PTR_MethodTable CoreLibBinder::LookupClassLocal(BinderClassID id) const CoreLibClassDescription *d = m_classDescriptions + (int)id; - pMT = ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), d->nameSpace, d->name).AsMethodTable(); + LPCUTF8 nameSpace = d->nameSpace; + LPCUTF8 name = d->name; + + LPCUTF8 nestedTypeMaybe = strchr(name, '+'); + if (nestedTypeMaybe == NULL) + { + NameHandle nameHandle = NameHandle(nameSpace, name); + pMT = ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), &nameHandle).AsMethodTable(); + } + else + { + // Handle the nested type scenario. + // The same NameHandle must be used to retain the scope to look for the nested type. + NameHandle nameHandle(GetModule(), mdtBaseType); + + SString splitName(SString::Utf8, name, (COUNT_T)(nestedTypeMaybe - name)); + nameHandle.SetName(nameSpace, splitName.GetUTF8()); + + // The side-effect of updating the scope in the NameHandle is the point of the call. + (void)ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), &nameHandle); + + // Now load the nested type. + nameHandle.SetName(NULL, nestedTypeMaybe + 1); + + // We don't support nested types in nested types. + _ASSERTE(strchr(nameHandle.GetName(), '+') == NULL); + pMT = ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), &nameHandle).AsMethodTable(); + } _ASSERTE(pMT->GetModule() == GetModule()); diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 1b2830e28e4699..1ca24e7fbfc09c 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -275,33 +275,22 @@ BOOL ClassLoader::IsTypicalInstantiation(Module *pModule, mdToken token, Instant return TRUE; } -namespace +/*static*/ +TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly, + LPCUTF8 nameSpace, + LPCUTF8 name, + NotFoundAction fNotFound, + ClassLoader::LoadTypesFlag fLoadTypes, + ClassLoadLevel level) { - TypeHandle LoadTypeByNameThrowingWorker( - ClassLoader* classLoader, - NameHandle* nameHandle, - ClassLoader::NotFoundAction fNotFound, - ClassLoader::LoadTypesFlag fLoadTypes, - ClassLoadLevel level) - { - WRAPPER_NO_CONTRACT; // The contract is enforced in caller. - _ASSERTE(classLoader != NULL); - _ASSERTE(nameHandle != NULL); - - if (fLoadTypes == ClassLoader::DontLoadTypes) - nameHandle->SetTokenNotToLoad(tdAllTypes); - if (fNotFound == ClassLoader::ThrowIfNotFound) - return classLoader->LoadTypeHandleThrowIfFailed(nameHandle, level); - else - return classLoader->LoadTypeHandleThrowing(nameHandle, level); - } + WRAPPER_NO_CONTRACT; + NameHandle nameHandle(nameSpace, name); + return LoadTypeByNameThrowing(pAssembly, &nameHandle, fNotFound, fLoadTypes, level); } -// External class loader entry point: load a type by name /*static*/ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly, - LPCUTF8 nameSpace, - LPCUTF8 name, + NameHandle *pNameHandle, NotFoundAction fNotFound, ClassLoader::LoadTypesFlag fLoadTypes, ClassLoadLevel level) @@ -316,6 +305,7 @@ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly, if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); } PRECONDITION(CheckPointer(pAssembly)); + PRECONDITION(pNameHandle != NULL); PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED); POSTCONDITION(CheckPointer(RETVAL, (fNotFound == ThrowIfNotFound && fLoadTypes == LoadTypes )? NULL_NOT_OK : NULL_OK)); @@ -327,32 +317,14 @@ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly, } CONTRACT_END - ClassLoader* classLoader = pAssembly->GetLoader(); - - NameHandle nameHandle; - LPCUTF8 nestedTypeMaybe = strchr(name, '+'); - if (nestedTypeMaybe == NULL) - { - nameHandle = NameHandle(nameSpace, name); - RETURN LoadTypeByNameThrowingWorker(classLoader, &nameHandle, fNotFound, fLoadTypes, level); - } - - // Handle the nested type scenario. - // The same NameHandle must be used to retain the scope to look for the nested type. - nameHandle = NameHandle(pAssembly->GetModule(), mdtBaseType); + if (fLoadTypes == ClassLoader::DontLoadTypes) + pNameHandle->SetTokenNotToLoad(tdAllTypes); - SString splitName(SString::Utf8, name, (COUNT_T)(nestedTypeMaybe - name)); - nameHandle.SetName(nameSpace, splitName.GetUTF8()); - - // The side-effect of updating the scope in the NameHandle is the point of the call. - (void)LoadTypeByNameThrowingWorker(classLoader, &nameHandle, fNotFound, fLoadTypes, level); - - // Now load the nested type. - nameHandle.SetName(NULL, nestedTypeMaybe + 1); - - // We don't support nested types in nested types. - _ASSERTE(strchr(nameHandle.GetName(), '+') == NULL); - RETURN LoadTypeByNameThrowingWorker(classLoader, &nameHandle, fNotFound, fLoadTypes, level); + ClassLoader* classLoader = pAssembly->GetLoader(); + if (fNotFound == ClassLoader::ThrowIfNotFound) + RETURN classLoader->LoadTypeHandleThrowIfFailed(pNameHandle, level); + else + RETURN classLoader->LoadTypeHandleThrowing(pNameHandle, level); } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index e446e239350aff..d3e6ac20576c7b 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -699,7 +699,9 @@ class ClassLoader LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED); - // Load types by name + // External class loader entry point + // Load types by name - doesn't support nested types. + // See overload using NameHandle. static TypeHandle LoadTypeByNameThrowing(Assembly *pAssembly, LPCUTF8 nameSpace, LPCUTF8 name, @@ -707,6 +709,13 @@ class ClassLoader LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED); + // Load types using a NameHandle. + static TypeHandle LoadTypeByNameThrowing(Assembly *pAssembly, + NameHandle *pNameHandle, + NotFoundAction fNotFound = ThrowIfNotFound, + LoadTypesFlag fLoadTypes = LoadTypes, + ClassLoadLevel level = CLASS_LOADED); + // Resolve a TypeRef to a TypeDef // (Just a no-op on TypeDefs) // Return FALSE if operation failed (e.g. type does not exist) From b440b71644be0f85d80ebae6de0b4a7265a054f5 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 8 Jul 2022 20:54:53 -0700 Subject: [PATCH 5/7] Allocate precise amounts for UTF8 and ANSI marshallers. --- .../Marshalling/AnsiStringMarshaller.cs | 15 ++++++++++++--- .../Marshalling/BStrStringMarshaller.cs | 4 ++-- .../Marshalling/Utf16StringMarshaller.cs | 4 ++-- .../Marshalling/Utf8StringMarshaller.cs | 16 +++++++++++++--- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index 86066d8a4befb3..bb4548f98dd35e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -19,7 +19,16 @@ public static unsafe class AnsiStringMarshaller /// A managed string /// An unmanaged string public static byte* ConvertToUnmanaged(string? managed) - => (byte*)Marshal.StringToCoTaskMemAnsi(managed); + { + if (managed is null) + return null; + + int exactByteCount = Marshal.GetAnsiStringByteCount(managed); // Includes null terminator + Span buffer = new ((byte*)NativeMemory.Alloc((nuint)exactByteCount), exactByteCount); + + Marshal.GetAnsiStringBytes(managed, buffer); // Includes null terminator + return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + } /// /// Convert an unmanaged string to a managed version. @@ -27,14 +36,14 @@ public static unsafe class AnsiStringMarshaller /// An unmanaged string /// A managed string public static string? ConvertToManaged(byte* unmanaged) - => Marshal.PtrToStringAnsi((nint)unmanaged); + => Marshal.PtrToStringAnsi((IntPtr)unmanaged); /// /// Free the memory for the unmanaged string. /// /// Memory allocated for the unmanaged string. public static void Free(byte* unmanaged) - => Marshal.FreeCoTaskMem((nint)unmanaged); + => NativeMemory.Free(unmanaged); /// /// Custom marshaller to marshal a managed string as a ANSI unmanaged string. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs index 052f09a646de71..4febd95b275a55 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs @@ -33,7 +33,7 @@ public static unsafe class BStrStringMarshaller if (unmanaged is null) return null; - return Marshal.PtrToStringBSTR((nint)unmanaged); + return Marshal.PtrToStringBSTR((IntPtr)unmanaged); } /// @@ -41,7 +41,7 @@ public static unsafe class BStrStringMarshaller /// /// Memory allocated for the unmanaged string. public static void Free(ushort* unmanaged) - => Marshal.FreeBSTR((nint)unmanaged); + => Marshal.FreeBSTR((IntPtr)unmanaged); /// /// Custom marshaller to marshal a managed string as a ANSI unmanaged string. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs index bf42a121b0bf9c..e8058e7f8df4c7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs @@ -26,14 +26,14 @@ public static unsafe class Utf16StringMarshaller /// An unmanaged string /// A managed string public static string? ConvertToManaged(ushort* unmanaged) - => Marshal.PtrToStringUni((nint)unmanaged); + => Marshal.PtrToStringUni((IntPtr)unmanaged); /// /// Free the memory for the unmanaged string. /// /// Memory allocated for the unmanaged string. public static void Free(ushort* unmanaged) - => Marshal.FreeCoTaskMem((nint)unmanaged); + => Marshal.FreeCoTaskMem((IntPtr)unmanaged); /// /// Get a pinnable reference for the string. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index b4dc79ce09187b..c735795e3855f3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -20,7 +20,17 @@ public static unsafe class Utf8StringMarshaller /// A managed string /// An unmanaged string public static byte* ConvertToUnmanaged(string? managed) - => (byte*)Marshal.StringToCoTaskMemUTF8(managed); + { + if (managed is null) + return null; + + int exactByteCount = checked(Encoding.UTF8.GetByteCount(managed) + 1); // + 1 for null terminator + Span buffer = new ((byte*)NativeMemory.Alloc((nuint)exactByteCount), exactByteCount); + + int byteCount = Encoding.UTF8.GetBytes(managed, buffer); + buffer[byteCount] = 0; // null-terminate + return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + } /// /// Convert an unmanaged string to a managed version. @@ -28,14 +38,14 @@ public static unsafe class Utf8StringMarshaller /// An unmanaged string /// A managed string public static string? ConvertToManaged(byte* unmanaged) - => Marshal.PtrToStringUTF8((nint)unmanaged); + => Marshal.PtrToStringUTF8((IntPtr)unmanaged); /// /// Free the memory for the unmanaged string. /// /// Memory allocated for the unmanaged string. public static void Free(byte* unmanaged) - => Marshal.FreeCoTaskMem((nint)unmanaged); + => NativeMemory.Free(unmanaged); /// /// Custom marshaller to marshal a managed string as a UTF-8 unmanaged string. From 431ec5c406cf281e97917bcb4f9a482bb22a0429 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 8 Jul 2022 21:37:26 -0700 Subject: [PATCH 6/7] Use CoTaskMemAlloc/Free --- .../InteropServices/Marshalling/AnsiStringMarshaller.cs | 7 ++++--- .../InteropServices/Marshalling/Utf8StringMarshaller.cs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index bb4548f98dd35e..93b53426ecfef8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -24,10 +24,11 @@ public static unsafe class AnsiStringMarshaller return null; int exactByteCount = Marshal.GetAnsiStringByteCount(managed); // Includes null terminator - Span buffer = new ((byte*)NativeMemory.Alloc((nuint)exactByteCount), exactByteCount); + Span buffer = new ((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); Marshal.GetAnsiStringBytes(managed, buffer); // Includes null terminator - return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + var ptr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + return ptr; } /// @@ -43,7 +44,7 @@ public static unsafe class AnsiStringMarshaller /// /// Memory allocated for the unmanaged string. public static void Free(byte* unmanaged) - => NativeMemory.Free(unmanaged); + => Marshal.FreeCoTaskMem((IntPtr)unmanaged); /// /// Custom marshaller to marshal a managed string as a ANSI unmanaged string. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index c735795e3855f3..bc5e7051df5177 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -25,11 +25,12 @@ public static unsafe class Utf8StringMarshaller return null; int exactByteCount = checked(Encoding.UTF8.GetByteCount(managed) + 1); // + 1 for null terminator - Span buffer = new ((byte*)NativeMemory.Alloc((nuint)exactByteCount), exactByteCount); + Span buffer = new ((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); int byteCount = Encoding.UTF8.GetBytes(managed, buffer); buffer[byteCount] = 0; // null-terminate - return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + var ptr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + return ptr; } /// @@ -45,7 +46,7 @@ public static unsafe class Utf8StringMarshaller /// /// Memory allocated for the unmanaged string. public static void Free(byte* unmanaged) - => NativeMemory.Free(unmanaged); + => Marshal.FreeCoTaskMem((IntPtr)unmanaged); /// /// Custom marshaller to marshal a managed string as a UTF-8 unmanaged string. From f2bcfce1aa16f5624cfa79734111520ca4031784 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 8 Jul 2022 21:48:06 -0700 Subject: [PATCH 7/7] Keep allocation in local. --- .../InteropServices/Marshalling/AnsiStringMarshaller.cs | 6 +++--- .../InteropServices/Marshalling/Utf8StringMarshaller.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index 93b53426ecfef8..7161dc4ee20ad4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -24,11 +24,11 @@ public static unsafe class AnsiStringMarshaller return null; int exactByteCount = Marshal.GetAnsiStringByteCount(managed); // Includes null terminator - Span buffer = new ((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + byte* mem = (byte*)Marshal.AllocCoTaskMem(exactByteCount); + Span buffer = new (mem, exactByteCount); Marshal.GetAnsiStringBytes(managed, buffer); // Includes null terminator - var ptr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); - return ptr; + return mem; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index bc5e7051df5177..a413349e4e4f29 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -25,12 +25,12 @@ public static unsafe class Utf8StringMarshaller return null; int exactByteCount = checked(Encoding.UTF8.GetByteCount(managed) + 1); // + 1 for null terminator - Span buffer = new ((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + byte* mem = (byte*)Marshal.AllocCoTaskMem(exactByteCount); + Span buffer = new (mem, exactByteCount); int byteCount = Encoding.UTF8.GetBytes(managed, buffer); buffer[byteCount] = 0; // null-terminate - var ptr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); - return ptr; + return mem; } ///