diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index 2175566c10b610..447e073c2603fe 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -194,6 +194,10 @@ BEGIN IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL "Unable to find an entry point named '%1' in DLL." IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO "Unable to find an entry point named '%1' in shared library." IDS_EE_NDIRECT_GETPROCADDRESS_NONAME "A library name must be specified in a DllImport attribute applied to non-IJW methods." + IDS_EE_NDIRECT_DISABLEDMARSHAL_SETLASTERROR "Setting SetLastError to 'true' is not supported when runtime marshalling is disabled." + IDS_EE_NDIRECT_DISABLEDMARSHAL_LCID "The LCIDConversionAttribute is not supported when runtime marshalling is disabled." + IDS_EE_NDIRECT_DISABLEDMARSHAL_PRESERVESIG "Setting PreserveSig to false for a P/Invoke is not supported when runtime marshalling is disabled." + IDS_EE_NDIRECT_DISABLEDMARSHAL_VARARGS "Using a variable argument list in a P/Invoke is not supported when runtime marshalling is disabled." IDS_EE_CLASS_CONSTRAINTS_VIOLATION "GenericArguments[%1], '%2', on '%3' violates the constraint of type parameter '%4'." IDS_EE_METHOD_CONSTRAINTS_VIOLATION "Method %1.%2: type argument '%3' violates the constraint of type parameter '%4'." IDS_EE_NOSYNCHRONIZED "Synchronized attribute cannot be used with this method type." @@ -297,6 +301,7 @@ BEGIN IDS_EE_BADMARSHAL_GENERICS_RESTRICTION "Non-blittable generic types cannot be marshaled." IDS_EE_BADMARSHAL_AUTOLAYOUT "Structures marked with [StructLayout(LayoutKind.Auto)] cannot be marshaled." IDS_EE_BADMARSHAL_STRING_OUT "Cannot marshal a string by-value with the [Out] attribute." + IDS_EE_BADMARSHAL_MARSHAL_DISABLED "Cannot marshal managed types when the runtime marshalling system is disabled." IDS_EE_BADMARSHALPARAM_STRINGBUILDER "Invalid managed/unmanaged type combination (StringBuilders must be paired with LPStr, LPWStr, or LPTStr)." IDS_EE_BADMARSHALPARAM_NO_LPTSTR "Invalid managed/unmanaged type combination (Strings cannot be paired with LPTStr for parameters and return types of methods in interfaces exposed to COM)." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index 05b31799ec5c47..7a8f148456a831 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -588,3 +588,8 @@ #define IDS_EE_BADMARSHAL_DELEGATE_TLB_INTERFACE 0x2649 #define IDS_EE_THREAD_APARTMENT_NOT_SUPPORTED 0x264A #define IDS_EE_NO_IINSPECTABLE 0x264B +#define IDS_EE_BADMARSHAL_MARSHAL_DISABLED 0x264C +#define IDS_EE_NDIRECT_DISABLEDMARSHAL_SETLASTERROR 0x264D +#define IDS_EE_NDIRECT_DISABLEDMARSHAL_LCID 0x264E +#define IDS_EE_NDIRECT_DISABLEDMARSHAL_PRESERVESIG 0x264F +#define IDS_EE_NDIRECT_DISABLEDMARSHAL_VARARGS 0x2650 diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index f800dadf431591..a9d4dd610b8ce6 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -658,7 +658,7 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN methodInfo->options |= CorInfoOptions.CORINFO_GENERICS_CTXT_FROM_METHODTABLE; } methodInfo->regionKind = CorInfoRegionKind.CORINFO_REGION_NONE; - Get_CORINFO_SIG_INFO(method, sig: &methodInfo->args); + Get_CORINFO_SIG_INFO(method, sig: &methodInfo->args, methodIL); Get_CORINFO_SIG_INFO(methodIL.GetLocals(), &methodInfo->locals); return true; @@ -683,9 +683,9 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN return (CORINFO_CLASS_STRUCT_**)GetPin(jitVisibleInstantiation); } - private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool suppressHiddenArgument = false) + private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, MethodILScope scope, bool suppressHiddenArgument = false) { - Get_CORINFO_SIG_INFO(method.Signature, sig); + Get_CORINFO_SIG_INFO(method.Signature, sig, scope); // Does the method have a hidden parameter? bool hasHiddenParameter = !suppressHiddenArgument && method.RequiresInstArg(); @@ -845,7 +845,7 @@ private static CorInfoCallConvExtension GetMemberFunctionCallingConventionVarian var c => c }; - private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig) + private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig, MethodILScope scope) { sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask); @@ -875,7 +875,7 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s sig->pSig = null; sig->cbSig = 0; // Not used by the JIT sig->methodSignature = ObjectToHandle(signature); - sig->scope = null; + sig->scope = scope is not null ? ObjectToHandle(scope) : null; // scope can be null for internal calls and COM methods. sig->token = 0; // Not used by the JIT } @@ -1149,7 +1149,7 @@ private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CO } } - Get_CORINFO_SIG_INFO(method, sig: sig); + Get_CORINFO_SIG_INFO(method, sig: sig, scope: null); } private bool getMethodInfo(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_INFO* info) @@ -1795,7 +1795,7 @@ private void findSig(CORINFO_MODULE_STRUCT_* module, uint sigTOK, CORINFO_CONTEX var methodIL = HandleToObject(module); var methodSig = (MethodSignature)methodIL.GetObject((int)sigTOK); - Get_CORINFO_SIG_INFO(methodSig, sig); + Get_CORINFO_SIG_INFO(methodSig, sig, methodIL); #if !READYTORUN // Check whether we need to report this as a fat pointer call @@ -1811,7 +1811,7 @@ private void findSig(CORINFO_MODULE_STRUCT_* module, uint sigTOK, CORINFO_CONTEX private void findCallSiteSig(CORINFO_MODULE_STRUCT_* module, uint methTOK, CORINFO_CONTEXT_STRUCT* context, CORINFO_SIG_INFO* sig) { var methodIL = HandleToObject(module); - Get_CORINFO_SIG_INFO(((MethodDesc)methodIL.GetObject((int)methTOK)), sig: sig); + Get_CORINFO_SIG_INFO(((MethodDesc)methodIL.GetObject((int)methTOK)), sig: sig, methodIL); } private CORINFO_CLASS_STRUCT_* getTokenTypeAsHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken) @@ -3747,7 +3747,7 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) #if READYTORUN // TODO: enable this check in full AOT - if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, Array.Empty())) // Only blittable arguments + if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, Array.Empty(), ((MetadataType)this.MethodBeingCompiled.OwningType).Module)) // Only blittable arguments { ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNonBlittableTypes, this.MethodBeingCompiled); } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs b/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs index 71c7423b8fbb04..2190f9ac1a2785 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs @@ -63,6 +63,11 @@ private static class FieldLayoutFlags /// True if type transitively has UnsafeValueTypeAttribute /// public const int IsUnsafeValueType = 0x200; + + /// + /// True if the type transitively has any types with LayoutKind.Auto in its layout. + /// + public const int IsAutoLayoutOrHasAutoLayoutFields = 0x400; } private class StaticBlockInfo @@ -115,6 +120,21 @@ public bool IsUnsafeValueType } } + /// + /// Does a type have auto-layout or transitively have any fields of a type with auto-layout. + /// + public virtual bool IsAutoLayoutOrHasAutoLayoutFields + { + get + { + if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedInstanceTypeLayout)) + { + ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields); + } + return _fieldLayoutFlags.HasFlags(FieldLayoutFlags.IsAutoLayoutOrHasAutoLayoutFields); + } + } + /// /// The number of bytes required to hold a field of this type @@ -406,6 +426,10 @@ public void ComputeInstanceLayout(InstanceLayoutKind layoutKind) { _fieldLayoutFlags.AddFlags(FieldLayoutFlags.ComputedInstanceLayoutAbiUnstable); } + if (computedLayout.IsAutoLayoutOrHasAutoLayoutFields) + { + _fieldLayoutFlags.AddFlags(FieldLayoutFlags.IsAutoLayoutOrHasAutoLayoutFields); + } if (computedLayout.Offsets != null) { diff --git a/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutAlgorithm.cs index 863e857a21def8..a19ec4b3603bf4 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutAlgorithm.cs @@ -82,6 +82,7 @@ public struct ComputedInstanceFieldLayout public LayoutInt ByteCountUnaligned; public LayoutInt ByteCountAlignment; public bool LayoutAbiStable; // Is the layout stable such that it can safely be used in function calling conventions + public bool IsAutoLayoutOrHasAutoLayoutFields; /// /// If Offsets is non-null, then all field based layout is complete. diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 249faef7cd0405..9973be45ff1308 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -112,7 +112,8 @@ out instanceByteSizeAndAlignment ByteCountAlignment = instanceByteSizeAndAlignment.Alignment, FieldAlignment = sizeAndAlignment.Alignment, FieldSize = sizeAndAlignment.Size, - LayoutAbiStable = true + LayoutAbiStable = true, + IsAutoLayoutOrHasAutoLayoutFields = false, }; if (numInstanceFields > 0) @@ -214,7 +215,7 @@ public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defTy } ref StaticsBlock block = ref GetStaticsBlockForField(ref result, field); - SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout: false, context.Target.DefaultPackingSize, out bool _); + SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout: false, context.Target.DefaultPackingSize, out bool _, out bool _); block.Size = LayoutInt.AlignUp(block.Size, sizeAndAlignment.Alignment, context.Target); result.Offsets[index] = new FieldAndOffset(field, block.Size); @@ -305,13 +306,16 @@ protected ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType ty var offsets = new FieldAndOffset[numInstanceFields]; int fieldOrdinal = 0; bool layoutAbiStable = true; + bool hasAutoLayoutField = false; foreach (var fieldAndOffset in layoutMetadata.Offsets) { TypeDesc fieldType = fieldAndOffset.Field.FieldType; - var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType.UnderlyingType, hasLayout: true, packingSize, out bool fieldLayoutAbiStable); + var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType.UnderlyingType, hasLayout: true, packingSize, out bool fieldLayoutAbiStable, out bool fieldHasAutoLayout); if (!fieldLayoutAbiStable) layoutAbiStable = false; + if (fieldHasAutoLayout) + hasAutoLayoutField = true; largestAlignmentRequired = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequired); @@ -354,7 +358,10 @@ protected ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType ty layoutMetadata.Size, out instanceByteSizeAndAlignment); - ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); + ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout + { + IsAutoLayoutOrHasAutoLayoutFields = hasAutoLayoutField, + }; computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment; computedLayout.FieldSize = instanceSizeAndAlignment.Size; computedLayout.ByteCountUnaligned = instanceByteSizeAndAlignment.Size; @@ -388,15 +395,18 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType int fieldOrdinal = 0; int packingSize = ComputePackingSize(type, layoutMetadata); bool layoutAbiStable = true; + bool hasAutoLayoutField = false; foreach (var field in type.GetFields()) { if (field.IsStatic) continue; - var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType.UnderlyingType, hasLayout: true, packingSize, out bool fieldLayoutAbiStable); + var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType.UnderlyingType, hasLayout: true, packingSize, out bool fieldLayoutAbiStable, out bool fieldHasAutoLayout); if (!fieldLayoutAbiStable) layoutAbiStable = false; + if (fieldHasAutoLayout) + hasAutoLayoutField = true; largestAlignmentRequirement = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequirement); @@ -415,7 +425,10 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType layoutMetadata.Size, out instanceByteSizeAndAlignment); - ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); + ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout + { + IsAutoLayoutOrHasAutoLayoutFields = hasAutoLayoutField, + }; computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment; computedLayout.FieldSize = instanceSizeAndAlignment.Size; computedLayout.ByteCountUnaligned = instanceByteSizeAndAlignment.Size; @@ -470,7 +483,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, { Debug.Assert(fieldType.IsPrimitive || fieldType.IsPointer || fieldType.IsFunctionPointer || fieldType.IsEnum); - var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout, packingSize, out bool _); + var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout, packingSize, out bool _, out bool _); instanceNonGCPointerFieldsCount[CalculateLog2(fieldSizeAndAlignment.Size.AsInt)]++; } } @@ -507,7 +520,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, TypeDesc fieldType = field.FieldType; - var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable); + var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable, out bool _); if (!fieldLayoutAbiStable) layoutAbiStable = false; @@ -646,7 +659,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, for (int i = 0; i < instanceValueClassFieldsArr.Length; i++) { // Align the cumulative field offset to the indeterminate value - var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(instanceValueClassFieldsArr[i].FieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable); + var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(instanceValueClassFieldsArr[i].FieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable, out bool _); if (!fieldLayoutAbiStable) layoutAbiStable = false; @@ -694,7 +707,10 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, classLayoutSize: 0, byteCount: out instanceByteSizeAndAlignment); - ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); + ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout + { + IsAutoLayoutOrHasAutoLayoutFields = true, + }; computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment; computedLayout.FieldSize = instanceSizeAndAlignment.Size; computedLayout.ByteCountUnaligned = instanceByteSizeAndAlignment.Size; @@ -707,7 +723,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, private static void PlaceInstanceField(FieldDesc field, bool hasLayout, int packingSize, FieldAndOffset[] offsets, ref LayoutInt instanceFieldPos, ref int fieldOrdinal, LayoutInt offsetBias) { - var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, hasLayout, packingSize, out bool _); + var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, hasLayout, packingSize, out bool _, out bool _); instanceFieldPos = AlignUpInstanceFieldOffset(field.OwningType, instanceFieldPos, fieldSizeAndAlignment.Alignment, field.Context.Target); offsets[fieldOrdinal] = new FieldAndOffset(field, instanceFieldPos + offsetBias); @@ -767,10 +783,11 @@ public LayoutInt CalculateFieldBaseOffset(MetadataType type, bool requiresAlign8 return cumulativeInstanceFieldPos; } - private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, bool hasLayout, int packingSize, out bool layoutAbiStable) + private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, bool hasLayout, int packingSize, out bool layoutAbiStable, out bool fieldTypeHasAutoLayout) { SizeAndAlignment result; layoutAbiStable = true; + fieldTypeHasAutoLayout = true; if (fieldType.IsDefType) { @@ -780,6 +797,7 @@ private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, result.Size = defType.InstanceFieldSize; result.Alignment = defType.InstanceFieldAlignment; layoutAbiStable = defType.LayoutAbiStable; + fieldTypeHasAutoLayout = defType.IsAutoLayoutOrHasAutoLayoutFields; } else { diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaAssembly.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaAssembly.cs index 0e155fda998f34..5a281d21ba11ad 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaAssembly.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaAssembly.cs @@ -67,7 +67,7 @@ public override string ToString() public bool HasAssemblyCustomAttribute(string attributeNamespace, string attributeName) { - return _metadataReader.GetCustomAttributeHandle(_assemblyDefinition.GetCustomAttributes(), + return !_metadataReader.GetCustomAttributeHandle(_assemblyDefinition.GetCustomAttributes(), attributeNamespace, attributeName).IsNil; } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs index c68f9a10fe2fa6..dd91b65fa90798 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs @@ -20,7 +20,7 @@ string IPrefixMangledSignature.Prefix { get { - return "Calli"; + return RuntimeMarshallingEnabled ? "CalliWithRuntimeMarshalling" : "Calli"; } } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs index 89be9fa3a7d881..cd6e620aa86074 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs @@ -13,6 +13,11 @@ partial class CalliMarshallingMethodThunk protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) { var otherMethod = (CalliMarshallingMethodThunk)other; + int result = RuntimeMarshallingEnabled.CompareTo(otherMethod.RuntimeMarshallingEnabled); + if (result != 0) + { + return result; + } return comparer.Compare(_targetSignature, otherMethod._targetSignature); } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs index bdbd7b67998f48..d0d397bafdf388 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs @@ -21,11 +21,13 @@ public partial class CalliMarshallingMethodThunk : ILStubMethod private MethodSignature _signature; public CalliMarshallingMethodThunk(MethodSignature targetSignature, TypeDesc owningType, - InteropStateManager interopStateManager) + InteropStateManager interopStateManager, + bool runtimeMarshallingEnabled) { _targetSignature = targetSignature; _owningType = owningType; _interopStateManager = interopStateManager; + RuntimeMarshallingEnabled = runtimeMarshallingEnabled; } public MethodSignature TargetSignature @@ -87,6 +89,8 @@ public override string DiagnosticName } } + public bool RuntimeMarshallingEnabled { get; } + public override PInvokeMetadata GetPInvokeMethodMetadata() { // Return PInvokeAttributes.PreserveSig to circumvent marshalling required checks diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs index d77bb5994ec0e5..81245ffe82515a 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs @@ -12,11 +12,6 @@ namespace Internal.IL.Stubs { /// /// Provides method bodies for PInvoke methods - /// - /// This by no means intends to provide full PInvoke support. The intended use of this is to - /// a) prevent calls getting generated to targets that require a full marshaller - /// (this compiler doesn't provide that), and b) offer a hand in some very simple marshalling - /// situations (but support for this part might go away as the product matures). /// public struct PInvokeILEmitter { @@ -55,23 +50,27 @@ private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, Inter { MarshalDirection direction = MarshalDirection.Forward; MethodSignature methodSig; + bool runtimeMarshallingEnabled; switch (targetMethod) { case DelegateMarshallingMethodThunk delegateMethod: methodSig = delegateMethod.DelegateSignature; direction = delegateMethod.Direction; + runtimeMarshallingEnabled = MarshalHelpers.IsRuntimeMarshallingEnabled(delegateMethod.DelegateType.Module); break; case CalliMarshallingMethodThunk calliMethod: methodSig = calliMethod.TargetSignature; + runtimeMarshallingEnabled = calliMethod.RuntimeMarshallingEnabled; break; default: methodSig = targetMethod.Signature; + runtimeMarshallingEnabled = MarshalHelpers.IsRuntimeMarshallingEnabled(((MetadataType)targetMethod.OwningType).Module); break; } int indexOffset = 0; if (!methodSig.IsStatic && direction == MarshalDirection.Forward) { - // For instance methods(eg. Forward delegate marshalling thunk), first argument is + // For instance methods(eg. Forward delegate marshalling thunk), first argument is // the instance indexOffset = 1; } @@ -88,7 +87,7 @@ private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, Inter // if we don't have metadata for the parameter, create a dummy one parameterMetadata = new ParameterMetadata(i, ParameterMetadataAttributes.None, null); } - else + else { Debug.Assert(i == parameterMetadataArray[parameterIndex].Index); parameterMetadata = parameterMetadataArray[parameterIndex++]; @@ -104,7 +103,7 @@ private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, Inter { // PreserveSig = false can only show up an regular forward PInvokes Debug.Assert(direction == MarshalDirection.Forward); - + parameterType = methodSig.Context.GetByRefType(parameterType); isHRSwappedRetVal = true; } @@ -114,20 +113,35 @@ private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, Inter parameterType = methodSig[i - 1]; } - marshallers[i] = Marshaller.CreateMarshaller(parameterType, - parameterIndex, - methodSig.GetEmbeddedSignatureData(), - MarshallerType.Argument, - parameterMetadata.MarshalAsDescriptor, - direction, - marshallers, - interopStateManager, - indexOffset + parameterMetadata.Index, - flags, - parameterMetadata.In, - isHRSwappedRetVal ? true : parameterMetadata.Out, - isHRSwappedRetVal ? false : parameterMetadata.Return - ); + if (runtimeMarshallingEnabled) + { + marshallers[i] = Marshaller.CreateMarshaller(parameterType, + parameterIndex, + methodSig.GetEmbeddedSignatureData(), + MarshallerType.Argument, + parameterMetadata.MarshalAsDescriptor, + direction, + marshallers, + interopStateManager, + indexOffset + parameterMetadata.Index, + flags, + parameterMetadata.In, + isHRSwappedRetVal ? true : parameterMetadata.Out, + isHRSwappedRetVal ? false : parameterMetadata.Return + ); + } + else + { + marshallers[i] = Marshaller.CreateDisabledMarshaller( + parameterType, + parameterIndex, + MarshallerType.Argument, + direction, + marshallers, + indexOffset + parameterMetadata.Index, + flags, + parameterMetadata.Return); + } } return marshallers; @@ -146,10 +160,10 @@ private void EmitDelegateCall(DelegateMarshallingMethodThunk delegateMethod, PIn if (delegateMethod.Kind == DelegateMarshallingMethodThunkKind.ReverseOpenStatic) { // - // For Open static delegates call + // For Open static delegates call // InteropHelpers.GetCurrentCalleeOpenStaticDelegateFunctionPointer() // which returns a function pointer. Just call the function pointer and we are done. - // + // TypeDesc[] parameters = new TypeDesc[_marshallers.Length - 1]; for (int i = 1; i < _marshallers.Length; i++) { @@ -193,7 +207,7 @@ private void EmitDelegateCall(DelegateMarshallingMethodThunk delegateMethod, PIn else if (delegateMethod.Kind == DelegateMarshallingMethodThunkKind .ForwardNativeFunctionWrapper) { - // if the SetLastError flag is set in UnmanagedFunctionPointerAttribute, clear the error code before doing P/Invoke + // if the SetLastError flag is set in UnmanagedFunctionPointerAttribute, clear the error code before doing P/Invoke if (_flags.SetLastError) { callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( @@ -225,7 +239,7 @@ private void EmitDelegateCall(DelegateMarshallingMethodThunk delegateMethod, PIn MethodSignatureFlags unmanagedCallingConvention = _flags.UnmanagedCallingConvention; if (unmanagedCallingConvention == MethodSignatureFlags.None) unmanagedCallingConvention = MethodSignatureFlags.UnmanagedCallingConvention; - + MethodSignature nativeSig = new MethodSignature( MethodSignatureFlags.Static | unmanagedCallingConvention, 0, nativeReturnType, nativeParameterTypes); @@ -257,9 +271,16 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) TypeDesc nativeReturnType = _flags.PreserveSig ? _marshallers[0].NativeParameterType : context.GetWellKnownType(WellKnownType.Int32); TypeDesc[] nativeParameterTypes = new TypeDesc[isHRSwappedRetVal ? _marshallers.Length : _marshallers.Length - 1]; - // if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke + bool runtimeMarshallingEnabled = MarshalHelpers.IsRuntimeMarshallingEnabled(((MetadataType)_targetMethod.OwningType).Module); + + // if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke if (_flags.SetLastError) { + if (!runtimeMarshallingEnabled) + { + // When runtime marshalling is disabled, we don't support SetLastError + throw new NotSupportedException(); + } callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( InteropTypes.GetPInvokeMarshal(context).GetKnownMethod("ClearLastError", null))); } @@ -271,6 +292,11 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) if (isHRSwappedRetVal) { + if (!runtimeMarshallingEnabled) + { + // When runtime marshalling is disabled, we don't support HResult-return-swapping + throw new NotSupportedException(); + } nativeParameterTypes[_marshallers.Length - 1] = _marshallers[0].NativeParameterType; } @@ -364,6 +390,9 @@ private void EmitCalli(PInvokeILCodeStreams ilCodeStreams, CalliMarshallingMetho private MethodIL EmitIL() { + if (_targetMethod.HasCustomAttribute("System.Runtime.InteropServices", "LCIDConversionAttribute")) + throw new NotSupportedException(); + PInvokeILCodeStreams pInvokeILCodeStreams = new PInvokeILCodeStreams(); ILEmitter emitter = pInvokeILCodeStreams.Emitter; ILCodeStream marshallingCodestream = pInvokeILCodeStreams.MarshallingCodeStream; @@ -417,8 +446,8 @@ private MethodIL EmitIL() return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod), IsStubRequired()); } - public static MethodIL EmitIL(MethodDesc method, - PInvokeILEmitterConfiguration pinvokeILEmitterConfiguration, + public static MethodIL EmitIL(MethodDesc method, + PInvokeILEmitterConfiguration pinvokeILEmitterConfiguration, InteropStateManager interopStateManager) { try diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs index 8925f9964b093c..b047f29e9ec64f 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs @@ -126,7 +126,7 @@ public static MethodIL EmitExceptionBody(string message, MethodDesc method) TypeSystemContext context = method.Context; MethodSignature ctorSignature = new MethodSignature(0, 0, context.GetWellKnownType(WellKnownType.Void), new TypeDesc[] { context.GetWellKnownType(WellKnownType.String) }); - MethodDesc exceptionCtor = method.Context.GetWellKnownType(WellKnownType.Exception).GetKnownMethod(".ctor", ctorSignature); + MethodDesc exceptionCtor = InteropTypes.GetMarshalDirectiveException(context).GetKnownMethod(".ctor", ctorSignature); ILCodeStream codeStream = emitter.NewCodeStream(); codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(message)); diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs index a736d065dd8332..e0f1d35809351f 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs @@ -5,6 +5,7 @@ using Internal.IL; using Debug = System.Diagnostics.Debug; using Internal.IL.Stubs; +using Internal.TypeSystem.Ecma; namespace Internal.TypeSystem.Interop { @@ -75,6 +76,7 @@ internal static TypeDesc GetNativeTypeFromMarshallerKind(TypeDesc type, #if !READYTORUN case MarshallerKind.Struct: case MarshallerKind.LayoutClass: + Debug.Assert(interopStateManager is not null, "An InteropStateManager is required to look up the native representation of a non-blittable struct or class with layout."); return interopStateManager.GetStructMarshallingNativeType((MetadataType)type); #endif @@ -844,6 +846,37 @@ private static MarshallerKind GetArrayElementMarshallerKind( } } + internal static MarshallerKind GetDisabledMarshallerKind( + TypeDesc type) + { + if (type.Category == TypeFlags.Void) + { + return MarshallerKind.VoidReturn; + } + else if (type.IsByRef) + { + // Managed refs are not supported when runtime marshalling is disabled. + return MarshallerKind.Invalid; + } + else if (type.IsPrimitive) + { + return MarshallerKind.BlittableValue; + } + else if (type.IsPointer || type.IsFunctionPointer) + { + return MarshallerKind.BlittableValue; + } + else if (type.IsValueType) + { + var defType = (DefType)type; + if (!defType.ContainsGCPointers && !defType.IsAutoLayoutOrHasAutoLayoutFields) + { + return MarshallerKind.BlittableValue; + } + } + return MarshallerKind.Invalid; + } + internal static bool ShouldCheckForPendingException(TargetDetails target, PInvokeMetadata metadata) { if (!target.IsOSX) @@ -861,5 +894,10 @@ internal static bool ShouldCheckForPendingException(TargetDetails target, PInvok return metadata.Module.Equals(ObjectiveCLibrary) && metadata.Name.StartsWith(ObjectiveCMsgSend); } + + public static bool IsRuntimeMarshallingEnabled(ModuleDesc module) + { + return module.Assembly is not EcmaAssembly assembly || !assembly.HasAssemblyCustomAttribute("System.Runtime.CompilerServices", "DisableRuntimeMarshallingAttribute"); + } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs index 40e316e2834288..d6efc6b22582ed 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs @@ -382,6 +382,44 @@ public static Marshaller CreateMarshaller(TypeDesc parameterType, return marshaller; } + + /// + /// Create a marshaller + /// + /// type of the parameter to marshal + /// The created Marshaller + public static Marshaller CreateDisabledMarshaller(TypeDesc parameterType, + int? parameterIndex, + MarshallerType marshallerType, + MarshalDirection direction, + Marshaller[] marshallers, + int index, + PInvokeFlags flags, + bool isReturn) + { + MarshallerKind marshallerKind = MarshalHelpers.GetDisabledMarshallerKind(parameterType); + + TypeSystemContext context = parameterType.Context; + // Create the marshaller based on MarshallerKind + Marshaller marshaller = CreateMarshaller(marshallerKind); + marshaller.Context = context; + marshaller.MarshallerKind = marshallerKind; + marshaller.MarshallerType = marshallerType; + marshaller.ElementMarshallerKind = MarshallerKind.Unknown; + marshaller.ManagedParameterType = parameterType; + marshaller.ManagedType = parameterType; + marshaller.Return = isReturn; + marshaller.IsManagedByRef = false; + marshaller.IsNativeByRef = false; + marshaller.MarshalDirection = direction; + marshaller.MarshalAsDescriptor = null; + marshaller.Marshallers = marshallers; + marshaller.Index = index; + marshaller.PInvokeFlags = flags; + marshaller.In = true; + marshaller.Out = false; + return marshaller; + } #endregion @@ -1613,7 +1651,7 @@ protected override void EmitCleanupManaged(ILCodeStream codeStream) class AnsiStringMarshaller : Marshaller { #if READYTORUN - const int MAX_LOCAL_BUFFER_LENGTH = 260 + 1; // MAX_PATH + 1 + const int MAX_LOCAL_BUFFER_LENGTH = 260 + 1; // MAX_PATH + 1 private ILLocalVariable? _localBuffer = null; #endif @@ -1650,7 +1688,7 @@ protected override void TransformManagedToNative(ILCodeStream codeStream) .GetKnownMethod("ConvertToNative", null); bool bPassByValueInOnly = In && !Out && !IsManagedByRef; - + if (bPassByValueInOnly) { var bufSize = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); @@ -1685,7 +1723,7 @@ protected override void TransformManagedToNative(ILCodeStream codeStream) codeStream.EmitStLoc(bufSize); // if (MAX_LOCAL_BUFFER_LENGTH < BufSize ) goto NoOptimize - codeStream.EmitLdc(MAX_LOCAL_BUFFER_LENGTH + 1); + codeStream.EmitLdc(MAX_LOCAL_BUFFER_LENGTH + 1); codeStream.EmitLdLoc(bufSize); codeStream.Emit(ILOpcode.clt); codeStream.Emit(ILOpcode.brtrue, noOptimize); diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs b/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs index 202fcce90becef..0d594010328e67 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs @@ -38,7 +38,7 @@ public InteropStateManager(ModuleDesc generatedAssembly) // // Delegate Marshalling Stubs // - + /// /// Generates marshalling stubs for open static delegates /// @@ -105,9 +105,9 @@ public PInvokeDelegateWrapper GetPInvokeDelegateWrapper(TypeDesc delegateType) } // - // Struct Marshalling + // Struct Marshalling // To support struct marshalling compiler needs to generate a native type which - // imitates the original struct being passed to managed side with corresponding + // imitates the original struct being passed to managed side with corresponding // fields of marshalled types. Additionally it needs to generate three thunks // 1. Managed to Native Thunk: For forward marshalling // 2. Native to Managed Thunk: For reverse marshalling @@ -186,9 +186,9 @@ public FieldDesc GetPInvokeLazyFixupField(MethodDesc method) return _pInvokeLazyFixupFieldHashtable.GetOrCreateValue(method); } - public MethodDesc GetPInvokeCalliStub(MethodSignature signature) + public MethodDesc GetPInvokeCalliStub(MethodSignature signature, ModuleDesc moduleContext) { - return _pInvokeCalliHashtable.GetOrCreateValue(signature); + return _pInvokeCalliHashtable.GetOrCreateValue(new CalliMarshallingMethodThunkKey(signature, MarshalHelpers.IsRuntimeMarshallingEnabled(moduleContext))); } private class NativeStructTypeHashtable : LockFreeReaderHashtable @@ -355,7 +355,7 @@ protected override bool CompareValueToValue(DelegateMarshallingMethodThunk value protected override DelegateMarshallingMethodThunk CreateValueFromKey(DelegateMarshallingStubHashtableKey key) { - return new DelegateMarshallingMethodThunk(key.DelegateType, _owningType, + return new DelegateMarshallingMethodThunk(key.DelegateType, _owningType, _interopStateManager, key.Kind); } @@ -478,34 +478,36 @@ public PInvokeLazyFixupFieldHashtable(DefType owningType) } } - private class PInvokeCalliHashtable : LockFreeReaderHashtable + private readonly record struct CalliMarshallingMethodThunkKey(MethodSignature Signature, bool RuntimeMarshallingEnabled); + + private class PInvokeCalliHashtable : LockFreeReaderHashtable { private readonly InteropStateManager _interopStateManager; private readonly TypeDesc _owningType; - protected override int GetKeyHashCode(MethodSignature key) + protected override int GetKeyHashCode(CalliMarshallingMethodThunkKey key) { return key.GetHashCode(); } protected override int GetValueHashCode(CalliMarshallingMethodThunk value) { - return value.TargetSignature.GetHashCode(); + return new CalliMarshallingMethodThunkKey(value.TargetSignature, value.RuntimeMarshallingEnabled).GetHashCode(); } - protected override bool CompareKeyToValue(MethodSignature key, CalliMarshallingMethodThunk value) + protected override bool CompareKeyToValue(CalliMarshallingMethodThunkKey key, CalliMarshallingMethodThunk value) { - return key.Equals(value.TargetSignature); + return key.Signature.Equals(value.TargetSignature) && key.RuntimeMarshallingEnabled == value.RuntimeMarshallingEnabled; } protected override bool CompareValueToValue(CalliMarshallingMethodThunk value1, CalliMarshallingMethodThunk value2) { - return value1.TargetSignature.Equals(value2.TargetSignature); + return value1.TargetSignature.Equals(value2.TargetSignature) && value1.RuntimeMarshallingEnabled == value2.RuntimeMarshallingEnabled; } - protected override CalliMarshallingMethodThunk CreateValueFromKey(MethodSignature key) + protected override CalliMarshallingMethodThunk CreateValueFromKey(CalliMarshallingMethodThunkKey key) { - return new CalliMarshallingMethodThunk(key, _owningType, _interopStateManager); + return new CalliMarshallingMethodThunk(key.Signature, _owningType, _interopStateManager, key.RuntimeMarshallingEnabled); } public PInvokeCalliHashtable(InteropStateManager interopStateManager, TypeDesc owningType) diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/InteropTypes.cs b/src/coreclr/tools/Common/TypeSystem/Interop/InteropTypes.cs index 4007dc756494ee..998bf3c03a7843 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/InteropTypes.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/InteropTypes.cs @@ -54,6 +54,11 @@ public static MetadataType GetNativeFunctionPointerWrapper(TypeSystemContext con return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "NativeFunctionPointerWrapper"); } + public static MetadataType GetMarshalDirectiveException(TypeSystemContext context) + { + return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "MarshalDirectiveException"); + } + public static MetadataType GetVariant(TypeSystemContext context) { return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "Variant"); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs index 9aa675ca738fc0..4d10465a4d8ec7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs @@ -25,7 +25,7 @@ public UsageBasedInteropStubManager(InteropStateManager interopStateManager, PIn public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { - if (method.IsPInvoke) + if (method.IsPInvoke && method.OwningType is MetadataType type && MarshalHelpers.IsRuntimeMarshallingEnabled(type.Module)) { dependencies = dependencies ?? new DependencyList(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs index 830fb11d1ef21a..93fb4cc376c07e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs @@ -30,9 +30,9 @@ public override MethodIL GetMethodIL(MethodDesc method) return PInvokeILEmitter.EmitIL(method, _pInvokeILEmitterConfiguration, _interopStateManager); } - public MethodDesc GetCalliStub(MethodSignature signature) + public MethodDesc GetCalliStub(MethodSignature signature, ModuleDesc moduleContext) { - return _interopStateManager.GetPInvokeCalliStub(signature); + return _interopStateManager.GetPInvokeCalliStub(signature, moduleContext); } public string GetDirectCallExternName(MethodDesc method) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs index a8e42ec37b9bc0..6fc1687d7a7f99 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs @@ -46,6 +46,13 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) // if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke if (_importMetadata.Flags.SetLastError) { + if (!MarshalHelpers.IsRuntimeMarshallingEnabled(((MetadataType)_targetMethod.OwningType).Module)) + { + // When runtime marshalling is disabled, we don't support generating the stub IL + // in Ready-to-Run so we can correctly throw an exception at runtime when the user tries to + // use SetLastError=true when marshalling is disabled. + throw new NotSupportedException(); + } callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( stubHelpersType.GetKnownMethod("ClearLastError", null))); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs index 927148cf006e26..ddb1d24c1b5357 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs @@ -43,7 +43,8 @@ protected static Marshaller CreateMarshaller(MarshallerKind kind) private static Marshaller[] GetMarshallers( MethodSignature methodSig, PInvokeFlags flags, - ParameterMetadata[] parameterMetadataArray) + ParameterMetadata[] parameterMetadataArray, + bool runtimeMarshallingEnabled) { Marshaller[] marshallers = new Marshaller[methodSig.Length + 1]; @@ -64,39 +65,55 @@ private static Marshaller[] GetMarshallers( } TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type - marshallers[i] = CreateMarshaller(parameterType, - parameterIndex, - methodSig.GetEmbeddedSignatureData(), - MarshallerType.Argument, - parameterMetadata.MarshalAsDescriptor, - MarshalDirection.Forward, - marshallers, - parameterMetadata.Index, - flags, - parameterMetadata.In, - parameterMetadata.Out, - parameterMetadata.Return); + if (runtimeMarshallingEnabled) + { + marshallers[i] = CreateMarshaller(parameterType, + parameterIndex, + methodSig.GetEmbeddedSignatureData(), + MarshallerType.Argument, + parameterMetadata.MarshalAsDescriptor, + MarshalDirection.Forward, + marshallers, + parameterMetadata.Index, + flags, + parameterMetadata.In, + parameterMetadata.Out, + parameterMetadata.Return); + } + else + { + marshallers[i] = CreateDisabledMarshaller( + parameterType, + parameterIndex, + MarshallerType.Argument, + MarshalDirection.Forward, + marshallers, + parameterMetadata.Index, + flags, + parameterMetadata.Return); + } } return marshallers; } - public static Marshaller[] GetMarshallersForMethod(MethodDesc targetMethod) { Debug.Assert(targetMethod.IsPInvoke); return GetMarshallers( targetMethod.Signature, targetMethod.GetPInvokeMethodMetadata().Flags, - targetMethod.GetParameterMetadata()); + targetMethod.GetParameterMetadata(), + MarshalHelpers.IsRuntimeMarshallingEnabled(((MetadataType)targetMethod.OwningType).Module)); } - public static Marshaller[] GetMarshallersForSignature(MethodSignature methodSig, ParameterMetadata[] paramMetadata) + public static Marshaller[] GetMarshallersForSignature(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext) { return GetMarshallers( methodSig, new PInvokeFlags(PInvokeAttributes.None), - paramMetadata); + paramMetadata, + MarshalHelpers.IsRuntimeMarshallingEnabled(moduleContext)); } public static bool IsMarshallingRequired(MethodDesc targetMethod) @@ -128,9 +145,9 @@ public static bool IsMarshallingRequired(MethodDesc targetMethod) return false; } - public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMetadata[] paramMetadata) + public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext) { - Marshaller[] marshallers = GetMarshallersForSignature(methodSig, paramMetadata); + Marshaller[] marshallers = GetMarshallersForSignature(methodSig, paramMetadata, moduleContext); for (int i = 0; i < marshallers.Length; i++) { if (marshallers[i].IsMarshallingRequired()) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index f8282ab82cd27c..c2dcc6b397016c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1811,7 +1811,7 @@ private void ceeInfoGetCallInfo( pResult->wrapperDelegateInvoke = false; - Get_CORINFO_SIG_INFO(methodToCall, &pResult->sig, useInstantiatingStub); + Get_CORINFO_SIG_INFO(methodToCall, &pResult->sig, scope: null, useInstantiatingStub); } private uint getMethodAttribs(CORINFO_METHOD_STRUCT_* ftn) @@ -2579,7 +2579,7 @@ private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_S else { var sig = HandleToObject(callSiteSig->methodSignature); - return Marshaller.IsMarshallingRequired(sig, Array.Empty()); + return Marshaller.IsMarshallingRequired(sig, Array.Empty(), ((MetadataType)HandleToObject(callSiteSig->scope).OwningMethod.OwningType).Module); } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 5966efead0066f..1b1fbdf550777d 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -1027,7 +1027,7 @@ private CorInfoHelpFunc getNewHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, TypeDesc type = HandleToObject(pResolvedToken.hClass); Debug.Assert(!type.IsString && !type.IsArray && !type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); - + pHasSideEffects = type.HasFinalizer; if (type.RequiresAlign8()) @@ -1520,7 +1520,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO targetIsFatFunctionPointer |= (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0 && !(pResult->kind == CORINFO_CALL_KIND.CORINFO_CALL); - Get_CORINFO_SIG_INFO(targetMethod, &pResult->sig, targetIsFatFunctionPointer); + Get_CORINFO_SIG_INFO(targetMethod, &pResult->sig, scope: null, targetIsFatFunctionPointer); if (useFatCallTransform) { pResult->sig.flags |= CorInfoSigInfoFlags.CORINFO_SIGFLAG_FAT_CALL; @@ -1531,7 +1531,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO if (pResult->hMethod != pResolvedToken.hMethod) { pResult->verMethodFlags = getMethodAttribsInternal(targetMethod); - Get_CORINFO_SIG_INFO(targetMethod, &pResult->verSig); + Get_CORINFO_SIG_INFO(targetMethod, &pResult->verSig, scope: null); } else { @@ -1791,7 +1791,9 @@ private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_S #if DEBUG MethodSignature methodSignature = (MethodSignature)HandleToObject((IntPtr)callSiteSig->pSig); - MethodDesc stub = _compilation.PInvokeILProvider.GetCalliStub(methodSignature); + MethodDesc stub = _compilation.PInvokeILProvider.GetCalliStub( + methodSignature, + ((MetadataType)HandleToObject(callSiteSig->scope).OwningMethod.OwningType).Module); Debug.Assert(!IsPInvokeStubRequired(stub)); #endif @@ -1825,7 +1827,9 @@ private bool convertPInvokeCalliToCall(ref CORINFO_RESOLVED_TOKEN pResolvedToken if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) == 0) return false; - MethodDesc stub = _compilation.PInvokeILProvider.GetCalliStub(signature); + MethodDesc stub = _compilation.PInvokeILProvider.GetCalliStub( + signature, + ((MetadataType)methodIL.OwningMethod.OwningType).Module); if (!mustConvert && !IsPInvokeStubRequired(stub)) return false; diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index e41efa704c72fd..b68d7bcbcb8f78 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -1775,6 +1775,42 @@ BOOL Module::IsRuntimeWrapExceptions() return !!(m_dwPersistedFlags & WRAP_EXCEPTIONS); } +BOOL Module::IsRuntimeMarshallingEnabled() +{ + CONTRACTL + { + THROWS; + if (IsRuntimeMarshallingEnabledCached()) GC_NOTRIGGER; else GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END + + if (IsRuntimeMarshallingEnabledCached()) + { + return !!(m_dwPersistedFlags & RUNTIME_MARSHALLING_ENABLED); + } + + HRESULT hr; + + IMDInternalImport *mdImport = GetAssembly()->GetManifestImport(); + + mdToken token; + if (SUCCEEDED(hr = mdImport->GetAssemblyFromScope(&token))) + { + const BYTE *pVal; + ULONG cbVal; + + hr = mdImport->GetCustomAttributeByName(token, + g_DisableRuntimeMarshallingAttribute, + (const void**)&pVal, &cbVal); + } + + FastInterlockOr(&m_dwPersistedFlags, RUNTIME_MARSHALLING_ENABLED_IS_CACHED | + (hr == S_OK ? 0 : RUNTIME_MARSHALLING_ENABLED)); + + return hr != S_OK; +} + BOOL Module::IsPreV4Assembly() { CONTRACTL diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index e7dd5629a2c84b..9b92b26defc268 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -834,6 +834,11 @@ class Module // Low level system assembly. Used by preferred zap module computation. LOW_LEVEL_SYSTEM_ASSEMBLY_BY_NAME = 0x00004000, + + //If setting has been cached + RUNTIME_MARSHALLING_ENABLED_IS_CACHED = 0x00008000, + //If runtime marshalling is enabled for this assembly + RUNTIME_MARSHALLING_ENABLED = 0x00010000, }; Volatile m_dwTransientFlags; @@ -2071,6 +2076,18 @@ class Module //----------------------------------------------------------------------------------------- BOOL IsRuntimeWrapExceptions(); + //----------------------------------------------------------------------------------------- + // If true, the built-in runtime-generated marshalling subsystem will be used for + // P/Invokes, function pointer invocations, and delegates defined in this module + //----------------------------------------------------------------------------------------- + BOOL IsRuntimeMarshallingEnabled(); + + BOOL IsRuntimeMarshallingEnabledCached() + { + LIMITED_METHOD_CONTRACT; + return (m_dwPersistedFlags & RUNTIME_MARSHALLING_ENABLED_IS_CACHED); + } + BOOL HasDefaultDllImportSearchPathsAttribute(); BOOL IsDefaultDllImportSearchPathsAttributeCached() diff --git a/src/coreclr/vm/class.h b/src/coreclr/vm/class.h index 0ccacd36a9f914..9aa6c6994e726e 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -363,19 +363,21 @@ class EEClassLayoutInfo private: enum { // TRUE if the GC layout of the class is bit-for-bit identical - // to its unmanaged counterpart (i.e. no internal reference fields, - // no ansi-unicode char conversions required, etc.) Used to - // optimize marshaling. - e_BLITTABLE = 0x01, - // Post V1.0 addition: Is this type also sequential in managed memory? - e_MANAGED_SEQUENTIAL = 0x02, + // to its unmanaged counterpart with the runtime marshalling system + // (i.e. no internal reference fields, no ansi-unicode char conversions required, etc.) + // Used to optimize marshaling. + e_BLITTABLE = 0x01, + // Is this type also sequential in managed memory? + e_MANAGED_SEQUENTIAL = 0x02, // When a sequential/explicit type has no fields, it is conceptually // zero-sized, but actually is 1 byte in length. This holds onto this // fact and allows us to revert the 1 byte of padding when another // explicit type inherits from this type. - e_ZERO_SIZED = 0x04, + e_ZERO_SIZED = 0x04, // The size of the struct is explicitly specified in the meta-data. - e_HAS_EXPLICIT_SIZE = 0x08 + e_HAS_EXPLICIT_SIZE = 0x08, + // The type recursively has a field that is LayoutKind.Auto and not an enum. + e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT = 0x10 }; BYTE m_bFlags; @@ -419,6 +421,12 @@ class EEClassLayoutInfo return (m_bFlags & e_HAS_EXPLICIT_SIZE) == e_HAS_EXPLICIT_SIZE; } + BOOL HasAutoLayoutField() const + { + LIMITED_METHOD_CONTRACT; + return (m_bFlags & e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT) == e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT; + } + BYTE GetPackingSize() const { LIMITED_METHOD_CONTRACT; @@ -453,6 +461,13 @@ class EEClassLayoutInfo m_bFlags = hasExplicitSize ? (m_bFlags | e_HAS_EXPLICIT_SIZE) : (m_bFlags & ~e_HAS_EXPLICIT_SIZE); } + + void SetHasAutoLayoutField(BOOL hasAutoLayoutField) + { + LIMITED_METHOD_CONTRACT; + m_bFlags = hasAutoLayoutField ? (m_bFlags | e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT) + : (m_bFlags & ~e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT); + } }; // @@ -1395,6 +1410,8 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! BOOL HasExplicitSize(); + BOOL IsAutoLayoutOrHasAutoLayoutField(); + static void GetBestFitMapping(MethodTable * pMT, BOOL *pfBestFitMapping, BOOL *pfThrowOnUnmappableChar); /* @@ -2082,6 +2099,13 @@ inline BOOL EEClass::HasExplicitSize() return HasLayout() && GetLayoutInfo()->HasExplicitSize(); } +inline BOOL EEClass::IsAutoLayoutOrHasAutoLayoutField() +{ + LIMITED_METHOD_CONTRACT; + // If this type is not auto + return !HasLayout() || GetLayoutInfo()->HasAutoLayoutField(); +} + //========================================================================== // These routines manage the prestub (a bootstrapping stub that all // FunctionDesc's are initialized with.) diff --git a/src/coreclr/vm/classlayoutinfo.cpp b/src/coreclr/vm/classlayoutinfo.cpp index 90315764cfbc5d..b95c6d4108ed45 100644 --- a/src/coreclr/vm/classlayoutinfo.cpp +++ b/src/coreclr/vm/classlayoutinfo.cpp @@ -226,23 +226,18 @@ namespace *pLargestAlignmentRequirementOut = LargestAlignmentRequirement; } - //======================================================================= - // This function returns TRUE if the provided corElemType disqualifies - // the structure from being a managed-sequential structure. - // The fsig parameter is used when the corElemType doesn't contain enough information - // to successfully determine if this field disqualifies the type from being - // managed-sequential. - // This function also fills in the pManagedPlacementInfo structure for this field. - //======================================================================= - BOOL CheckIfDisqualifiedFromManagedSequential(CorElementType corElemType, MetaSig& fsig, RawFieldPlacementInfo* pManagedPlacementInfo) + RawFieldPlacementInfo GetFieldPlacementInfo(CorElementType corElemType, TypeHandle pNestedType) { - pManagedPlacementInfo->m_alignment = TARGET_POINTER_SIZE; - pManagedPlacementInfo->m_size = TARGET_POINTER_SIZE; + RawFieldPlacementInfo placementInfo; + // Initialize offset to a dummy value as we set it to the correct value later. + placementInfo.m_offset = (UINT32)-1; + placementInfo.m_size = TARGET_POINTER_SIZE; + placementInfo.m_alignment = TARGET_POINTER_SIZE; // This type may qualify for ManagedSequential. Collect managed size and alignment info. if (CorTypeInfo::IsPrimitiveType(corElemType)) { // Safe cast - no primitive type is larger than 4gb! - pManagedPlacementInfo->m_size = ((UINT32)CorTypeInfo::Size(corElemType)); + placementInfo.m_size = ((UINT32)CorTypeInfo::Size(corElemType)); #if defined(TARGET_X86) && defined(UNIX_X86_ABI) switch (corElemType) { @@ -251,67 +246,87 @@ namespace case ELEMENT_TYPE_U8: case ELEMENT_TYPE_R8: { - pManagedPlacementInfo->m_alignment = 4; + placementInfo.m_alignment = 4; break; } default: { - pManagedPlacementInfo->m_alignment = pManagedPlacementInfo->m_size; + placementInfo.m_alignment = placementInfo.m_size; break; } } #else // TARGET_X86 && UNIX_X86_ABI - pManagedPlacementInfo->m_alignment = pManagedPlacementInfo->m_size; + placementInfo.m_alignment = placementInfo.m_size; #endif - return FALSE; } else if (corElemType == ELEMENT_TYPE_PTR || corElemType == ELEMENT_TYPE_FNPTR) { - pManagedPlacementInfo->m_size = TARGET_POINTER_SIZE; - pManagedPlacementInfo->m_alignment = TARGET_POINTER_SIZE; - - return FALSE; + placementInfo.m_size = TARGET_POINTER_SIZE; + placementInfo.m_alignment = TARGET_POINTER_SIZE; } else if (corElemType == ELEMENT_TYPE_VALUETYPE) { - TypeHandle pNestedType = fsig.GetLastTypeHandleThrowing(ClassLoader::LoadTypes, - CLASS_LOAD_APPROXPARENTS, - TRUE); + _ASSERTE(!pNestedType.IsNull()); - pManagedPlacementInfo->m_size = (pNestedType.GetMethodTable()->GetNumInstanceFieldBytes()); + placementInfo.m_size = (pNestedType.GetMethodTable()->GetNumInstanceFieldBytes()); #if !defined(TARGET_64BIT) && (DATA_ALIGNMENT > 4) - if (pManagedPlacementInfo->m_size >= DATA_ALIGNMENT) + if (placementInfo.m_size >= DATA_ALIGNMENT) { - pManagedPlacementInfo->m_alignment = DATA_ALIGNMENT; + placementInfo.m_alignment = DATA_ALIGNMENT; } else #elif defined(FEATURE_64BIT_ALIGNMENT) if (pNestedType.RequiresAlign8()) { - pManagedPlacementInfo->m_alignment = 8; + placementInfo.m_alignment = 8; } else #endif // FEATURE_64BIT_ALIGNMENT if (pNestedType.GetMethodTable()->ContainsPointers()) { // this field type has GC pointers in it, which need to be pointer-size aligned - // so do this if it has not been done already - pManagedPlacementInfo->m_alignment = TARGET_POINTER_SIZE; + placementInfo.m_alignment = TARGET_POINTER_SIZE; } else { - pManagedPlacementInfo->m_alignment = pNestedType.GetMethodTable()->GetFieldAlignmentRequirement(); + placementInfo.m_alignment = pNestedType.GetMethodTable()->GetFieldAlignmentRequirement(); } - // Types that have GC Pointer fields (objects or byrefs) are disqualified from ManagedSequential layout. - return pNestedType.GetMethodTable()->ContainsPointers() != FALSE; } // No other type permitted for ManagedSequential. + return placementInfo; + } + + BOOL TypeHasGCPointers(CorElementType corElemType, TypeHandle pNestedType) + { + if (CorTypeInfo::IsPrimitiveType(corElemType) || corElemType == ELEMENT_TYPE_PTR || corElemType == ELEMENT_TYPE_FNPTR) + { + return FALSE; + } + if (corElemType == ELEMENT_TYPE_VALUETYPE) + { + _ASSERTE(!pNestedType.IsNull()); + return pNestedType.GetMethodTable()->ContainsPointers() != FALSE; + } return TRUE; } + BOOL TypeHasAutoLayoutField(CorElementType corElemType, TypeHandle pNestedType) + { + if (CorTypeInfo::IsPrimitiveType(corElemType) || corElemType == ELEMENT_TYPE_PTR || corElemType == ELEMENT_TYPE_FNPTR) + { + return FALSE; + } + if (corElemType == ELEMENT_TYPE_VALUETYPE) + { + _ASSERTE(!pNestedType.IsNull()); + return pNestedType.IsEnum() || pNestedType.GetMethodTable()->IsAutoLayoutOrHasAutoLayoutField(); + } + return FALSE; + } + #ifdef UNIX_AMD64_ABI void SystemVAmd64CheckForPassNativeStructInRegister(MethodTable* pMT, EEClassNativeLayoutInfo* pNativeLayoutInfo) { @@ -437,6 +452,7 @@ namespace ParseNativeTypeFlags nativeTypeFlags, const SigTypeContext* pTypeContext, BOOL* fDisqualifyFromManagedSequential, + BOOL* fHasAutoLayoutField, LayoutRawFieldInfo* pFieldInfoArrayOut, BOOL* pIsBlittableOut, ULONG* cInstanceFields @@ -505,7 +521,16 @@ namespace #endif MetaSig fsig(pCOMSignature, cbCOMSignature, pModule, pTypeContext, MetaSig::sigField); CorElementType corElemType = fsig.NextArgNormalized(); - *fDisqualifyFromManagedSequential |= CheckIfDisqualifiedFromManagedSequential(corElemType, fsig, &pFieldInfoArrayOut->m_placement); + TypeHandle typeHandleMaybe; + if (corElemType == ELEMENT_TYPE_VALUETYPE) // Only look up the next element in the signature if it is a value type to avoid causing recursive type loads in valid scenarios. + { + typeHandleMaybe = fsig.GetLastTypeHandleThrowing(ClassLoader::LoadTypes, + CLASS_LOAD_APPROXPARENTS, + TRUE); + } + pFieldInfoArrayOut->m_placement = GetFieldPlacementInfo(corElemType, typeHandleMaybe); + *fDisqualifyFromManagedSequential |= TypeHasGCPointers(corElemType, typeHandleMaybe); + *fHasAutoLayoutField |= TypeHasAutoLayoutField(corElemType, typeHandleMaybe); if (!IsFieldBlittable(pModule, fd, fsig.GetArgProps(), pTypeContext, nativeTypeFlags)) *pIsBlittableOut = FALSE; @@ -598,6 +623,7 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( // Running tote - if anything in this type disqualifies it from being ManagedSequential, somebody will set this to TRUE by the the time // function exits. BOOL fDisqualifyFromManagedSequential; + BOOL hasAutoLayoutField = FALSE; // Check if this type might be ManagedSequential. Only valuetypes marked Sequential can be // ManagedSequential. Other issues checked below might also disqualify the type. @@ -612,6 +638,11 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( fDisqualifyFromManagedSequential = TRUE; } + if (pParentMT && !pParentMT->IsValueTypeClass() && pParentMT->IsAutoLayoutOrHasAutoLayoutField()) + { + hasAutoLayoutField = TRUE; + } + BOOL fHasNonTrivialParent = pParentMT && !pParentMT->IsObjectClass() && @@ -659,6 +690,7 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( nativeTypeFlags, pTypeContext, &fDisqualifyFromManagedSequential, + &hasAutoLayoutField, pInfoArrayOut, &isBlittable, &cInstanceFields @@ -671,6 +703,8 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( isBlittable = isBlittable && (fHasNonTrivialParent ? pParentMT->IsBlittable() : TRUE); pEEClassLayoutInfoOut->SetIsBlittable(isBlittable); + pEEClassLayoutInfoOut->SetHasAutoLayoutField(hasAutoLayoutField); + S_UINT32 cbSortArraySize = S_UINT32(cTotalFields) * S_UINT32(sizeof(LayoutRawFieldInfo*)); if (cbSortArraySize.IsOverflow()) { diff --git a/src/coreclr/vm/classnames.h b/src/coreclr/vm/classnames.h index 94e9b1025c7a4b..7d71ce2c7d891c 100644 --- a/src/coreclr/vm/classnames.h +++ b/src/coreclr/vm/classnames.h @@ -113,4 +113,6 @@ #define g_CriticalFinalizerObjectName "CriticalFinalizerObject" +#define g_DisableRuntimeMarshallingAttribute "System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute" + #endif //!__CLASSNAMES_H__ diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 6c4ce7608660ee..3491d7c6262f72 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -107,6 +107,7 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD) m_tkMethodDef = pMD->GetMemberDef(); SigTypeContext::InitTypeContext(pMD, &m_typeContext); + m_pMetadataModule = pMD->GetModule(); m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation. INDEBUG(InitDebugNames()); @@ -133,11 +134,13 @@ StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule) { m_tkMethodDef = pMD->GetMemberDef(); SigTypeContext::InitTypeContext(pMD, &m_typeContext); + m_pMetadataModule = pMD->GetModule(); m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation. } else { m_tkMethodDef = mdMethodDefNil; + m_pMetadataModule = m_pModule; m_pLoaderModule = m_pModule; } @@ -166,7 +169,8 @@ StubSigDesc::StubSigDesc(MethodTable* pMT, const Signature& sig, Module* pModule if (pMT != NULL) { SigTypeContext::InitTypeContext(pMT, &m_typeContext); - m_pLoaderModule = pMT->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation. + m_pMetadataModule = pMT->GetModule(); + m_pLoaderModule = pMT->GetLoaderModule(); } else { @@ -193,6 +197,7 @@ StubSigDesc::StubSigDesc(const Signature& sig, Module* pModule) m_sig = sig; m_pModule = pModule; m_tkMethodDef = mdMethodDefNil; + m_pMetadataModule = m_pModule; m_pLoaderModule = m_pModule; INDEBUG(InitDebugNames()); @@ -3287,6 +3292,15 @@ BOOL NDirect::MarshalingRequired( } CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray); + // We enable the runtime marshalling system whenever it is enabled on the module as a whole + // or when the call is a COM interop call. COM interop calls are already using a significant portion of the runtime + // marshalling system just to function at all, so we aren't going to disable the parameter marshalling; + // we'd rather have developers use the feature flag to diable the whole COM interop subsystem at once. + bool runtimeMarshallingEnabled = pModule->IsRuntimeMarshallingEnabled(); +#ifdef FEATURE_COMINTEROP + runtimeMarshallingEnabled |= pMD && pMD->IsComPlusCall(); +#endif + for (ULONG i = 0; i < numArgs; i++) { SigPointer arg = ptr; @@ -3300,7 +3314,7 @@ BOOL NDirect::MarshalingRequired( IfFailThrow(arg.GetElemType(NULL)); // skip ELEMENT_TYPE_PTR IfFailThrow(arg.PeekElemType(&type)); - if (type == ELEMENT_TYPE_VALUETYPE) + if (runtimeMarshallingEnabled && type == ELEMENT_TYPE_VALUETYPE) { if ((arg.HasCustomModifier(pModule, "Microsoft.VisualC.NeedsCopyConstructorModifier", @@ -3331,11 +3345,21 @@ BOOL NDirect::MarshalingRequired( { TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext); - // JIT can handle internal blittable value types - if (!hndArgType.IsBlittable() && !hndArgType.IsEnum()) + // When the runtime runtime marshalling system is disabled, we don't support + // any types that contain gc pointers, but all "unmanaged" types are treated as blittable + // as long as they aren't auto-layout and don't have any auto-layout fields. + if (!runtimeMarshallingEnabled && + (hndArgType.GetMethodTable()->ContainsPointers() + || hndArgType.GetMethodTable()->IsAutoLayoutOrHasAutoLayoutField())) { return TRUE; } + else if (runtimeMarshallingEnabled && !hndArgType.IsBlittable() && !hndArgType.IsEnum()) + { + // When the runtime runtime marshalling system is enabled, we do special handling + // for any types that aren't blittable or enums. + return TRUE; + } if (i > 0) { @@ -3348,10 +3372,15 @@ BOOL NDirect::MarshalingRequired( case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: { + // When runtime marshalling is enabled: // Bool requires marshaling // Char may require marshaling (MARSHAL_TYPE_ANSICHAR) - return TRUE; + if (runtimeMarshallingEnabled) + { + return TRUE; + } } + FALLTHROUGH; default: { @@ -3376,7 +3405,10 @@ BOOL NDirect::MarshalingRequired( // check for explicit MarshalAs NativeTypeParamInfo paramInfo; - if (pParamTokenArray[i] != mdParamDefNil) + // We only check the MarshalAs info when the runtime marshalling system is enabled. + // We ignore MarshalAs when the system is disabled, so no reason to disqualify from inlining + // when it is present. + if (runtimeMarshallingEnabled && pParamTokenArray[i] != mdParamDefNil) { if (!ParseNativeTypeInfo(pParamTokenArray[i], pMDImport, ¶mInfo) || paramInfo.m_NativeType != NATIVE_TYPE_DEFAULT) @@ -3594,6 +3626,7 @@ static void CreateNDirectStubWorker(StubState* pss, CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName)); #endif // _DEBUG + bool runtimeMarshallingEnabled = SF_IsCOMStub(dwStubFlags) || pSigDesc->m_pMetadataModule->IsRuntimeMarshallingEnabled(); if (SF_IsCOMStub(dwStubFlags)) { _ASSERTE(0 == nlType); @@ -3615,26 +3648,48 @@ static void CreateNDirectStubWorker(StubState* pss, &pSigDesc->m_typeContext); if (SF_IsVarArgStub(dwStubFlags)) + { + if (!runtimeMarshallingEnabled) + { + COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_DISABLEDMARSHAL_VARARGS); + } msig.SetTreatAsVarArg(); + } bool fThisCall = (unmgdCallConv == CorInfoCallConvExtension::Thiscall); - pss->SetLastError(nlFlags & nlfLastError); + if (nlFlags & nlfLastError) + { + if (!runtimeMarshallingEnabled) + { + COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_DISABLEDMARSHAL_SETLASTERROR); + } + pss->SetLastError(TRUE); + } // This has been in the product since forward P/Invoke via delegates was // introduced. It's wrong, but please keep it for backward compatibility. - if (SF_IsDelegateStub(dwStubFlags)) + if (runtimeMarshallingEnabled && SF_IsDelegateStub(dwStubFlags)) pss->SetLastError(TRUE); pss->BeginEmit(dwStubFlags); if (-1 != iLCIDArg) { - // The code to handle the LCID will call MarshalLCID before calling MarshalArgument + if (!runtimeMarshallingEnabled) + { + COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_DISABLEDMARSHAL_LCID); + } + // The code to handle the LCID will call MarshalLCID before calling MarshalArgument // on the argument the LCID should go after. So we just bump up the index here. iLCIDArg++; } + if (!runtimeMarshallingEnabled && SF_IsHRESULTSwapping(dwStubFlags)) + { + COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_DISABLEDMARSHAL_PRESERVESIG); + } + int numArgs = msig.NumFixedArgs(); // thiscall must have at least one parameter (the "this") diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h index f731823ff4a0b0..8b472e3a0aafb6 100644 --- a/src/coreclr/vm/dllimport.h +++ b/src/coreclr/vm/dllimport.h @@ -23,7 +23,15 @@ struct StubSigDesc MethodDesc *m_pMD; MethodTable *m_pMT; Signature m_sig; + // Module to use for signature reading. Module *m_pModule; + // Module that owns any metadata that influences interop behavior. + // This is usually the same as m_pModule, but can differ with vararg + // P/Invokes, where the calling assembly's module is assigned to m_pModule + // since the specific caller signature is defined in that assembly, not the + // assembly that defined the P/Invoke. + Module *m_pMetadataModule; + // Used for ILStubCache selection and MethodTable creation. Module *m_pLoaderModule; mdMethodDef m_tkMethodDef; SigTypeContext m_typeContext; diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 86a033a0593a29..76c552a3042849 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -1517,6 +1517,8 @@ class MethodTable inline BOOL HasExplicitSize(); + inline BOOL IsAutoLayoutOrHasAutoLayoutField(); + UINT32 GetNativeSize(); DWORD GetBaseSize() diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index d14a73532b6e12..72b628dae9545a 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -947,6 +947,13 @@ inline BOOL MethodTable::HasExplicitSize() return GetClass()->HasExplicitSize(); } +//========================================================================================== +inline BOOL MethodTable::IsAutoLayoutOrHasAutoLayoutField() +{ + LIMITED_METHOD_CONTRACT; + return GetClass()->IsAutoLayoutOrHasAutoLayoutField(); +} + //========================================================================================== inline DWORD MethodTable::GetPerInstInfoSize() { diff --git a/src/coreclr/vm/mlinfo.cpp b/src/coreclr/vm/mlinfo.cpp index 1dcc726213640f..b8159f5aeed136 100644 --- a/src/coreclr/vm/mlinfo.cpp +++ b/src/coreclr/vm/mlinfo.cpp @@ -1056,6 +1056,78 @@ OleColorMarshalingInfo *EEMarshalingData::GetOleColorMarshalingInfo() } #endif // FEATURE_COMINTEROP +namespace +{ + MarshalInfo::MarshalType GetDisabledMarshallerType( + Module* pModule, + SigPointer sig, + const SigTypeContext * pTypeContext, + MethodTable** pMTOut, + UINT* errorResIDOut) + { + switch (sig.PeekElemTypeNormalized(pModule, pTypeContext)) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_U1: + return MarshalInfo::MARSHAL_TYPE_GENERIC_U1; + case ELEMENT_TYPE_I1: + return MarshalInfo::MARSHAL_TYPE_GENERIC_1; + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_U2: + return MarshalInfo::MARSHAL_TYPE_GENERIC_U2; + case ELEMENT_TYPE_I2: + return MarshalInfo::MARSHAL_TYPE_GENERIC_2; + case ELEMENT_TYPE_U4: + return MarshalInfo::MARSHAL_TYPE_GENERIC_U4; + case ELEMENT_TYPE_I4: + return MarshalInfo::MARSHAL_TYPE_GENERIC_4; + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_I8: + return MarshalInfo::MARSHAL_TYPE_GENERIC_8; +#ifdef TARGET_64BIT + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_FNPTR: + case ELEMENT_TYPE_I: + return MarshalInfo::MARSHAL_TYPE_GENERIC_8; +#else + case ELEMENT_TYPE_U: + return MarshalInfo::MARSHAL_TYPE_GENERIC_U4; + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_FNPTR: + case ELEMENT_TYPE_I: + return MarshalInfo::MARSHAL_TYPE_GENERIC_4; +#endif + case ELEMENT_TYPE_R4: + return MarshalInfo::MARSHAL_TYPE_FLOAT; + case ELEMENT_TYPE_R8: + return MarshalInfo::MARSHAL_TYPE_DOUBLE; + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_VALUETYPE: + { + TypeHandle sigTH = sig.GetTypeHandleThrowing(pModule, pTypeContext); + MethodTable* pMT = sigTH.GetMethodTable(); + + if (!pMT->IsValueType() || pMT->ContainsPointers()) + { + *errorResIDOut = IDS_EE_BADMARSHAL_MARSHAL_DISABLED; + return MarshalInfo::MARSHAL_TYPE_UNKNOWN; + } + if (pMT->IsAutoLayoutOrHasAutoLayoutField()) + { + *errorResIDOut = IDS_EE_BADMARSHAL_AUTOLAYOUT; + return MarshalInfo::MARSHAL_TYPE_UNKNOWN; + } + *pMTOut = pMT; + return MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS; + } + default: + *errorResIDOut = IDS_EE_BADMARSHAL_MARSHAL_DISABLED; + return MarshalInfo::MARSHAL_TYPE_UNKNOWN; + } + } +} + //========================================================================== // Constructs MarshalInfo. //========================================================================== @@ -1163,7 +1235,30 @@ MarshalInfo::MarshalInfo(Module* pModule, m_byref = TRUE; #endif + // For COM IL-stub scenarios, we do not support disabling the runtime marshalling support. + // The runtime-integrated COM support uses a significant portion of the marshalling infrastructure as well as + // quite a bit of its own custom marshalling infrastructure to function in basically any aspect. + // As a result, disabling marshalling in COM scenarios isn't useful. Instead, we recommend that people set the + // feature switch to false to disable the runtime COM support if they want it disabled. + // For field marshalling scenarios, we also don't disable runtime marshalling. If we're already in a field + // marshalling scenario, we've already decided that the context for the owning type is using runtime marshalling, + // so the fields of the struct should also use runtime marshalling. + const bool useRuntimeMarshalling = ms != MARSHAL_SCENARIO_NDIRECT || pModule->IsRuntimeMarshallingEnabled(); + if (!useRuntimeMarshalling) + { + m_in = TRUE; + m_out = FALSE; + m_byref = FALSE; + m_type = GetDisabledMarshallerType( + pModule, + sig, + pTypeContext, + &m_pMT, + &m_resID); + m_args.m_pMT = m_pMT; + return; + } // Retrieve the native type for the current parameter. if (!ParseNativeTypeInfo(token, pModule->GetMDImport(), &ParamInfo)) @@ -1322,6 +1417,7 @@ MarshalInfo::MarshalInfo(Module* pModule, switch (mtype) { case ELEMENT_TYPE_BOOLEAN: + switch (nativeType) { case NATIVE_TYPE_BOOLEAN: @@ -1482,10 +1578,6 @@ MarshalInfo::MarshalInfo(Module* pModule, break; case ELEMENT_TYPE_I: - // Technically the "native int" and "native uint" types aren't supported in the WinRT scenario, - // but we need to not block ourselves from using them to enable accurate managed->native marshalling of - // projected types such as NotifyCollectionChangedEventArgs and NotifyPropertyChangedEventArgs. - if (!(nativeType == NATIVE_TYPE_INT || nativeType == NATIVE_TYPE_UINT || nativeType == NATIVE_TYPE_DEFAULT)) { m_resID = IDS_EE_BADMARSHAL_I; @@ -1499,7 +1591,6 @@ MarshalInfo::MarshalInfo(Module* pModule, break; case ELEMENT_TYPE_U: - if (!(nativeType == NATIVE_TYPE_UINT || nativeType == NATIVE_TYPE_INT || nativeType == NATIVE_TYPE_DEFAULT)) { m_resID = IDS_EE_BADMARSHAL_I; @@ -1564,6 +1655,21 @@ MarshalInfo::MarshalInfo(Module* pModule, { TypeHandle sigTH = sig.GetTypeHandleThrowing(pModule, pTypeContext); + if (sigTH.GetMethodTable()->IsValueType()) + { + // For value types, we need to handle the "value type marshalled as a COM interface" + // case here for back-compat. + // Otherwise, we can go to the value-type case. +#ifdef FEATURE_COMINTEROP + if (nativeType != NATIVE_TYPE_INTF) + { + goto lValueClass; + } +#else + goto lValueClass; +#endif + } + // Disallow marshaling generic types. if (sigTH.HasInstantiation()) { @@ -1968,14 +2074,13 @@ MarshalInfo::MarshalInfo(Module* pModule, } } - else if (m_pMT->IsArray()) { _ASSERTE(!"This invalid signature should never be hit!"); IfFailGoto(E_FAIL, lFail); } #endif // FEATURE_COMINTEROP - else if (!m_pMT->IsValueType()) + else { if (!(nativeType == NATIVE_TYPE_INTF || nativeType == NATIVE_TYPE_DEFAULT)) { @@ -1990,12 +2095,6 @@ MarshalInfo::MarshalInfo(Module* pModule, IfFailGoto(E_FAIL, lFail); #endif // FEATURE_COMINTEROP } - - else - { - _ASSERTE(m_pMT->IsValueType()); - goto lValueClass; - } } break; } @@ -2166,8 +2265,7 @@ MarshalInfo::MarshalInfo(Module* pModule, || m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR64T)) || m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR128T)) || m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR256T)) - // Crossgen scenarios block Vector from even being loaded - || m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTORT)) + || m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTORT)) ))) { m_resID = IDS_EE_BADMARSHAL_GENERICS_RESTRICTION; @@ -2902,6 +3000,15 @@ UINT16 MarshalInfo::GetNativeSize(MarshalType mtype) if (nativeSize == VARIABLESIZE) { _ASSERTE(IsValueClass(mtype)); + // For blittable types, use the GetNumInstanceFieldBytes method. + // When we generate IL stubs when marshalling is disabled, + // we reuse the blittable value class marshalling mechanism. + // In that scenario, only GetNumInstanceFieldBytes will return the correct value. + // GetNativeSize will return the size for when runtime marshalling is enabled. + if (mtype == MARSHAL_TYPE_BLITTABLEVALUECLASS) + { + return (UINT16) m_pMT->GetNumInstanceFieldBytes(); + } return (UINT16) m_pMT->GetNativeSize(); } diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index f829212a8ef6ed..2249756c3358aa 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -2669,7 +2669,7 @@ void ILStubLinker::TransformArgForJIT(LocalDesc *pLoc) // JIT will handle structures if (pLoc->InternalToken.IsValueType()) { - _ASSERTE(pLoc->InternalToken.IsBlittable()); + _ASSERTE(pLoc->InternalToken.IsNativeValueType() || !pLoc->InternalToken.GetMethodTable()->ContainsPointers()); break; } FALLTHROUGH; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 24b049b1df2ff1..e9f81cc5409f49 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -711,6 +711,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisableRuntimeMarshallingAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisableRuntimeMarshallingAttribute.cs new file mode 100644 index 00000000000000..6ed3018d2479ee --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisableRuntimeMarshallingAttribute.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + /// + /// Disables the built-in runtime managed/unmanaged marshalling subsystem for + /// P/Invokes, Delegate types, and unmanaged function pointer invocations. + /// + /// + /// The built-in marshalling subsystem has some behaviors that cannot be changed due to + /// backward-compatibility requirements. This attribute allows disabling the built-in + /// subsystem and instead uses the following rules for P/Invokes, Delegates, + /// and unmanaged function pointer invocations: + /// + /// - All value types that do not contain reference type fields recursively (unmanaged in C#) are blittable + /// - Value types that recursively have any fields that have [StructLayout(LayoutKind.Auto)] are disallowed from interop. + /// - All reference types are disallowed from usage in interop scenarios. + /// - SetLastError support in P/Invokes is disabled. + /// - varargs support is disabled. + /// - LCIDConversionAttribute support is disabled. + /// + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] + public sealed class DisableRuntimeMarshallingAttribute : Attribute + { + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 4036cc486b12a7..4e774b8cba433c 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -12998,6 +12998,11 @@ public sealed partial class DisablePrivateReflectionAttribute : System.Attribute { public DisablePrivateReflectionAttribute() { } } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] + public sealed class DisableRuntimeMarshallingAttribute : Attribute + { + public DisableRuntimeMarshallingAttribute() { } + } [System.AttributeUsageAttribute(System.AttributeTargets.All)] public partial class DiscardableAttribute : System.Attribute { diff --git a/src/mono/mono/arch/s390x/tramp.c b/src/mono/mono/arch/s390x/tramp.c index 376970c4ab60bf..451511dfc53e53 100644 --- a/src/mono/mono/arch/s390x/tramp.c +++ b/src/mono/mono/arch/s390x/tramp.c @@ -166,7 +166,7 @@ calculate_sizes (MonoMethodSignature *sig, size_data *sz, goto enum_retvalue; } gr++; - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (sig->ret->data.klass, &align); else size = mono_class_value_size (sig->ret->data.klass, &align); @@ -235,7 +235,7 @@ calculate_sizes (MonoMethodSignature *sig, size_data *sz, simpletype = sig->params [i]->data.klass->enum_basetype->type; goto enum_calc_size; } - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (sig->params [i]->data.klass, &align); else size = mono_class_value_size (sig->params [i]->data.klass, &align); @@ -423,7 +423,7 @@ emit_save_parameters (guint8 *p, MonoMethodSignature *sig, size_data *sz) simpletype = sig->params [i]->data.klass->enum_basetype->type; goto enum_calc_size; } - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (sig->params [i]->data.klass, &align); else size = mono_class_value_size (sig->params [i]->data.klass, &align); @@ -625,7 +625,7 @@ emit_call_and_store_retval (guint8 *p, MonoMethodSignature *sig, simpletype = sig->ret->data.klass->enum_basetype->type; goto enum_retvalue; } - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) retSize = mono_class_native_size (sig->ret->data.klass, &align); else retSize = mono_class_value_size (sig->ret->data.klass, &align); @@ -848,7 +848,7 @@ mono_arch_create_method_pointer (MonoMethod *method) /* area. If necessary save this hidden parameter for later */ /*----------------------------------------------------------*/ if (MONO_TYPE_ISSTRUCT(sig->ret)) { - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) retSize = mono_class_native_size (sig->ret->data.klass, &align); else retSize = mono_class_value_size (sig->ret->data.klass, &align); @@ -904,7 +904,10 @@ mono_arch_create_method_pointer (MonoMethod *method) if (klass->enumtype) continue; - size = mono_class_native_size (klass, &align); + if (sig->pinvoke && !sig->marshalling_disabled) + size = mono_class_native_size (sig->ret->data.klass, &align); + else + size = mono_class_value_size (sig->ret->data.klass, &align); cpos += align - 1; cpos &= ~(align - 1); vtbuf [i] = cpos; @@ -947,7 +950,7 @@ mono_arch_create_method_pointer (MonoMethod *method) simple_type = sig->params [i]->data.klass->enum_basetype->type; goto enum_savechk; } - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) parSize = mono_class_native_size (sig->params [i]->data.klass, &align); else parSize = mono_class_value_size (sig->params [i]->data.klass, &align); @@ -996,7 +999,7 @@ mono_arch_create_method_pointer (MonoMethod *method) /* fixme: alignment */ DEBUG (printf ("arg_pos %d --> ", arg_pos)); - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) arg_pos += mono_type_native_stack_size (sig->params [i], &align); else arg_pos += mono_type_stack_size (sig->params [i], &align); diff --git a/src/mono/mono/metadata/class-getters.h b/src/mono/mono/metadata/class-getters.h index 0dc97fca5807d4..7f88b78780142d 100644 --- a/src/mono/mono/metadata/class-getters.h +++ b/src/mono/mono/metadata/class-getters.h @@ -24,6 +24,7 @@ MONO_CLASS_GETTER(m_class_is_size_inited, gboolean, , MonoClass, size_inited) MONO_CLASS_GETTER(m_class_is_valuetype, gboolean, , MonoClass, valuetype) MONO_CLASS_GETTER(m_class_is_enumtype, gboolean, , MonoClass, enumtype) MONO_CLASS_GETTER(m_class_is_blittable, gboolean, , MonoClass, blittable) +MONO_CLASS_GETTER(m_class_any_field_has_auto_layout, gboolean, , MonoClass, any_field_has_auto_layout) MONO_CLASS_GETTER(m_class_is_unicode, gboolean, , MonoClass, unicode) MONO_CLASS_GETTER(m_class_was_typebuilder, gboolean, , MonoClass, wastypebuilder) MONO_CLASS_GETTER(m_class_is_array_special_interface, gboolean, , MonoClass, is_array_special_interface) diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index 57c156d52e5d64..a7af1931eefc17 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -1968,6 +1968,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ gboolean has_static_refs = FALSE; MonoClassField *field; gboolean blittable; + gboolean any_field_has_auto_layout; int instance_size = base_instance_size; int element_size = -1; int class_size, min_align; @@ -2039,12 +2040,17 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ gc_aware_layout = TRUE; } - /* Compute klass->blittable */ + /* Compute klass->blittable and klass->any_field_has_auto_layout */ blittable = TRUE; - if (klass->parent) + any_field_has_auto_layout = FALSE; + if (klass->parent) { blittable = klass->parent->blittable; - if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT && !(mono_is_corlib_image (klass->image) && !strcmp (klass->name_space, "System") && !strcmp (klass->name, "ValueType")) && top) + any_field_has_auto_layout = klass->parent->any_field_has_auto_layout; + } + if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT && !(mono_is_corlib_image (klass->image) && !strcmp (klass->name_space, "System") && !strcmp (klass->name, "ValueType")) && top) { blittable = FALSE; + any_field_has_auto_layout = TRUE; // If a type is auto-layout, treat it as having an auto-layout field in its layout. + } for (i = 0; i < top; i++) { field = &klass->fields [i]; @@ -2074,8 +2080,12 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ blittable = FALSE; } } - if (klass->enumtype) - blittable = klass->element_class->blittable; + if (!any_field_has_auto_layout && field->type->type == MONO_TYPE_VALUETYPE && m_class_any_field_has_auto_layout (mono_class_from_mono_type_internal (field->type))) + any_field_has_auto_layout = TRUE; + } + if (klass->enumtype) { + blittable = klass->element_class->blittable; + any_field_has_auto_layout = klass->element_class->any_field_has_auto_layout; } if (mono_class_has_failure (klass)) return; @@ -2302,6 +2312,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ klass->has_references = has_references; klass->packing_size = packing_size; klass->min_align = min_align; + klass->any_field_has_auto_layout = any_field_has_auto_layout; for (i = 0; i < top; ++i) { field = &klass->fields [i]; if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) diff --git a/src/mono/mono/metadata/class-private-definition.h b/src/mono/mono/metadata/class-private-definition.h index 4724df84b7f8cf..a1b8432d690ce3 100644 --- a/src/mono/mono/metadata/class-private-definition.h +++ b/src/mono/mono/metadata/class-private-definition.h @@ -80,6 +80,7 @@ struct _MonoClass { guint has_failure : 1; /* See mono_class_get_exception_data () for a MonoErrorBoxed with the details */ guint has_weak_fields : 1; /* class has weak reference fields */ guint has_dim_conflicts : 1; /* Class has conflicting default interface methods */ + guint any_field_has_auto_layout : 1; /* a field in this type's layout uses auto-layout */ MonoClass *parent; MonoClass *nested_in; diff --git a/src/mono/mono/metadata/cominterop.c b/src/mono/mono/metadata/cominterop.c index ddeeaccb735027..7084b5feb81fb7 100644 --- a/src/mono/mono/metadata/cominterop.c +++ b/src/mono/mono/metadata/cominterop.c @@ -1012,7 +1012,7 @@ cominterop_get_native_wrapper_adjusted (MonoMethod *method) } } - mono_marshal_emit_native_wrapper (m_class_get_image (method->klass), mb_native, sig_native, piinfo, mspecs, piinfo->addr, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS); + mono_marshal_emit_native_wrapper (m_class_get_image (method->klass), mb_native, sig_native, piinfo, mspecs, piinfo->addr, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS | EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED); res = mono_mb_create_method (mb_native, sig_native, sig_native->param_count + 16); @@ -2103,6 +2103,7 @@ cominterop_get_ccw_method (MonoClass *iface, MonoMethod *method, MonoError *erro cominterop_setup_marshal_context (&m, adjust_method); m.mb = mb; + m.runtime_marshalling_enabled = TRUE; mono_marshal_emit_managed_wrapper (mb, sig_adjusted, mspecs, &m, adjust_method, 0); mono_cominterop_lock (); wrapper_method = mono_mb_create_method (mb, m.csig, m.csig->param_count + 16); diff --git a/src/mono/mono/metadata/marshal-ilgen.c b/src/mono/mono/metadata/marshal-ilgen.c index b8a12826aac4bc..2290c438125c9d 100644 --- a/src/mono/mono/metadata/marshal-ilgen.c +++ b/src/mono/mono/metadata/marshal-ilgen.c @@ -1991,6 +1991,7 @@ emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSi gboolean func_param = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM) != 0; gboolean func_param_unboxed = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED) != 0; gboolean skip_gc_trans = (flags & EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS) != 0; + gboolean runtime_marshalling_enabled = (flags & EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED) != 0; EmitMarshalContext m; MonoMethodSignature *csig; MonoClass *klass; @@ -2001,6 +2002,7 @@ emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSi GCSafeTransitionBuilder gc_safe_transition_builder; memset (&m, 0, sizeof (m)); + m.runtime_marshalling_enabled = runtime_marshalling_enabled; m.mb = mb; m.sig = sig; m.piinfo = piinfo; @@ -2016,6 +2018,8 @@ emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSi } csig = mono_metadata_signature_dup_full (get_method_image (mb->method), sig); csig->pinvoke = 1; + if (!runtime_marshalling_enabled) + csig->marshalling_disabled = 1; m.csig = csig; m.image = image; @@ -2102,6 +2106,10 @@ emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSi csig->ret = int_type; } + // Check if SetLastError usage is valid early so we don't try to throw an exception after transitioning GC modes. + if (piinfo && (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) && !m.runtime_marshalling_enabled) + mono_mb_emit_exception_marshal_directive(mb, g_strdup("Setting SetLastError=true is not supported when runtime marshalling is disabled.")); + /* we first do all conversions */ tmp_locals = g_newa (int, sig->param_count); m.orig_conv_args = g_newa (int, sig->param_count + 1); @@ -6762,6 +6770,18 @@ mb_emit_exception_for_error_ilgen (MonoMethodBuilder *mb, const MonoError *error mono_mb_emit_exception_for_error (mb, (MonoError*)error); } +static void +emit_marshal_directive_exception_ilgen (EmitMarshalContext *m, int argnum, const char* msg) +{ + char* fullmsg = NULL; + if (argnum == 0) + fullmsg = g_strdup_printf("Error marshalling return value: %s", msg); + else + fullmsg = g_strdup_printf("Error marshalling parameter #%d: %s", argnum, msg); + + mono_mb_emit_exception_marshal_directive (m->mb, fullmsg); +} + static void emit_vtfixup_ftnptr_ilgen (MonoMethodBuilder *mb, MonoMethod *method, int param_count, guint16 type) { @@ -6850,6 +6870,7 @@ mono_marshal_ilgen_init (void) cb.mb_emit_exception = mb_emit_exception_ilgen; cb.mb_emit_exception_for_error = mb_emit_exception_for_error_ilgen; cb.mb_emit_byte = mb_emit_byte_ilgen; + cb.emit_marshal_directive_exception = emit_marshal_directive_exception_ilgen; #ifdef DISABLE_NONBLITTABLE mono_marshal_noilgen_init_blittable (&cb); #endif diff --git a/src/mono/mono/metadata/marshal-noilgen.c b/src/mono/mono/metadata/marshal-noilgen.c index 0cc81a3e5744b8..7a004458bbfb79 100644 --- a/src/mono/mono/metadata/marshal-noilgen.c +++ b/src/mono/mono/metadata/marshal-noilgen.c @@ -246,6 +246,11 @@ mb_emit_exception_noilgen (MonoMethodBuilder *mb, const char *exc_nspace, const { } +static void +emit_marshal_directive_exception_noilgen (EmitMarshalContext *m, int argnum, const char* msg) +{ +} + static void mb_emit_exception_for_error_noilgen (MonoMethodBuilder *mb, const MonoError *error) { @@ -404,6 +409,7 @@ mono_marshal_noilgen_init (void) cb.mb_set_dynamic = mb_set_dynamic_noilgen; cb.mb_emit_exception = mb_emit_exception_noilgen; cb.mb_emit_exception_for_error = mb_emit_exception_for_error_noilgen; + cb.emit_marshal_directive_exception = emit_marshal_directive_exception_noilgen; cb.mb_emit_byte = mb_emit_byte_noilgen; mono_install_marshal_callbacks (&cb); } diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index c5b96cec20e73b..c47345b11848bb 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -2985,6 +2985,51 @@ mono_pinvoke_is_unicode (MonoMethodPInvoke *piinfo) } } +static GENERATE_TRY_GET_CLASS_WITH_CACHE (disable_runtime_marshalling_attr, "System.Runtime.CompilerServices", "DisableRuntimeMarshallingAttribute") + +/* + * runtime_marshalling_enabled: + * + * Determine whenever M's assembly has the runtime marshalling subsystem enabled. + */ +static gboolean +runtime_marshalling_enabled (MonoImage *img) +{ + ERROR_DECL (error); + MonoAssembly *ass = img->assembly; + MonoCustomAttrInfo* attrs; + MonoClass *klass; + int i; + gboolean runtime_marshalling_enabled = TRUE; + + g_assert (ass); + if (ass->runtime_marshalling_enabled_inited) + return ass->runtime_marshalling_enabled; + + klass = mono_class_try_get_disable_runtime_marshalling_attr_class (); + attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, error); + mono_error_cleanup (error); /* FIXME don't swallow the error */ + if (attrs && klass) { + for (i = 0; i < attrs->num_attrs; ++i) { + MonoCustomAttrEntry *attr = &attrs->attrs [i]; + if (attr->ctor && attr->ctor->klass == klass) { + runtime_marshalling_enabled = FALSE; + break; + } + } + } + + if (attrs) + mono_custom_attrs_free (attrs); + + ass->runtime_marshalling_enabled = runtime_marshalling_enabled; + mono_memory_barrier (); + ass->runtime_marshalling_enabled_inited = TRUE; + + return ass->runtime_marshalling_enabled; +} + + MonoType* mono_marshal_boolean_conv_in_get_local_type (MonoMarshalSpec *spec, guint8 *ldc_op /*out*/) { @@ -3031,6 +3076,76 @@ mono_marshal_boolean_managed_conv_in_get_conv_arg_class (MonoMarshalSpec *spec, return conv_arg_class; } +static int +mono_emit_disabled_marshal (EmitMarshalContext *m, int argnum, MonoType *t, + MonoMarshalSpec *spec, int conv_arg, + MonoType **conv_arg_type, MarshalAction action) +{ + // Some phases like MARSHAL_ACTION_PUSH need to still emit "correct" IL for the IL stack to be valid. + // For those phases, we won't emit the exception throw. + // In all other phases we'll emit the exception throw. + gboolean emit_exception = action == MARSHAL_ACTION_CONV_IN || action == MARSHAL_ACTION_CONV_RESULT; + + if (m_type_is_byref(t)) { + if (emit_exception) + get_marshal_cb ()->emit_marshal_directive_exception (m, argnum, "Cannot marshal managed references when the runtime marshalling system is disabled."); + else + get_marshal_cb ()->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action); + return conv_arg; + } + + switch (t->type) { + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (t)) + break; + // fallthrough + case MONO_TYPE_VALUETYPE: { + MonoClass* c = mono_class_from_mono_type_internal (t); + if (m_class_has_references(c)) { + if (emit_exception) + get_marshal_cb ()->emit_marshal_directive_exception (m, argnum, "Cannot marshal managed types when the runtime marshalling system is disabled."); + else + get_marshal_cb ()->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action); + return conv_arg; + } + if (m_class_any_field_has_auto_layout(c)) { + if (emit_exception) + get_marshal_cb ()->emit_marshal_directive_exception (m, argnum, "Structures marked with [StructLayout(LayoutKind.Auto)] cannot be marshaled."); + else + get_marshal_cb ()->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action); + return conv_arg; + } + } + // fallthrough + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_PTR: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_FNPTR: + return get_marshal_cb ()->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action); + default: + break; + } + + if (emit_exception) + get_marshal_cb ()->emit_marshal_directive_exception (m, argnum, "Cannot marshal managed types when the runtime marshalling system is disabled."); + else + get_marshal_cb ()->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action); + return conv_arg; +} + int mono_emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t, MonoMarshalSpec *spec, int conv_arg, @@ -3039,6 +3154,9 @@ mono_emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t, /* Ensure that we have marshalling info for this param */ mono_marshal_load_type_info (mono_class_from_mono_type_internal (t)); + if (!m->runtime_marshalling_enabled) + return mono_emit_disabled_marshal (m, argnum, t, spec, conv_arg, conv_arg_type, action); + if (spec && spec->native == MONO_NATIVE_CUSTOM) return get_marshal_cb ()->emit_marshal_custom (m, argnum, t, spec, conv_arg, conv_arg_type, action); @@ -3480,6 +3598,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, MonoNativeWrapperFlags flags = aot ? EMIT_NATIVE_WRAPPER_AOT : (MonoNativeWrapperFlags)0; flags |= check_exceptions ? EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS : (MonoNativeWrapperFlags)0; flags |= skip_gc_trans ? EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS : (MonoNativeWrapperFlags)0; + flags |= runtime_marshalling_enabled (get_method_image (method)) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; mono_marshal_emit_native_wrapper (get_method_image (mb->method), mb, csig, piinfo, mspecs, piinfo->addr, flags); info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_PINVOKE); @@ -3521,6 +3640,7 @@ mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig GHashTable *cache; gboolean found; char *name; + MonoNativeWrapperFlags flags; key.sig = sig; key.pointer = func; @@ -3536,7 +3656,10 @@ mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE); mb->method->save_lmf = 1; - mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS); + flags = EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS; + flags |= runtime_marshalling_enabled (image) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; + + mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, flags); csig = mono_metadata_signature_dup_full (image, sig); csig->pinvoke = 0; @@ -3575,6 +3698,7 @@ mono_marshal_get_native_func_wrapper_aot (MonoClass *klass) MonoMethod *invoke = mono_get_delegate_invoke_internal (klass); MonoImage *image = get_method_image (invoke); int i; + MonoNativeWrapperFlags flags; // FIXME: include UnmanagedFunctionPointerAttribute info @@ -3599,7 +3723,9 @@ mono_marshal_get_native_func_wrapper_aot (MonoClass *klass) mb = mono_mb_new (invoke->klass, name, MONO_WRAPPER_MANAGED_TO_NATIVE); mb->method->save_lmf = 1; - mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, NULL, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS | EMIT_NATIVE_WRAPPER_FUNC_PARAM); + flags = EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS | EMIT_NATIVE_WRAPPER_FUNC_PARAM; + flags |= runtime_marshalling_enabled (image) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; + mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, NULL, flags); info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NATIVE_FUNC_AOT); info->d.managed_to_native.method = invoke; @@ -3683,6 +3809,7 @@ mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMeth MonoMarshalSpec **mspecs = g_new0 (MonoMarshalSpec *, 1 + sig->param_count); MonoNativeWrapperFlags flags = aot ? EMIT_NATIVE_WRAPPER_AOT : (MonoNativeWrapperFlags)0; flags |= EMIT_NATIVE_WRAPPER_FUNC_PARAM | EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED; + flags |= runtime_marshalling_enabled (image) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, /*func*/NULL, flags); g_free (mspecs); @@ -3745,19 +3872,70 @@ type_is_blittable (MonoType *type) } static gboolean -method_signature_is_blittable (MonoMethodSignature *sig) +check_all_types_in_method_signature (MonoMethodSignature *sig, gboolean (*validate) (MonoType *type)) { - if (!type_is_blittable (sig->ret)) + if (!validate (sig->ret)) return FALSE; for (int i = 0; i < sig->param_count; ++i) { MonoType *type = sig->params [i]; - if (!type_is_blittable (type)) + if (!validate (type)) return FALSE; } return TRUE; } +static gboolean +method_signature_is_blittable (MonoMethodSignature *sig) +{ + return check_all_types_in_method_signature (sig, &type_is_blittable); +} + +static gboolean +type_is_usable_when_marshalling_disabled (MonoType *type) +{ + switch (type->type) { + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (type)) + return FALSE; + // fallthrough + case MONO_TYPE_VALUETYPE: { + MonoClass* c = mono_class_from_mono_type_internal (type); + if (m_class_has_references(c) || (m_class_is_auto_layout(c) && !m_class_is_enumtype(c))) { + return FALSE; + } + } + // fallthrough + case MONO_TYPE_VOID: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_PTR: + case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_FNPTR: + return TRUE; + default: + return FALSE; + } +} + + +static gboolean +method_signature_is_usable_when_marshalling_disabled (MonoMethodSignature *sig) +{ + return check_all_types_in_method_signature (sig, &type_is_usable_when_marshalling_disabled); +} + /** * mono_marshal_get_managed_wrapper: * Generates IL code to call managed methods from unmanaged code @@ -3777,6 +3955,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, GHashTable *cache; int i; EmitMarshalContext m; + gboolean marshalling_enabled; g_assert (method != NULL); error_init (error); @@ -3798,22 +3977,28 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, if (G_UNLIKELY (!delegate_klass)) { /* creating a wrapper for a function pointer with UnmanagedCallersOnlyAttribute */ - if (mono_method_has_marshal_info (method)) { - mono_error_set_invalid_program (error, "method %s with UnmanadedCallersOnlyAttribute has marshal specs", mono_method_full_name (method, TRUE)); - return NULL; - } + marshalling_enabled = runtime_marshalling_enabled(get_method_image(method)); invoke = NULL; invoke_sig = mono_method_signature_internal (method); if (invoke_sig->hasthis) { - mono_error_set_invalid_program (error, "method %s with UnamanagedCallersOnlyAttribute is an instance method", mono_method_full_name (method, TRUE)); + mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute is an instance method", mono_method_full_name (method, TRUE)); return NULL; } if (method->is_generic || method->is_inflated || mono_class_is_ginst (method->klass)) { - mono_error_set_invalid_program (error, "method %s with UnamangedCallersOnlyAttribute is generic", mono_method_full_name (method, TRUE)); + mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute is generic", mono_method_full_name (method, TRUE)); return NULL; } - if (!method_signature_is_blittable (invoke_sig)) { - mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has non-blittable parameters or return type", mono_method_full_name (method, TRUE)); + if (marshalling_enabled) { + if (mono_method_has_marshal_info (method)) { + mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has marshal specs", mono_method_full_name (method, TRUE)); + return NULL; + } + if (!method_signature_is_blittable (invoke_sig)) { + mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has non-blittable parameters or return type", mono_method_full_name (method, TRUE)); + return NULL; + } + } else if (!method_signature_is_usable_when_marshalling_disabled(invoke_sig)) { + mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has types not usable when marshalling is disabled", mono_method_full_name (method, TRUE)); return NULL; } mspecs = g_new0 (MonoMarshalSpec*, invoke_sig->param_count + 1); @@ -3822,6 +4007,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, invoke_sig = mono_method_signature_internal (invoke); mspecs = g_new0 (MonoMarshalSpec*, invoke_sig->param_count + 1); mono_method_get_marshal_info (invoke, mspecs); + marshalling_enabled = runtime_marshalling_enabled(m_class_get_image (delegate_klass)); } sig = mono_method_signature_internal (method); @@ -3840,6 +4026,9 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, csig->hasthis = 0; csig->pinvoke = 1; + if (!marshalling_enabled) + csig->marshalling_disabled = 1; + memset (&m, 0, sizeof (m)); m.mb = mb; m.sig = sig; @@ -3847,6 +4036,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, m.retobj_var = 0; m.csig = csig; m.image = get_method_image (method); + m.runtime_marshalling_enabled = marshalling_enabled; if (invoke) mono_marshal_set_callconv_from_modopt (invoke, csig, TRUE); @@ -3971,6 +4161,7 @@ mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type) MonoMethodSignature *csig; MonoMarshalSpec **mspecs; EmitMarshalContext m; + gboolean marshalling_enabled = runtime_marshalling_enabled(image); sig = mono_method_signature_internal (method); g_assert (!sig->hasthis); @@ -3982,6 +4173,8 @@ mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type) csig = mono_metadata_signature_dup_full (image, sig); csig->hasthis = 0; csig->pinvoke = 1; + if (!marshalling_enabled) + csig->marshalling_disabled = 1; memset (&m, 0, sizeof (m)); m.mb = mb; @@ -3990,6 +4183,7 @@ mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type) m.retobj_var = 0; m.csig = csig; m.image = image; + m.runtime_marshalling_enabled = marshalling_enabled; mono_marshal_set_callconv_from_modopt (method, csig, TRUE); diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index a941934c564462..1779131e450938 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -50,6 +50,7 @@ typedef struct { MonoClass *retobj_class; MonoMethodSignature *csig; /* Might need to be changed due to MarshalAs directives */ MonoImage *image; /* The image to use for looking up custom marshallers */ + gboolean runtime_marshalling_enabled; } EmitMarshalContext; typedef enum { @@ -295,12 +296,13 @@ typedef enum { EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS = 0x02, EMIT_NATIVE_WRAPPER_FUNC_PARAM = 0x04, EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED = 0x08, - EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS=0x10, + EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS = 0x10, + EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED = 0x20, } MonoNativeWrapperFlags; G_ENUM_FUNCTIONS(MonoNativeWrapperFlags); -#define MONO_MARSHAL_CALLBACKS_VERSION 5 +#define MONO_MARSHAL_CALLBACKS_VERSION 6 typedef struct { int version; @@ -347,6 +349,7 @@ typedef struct { void (*mb_emit_exception) (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg); void (*mb_emit_exception_for_error) (MonoMethodBuilder *mb, const MonoError *emitted_error); void (*mb_emit_byte) (MonoMethodBuilder *mb, guint8 op); + void (*emit_marshal_directive_exception) (EmitMarshalContext *m, int argnum, const char* msg); } MonoMarshalCallbacks; /*type of the function pointer of methods returned by mono_marshal_get_runtime_invoke*/ diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 4e5ddf40abe86b..ba8f957aaf2d02 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -213,6 +213,8 @@ struct _MonoAssembly { guint8 wrap_non_exception_throws_inited; guint8 jit_optimizer_disabled; guint8 jit_optimizer_disabled_inited; + guint8 runtime_marshalling_enabled; + guint8 runtime_marshalling_enabled_inited; /* security manager flags (one bit is for lazy initialization) */ guint32 skipverification:2; /* Has SecurityPermissionFlag.SkipVerification permission */ }; @@ -657,6 +659,7 @@ struct _MonoMethodSignature { unsigned int is_inflated : 1; unsigned int has_type_parameters : 1; unsigned int suppress_gc_transition : 1; + unsigned int marshalling_disabled : 1; MonoType *params [MONO_ZERO_LEN_ARRAY]; }; diff --git a/src/mono/mono/mini/calls.c b/src/mono/mono/mini/calls.c index dc3007b36bfb74..e4877f6d98c032 100644 --- a/src/mono/mono/mini/calls.c +++ b/src/mono/mono/mini/calls.c @@ -193,7 +193,7 @@ mini_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL); MonoInst *loada; - temp->backend.is_pinvoke = sig->pinvoke; + temp->backend.is_pinvoke = sig->pinvoke && !sig->marshalling_disabled; /* * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 2cd43b6fffd6a9..6d5592b80fd2e9 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1525,11 +1525,11 @@ interp_frame_arg_to_data (MonoInterpFrameHandle frame, MonoMethodSignature *sig, // If index == -1, we finished executing an InterpFrame and the result is at retval. if (index == -1) - stackval_to_data (sig->ret, iframe->retval, data, TRUE); + stackval_to_data (sig->ret, iframe->retval, data, sig->pinvoke && !sig->marshalling_disabled); else if (sig->hasthis && index == 0) *(gpointer*)data = iframe->stack->data.p; else - stackval_to_data (sig->params [index - sig->hasthis], STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)), data, sig->pinvoke); + stackval_to_data (sig->params [index - sig->hasthis], STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)), data, sig->pinvoke && !sig->marshalling_disabled); } static void @@ -1540,11 +1540,11 @@ interp_data_to_frame_arg (MonoInterpFrameHandle frame, MonoMethodSignature *sig, // Get result from pinvoke call, put it directly on top of execution stack in the caller frame if (index == -1) - stackval_from_data (sig->ret, iframe->retval, data, TRUE); + stackval_from_data (sig->ret, iframe->retval, data, sig->pinvoke && !sig->marshalling_disabled); else if (sig->hasthis && index == 0) iframe->stack->data.p = *(gpointer*)data; else - stackval_from_data (sig->params [index - sig->hasthis], STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)), data, sig->pinvoke); + stackval_from_data (sig->params [index - sig->hasthis], STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)), data, sig->pinvoke && !sig->marshalling_disabled); } static gpointer @@ -1676,7 +1676,7 @@ ves_pinvoke_method ( #else // Only the vt address has been returned, we need to copy the entire content on interp stack if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (sig->ret)) - stackval_from_data (sig->ret, frame.retval, (char*)frame.retval->data.p, sig->pinvoke); + stackval_from_data (sig->ret, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); g_free (margs->iargs); g_free (margs->fargs); @@ -2273,7 +2273,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *ret_sp, stackval *sp, gpoi /* convert the native representation to the stackval representation */ if (sig) - stackval_from_data (sig->ret, ret_sp, (char*) &ret_sp->data.p, sig->pinvoke); + stackval_from_data (sig->ret, ret_sp, (char*) &ret_sp->data.p, sig->pinvoke && !sig->marshalling_disabled); } /* MONO_NO_OPTIMIZATION is needed due to usage of INTERP_PUSH_LMF_WITH_CTX. */ @@ -2870,7 +2870,7 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype if (type->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (type)) { size = mono_class_value_size (mono_class_from_mono_type_internal (type), NULL); } else if (type->type == MONO_TYPE_VALUETYPE) { - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (type->data.klass, NULL); else size = mono_class_value_size (type->data.klass, NULL); @@ -6477,7 +6477,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_BREAK; MINT_IN_CASE(MINT_MONO_RETOBJ) stackval_from_data (mono_method_signature_internal (frame->imethod->method)->ret, frame->stack, LOCAL_VAR (ip [1], gpointer), - mono_method_signature_internal (frame->imethod->method)->pinvoke); + mono_method_signature_internal (frame->imethod->method)->pinvoke && !mono_method_signature_internal (frame->imethod->method)->marshalling_disabled); frame_data_allocator_pop (&context->data_stack, frame); goto exit_frame; MINT_IN_CASE(MINT_MONO_SGEN_THREAD_INFO) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 263603de741c18..e877f5ce0f5587 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -980,7 +980,7 @@ load_arg(TransformData *td, int n) if (mt == MINT_TYPE_VT) { klass = mono_class_from_mono_type_internal (type); - if (mono_method_signature_internal (td->method)->pinvoke) + if (mono_method_signature_internal (td->method)->pinvoke && !mono_method_signature_internal (td->method)->marshalling_disabled) size = mono_class_native_size (klass, NULL); else size = mono_class_value_size (klass, NULL); @@ -1023,7 +1023,7 @@ store_arg(TransformData *td, int n) if (mt == MINT_TYPE_VT) { MonoClass *klass = mono_class_from_mono_type_internal (type); - if (mono_method_signature_internal (td->method)->pinvoke) + if (mono_method_signature_internal (td->method)->pinvoke && !mono_method_signature_internal (td->method)->marshalling_disabled) size = mono_class_native_size (klass, NULL); else size = mono_class_value_size (klass, NULL); @@ -3397,7 +3397,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret); if (mt == MINT_TYPE_VT) { - if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE) + if (csignature->pinvoke && !csignature->marshalling_disabled && method->wrapper_type != MONO_WRAPPER_NONE) res_size = mono_class_native_size (klass, NULL); else res_size = mono_class_value_size (klass, NULL); @@ -7013,7 +7013,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int size; CHECK_STACK (td, 1); MonoClass *klass = td->sp [-1].klass; - if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && !signature->marshalling_disabled) size = mono_class_native_size (klass, NULL); else size = mono_class_value_size (klass, NULL); diff --git a/src/mono/mono/mini/mini-amd64.c b/src/mono/mono/mini/mini-amd64.c index 7b5c36779f1b05..7e1f5a879ea6ec 100644 --- a/src/mono/mono/mini/mini-amd64.c +++ b/src/mono/mono/mini/mini-amd64.c @@ -606,7 +606,7 @@ add_valuetype_win64 (MonoMethodSignature *signature, ArgInfo *arg_info, MonoType assert (signature != NULL && arg_info != NULL && type != NULL && current_int_reg != NULL && current_float_reg != NULL && stack_size != NULL); klass = mono_class_from_mono_type_internal (type); - get_valuetype_size_win64 (klass, signature->pinvoke, arg_info, type, &arg_class, &arg_size); + get_valuetype_size_win64 (klass, signature->pinvoke && !signature->marshalling_disabled, arg_info, type, &arg_class, &arg_size); /* Only drop value type if its not an empty struct as input that must be represented in call */ if ((arg_size == 0 && !arg_info->pass_empty_struct) || (arg_info->pass_empty_struct && is_return)) { @@ -640,7 +640,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, int struct_size; klass = mono_class_from_mono_type_internal (type); - size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke); + size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); if (!sig->pinvoke && ((is_return && (size == 8)) || (!is_return && (size <= 16)))) { /* We pass and return vtypes of size 8 in a register */ @@ -650,7 +650,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, /* If this struct can't be split up naturally into 8-byte */ /* chunks (registers), pass it on the stack. */ - if (sig->pinvoke) { + if (sig->pinvoke && !sig->marshalling_disabled) { MonoMarshalType *info = mono_marshal_load_type_info (klass); g_assert (info); struct_size = info->native_size; @@ -662,7 +662,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, * handle nested structures. */ fields_array = g_array_new (FALSE, TRUE, sizeof (StructFieldInfo)); - collect_field_info_nested (klass, fields_array, 0, sig->pinvoke, m_class_is_unicode (klass)); + collect_field_info_nested (klass, fields_array, 0, sig->pinvoke && !sig->marshalling_disabled, m_class_is_unicode (klass)); fields = (StructFieldInfo*)fields_array->data; nfields = fields_array->len; @@ -2311,7 +2311,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) guint32 align; guint32 size; - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_type_native_stack_size (t, &align); else { /* @@ -2462,7 +2462,7 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) g_assert (ainfo->storage == ArgValuetypeAddrInIReg || (ainfo->storage == ArgValuetypeAddrOnStack && ainfo->pair_storage [0] == ArgNone)); vtaddr = mono_compile_create_var (cfg, m_class_get_byval_arg (ins->klass), OP_LOCAL); - vtaddr->backend.is_pinvoke = call->signature->pinvoke; + vtaddr->backend.is_pinvoke = call->signature->pinvoke && !call->signature->marshalling_disabled; MONO_INST_NEW (cfg, load, OP_LDADDR); cfg->has_indirection = TRUE; diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c index 3f3b4a9bc88b33..c9a8c319786a55 100644 --- a/src/mono/mono/mini/mini-arm.c +++ b/src/mono/mono/mini/mini-arm.c @@ -605,7 +605,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit arg_info [0].size = frame_size; for (k = 0; k < param_count; k++) { - size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke); + size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke && !csig->marshalling_disabled); /* ignore alignment for now */ align = 1; @@ -1333,7 +1333,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) cinfo->ret.nregs = nfields; cinfo->ret.esize = esize; } else { - if (is_pinvoke) { + if (sig->pinvoke && !sig->marshalling_disabled) { int native_size = mono_class_native_size (mono_class_from_mono_type_internal (t), &align); int max_size; @@ -1496,7 +1496,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) align = sizeof (target_mgreg_t); } else { MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]); - if (is_pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (klass, &align); else size = mini_type_stack_size_full (t, &align, FALSE); @@ -2173,7 +2173,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) if (ins->opcode != OP_REGVAR) { ins->opcode = OP_REGOFFSET; ins->inst_basereg = cfg->frame_reg; - size = mini_type_stack_size_full (sig->params [i], &ualign, sig->pinvoke); + size = mini_type_stack_size_full (sig->params [i], &ualign, sig->pinvoke && !sig->marshalling_disabled); align = ualign; /* FIXME: if a structure is misaligned, our memcpy doesn't work, * since it loads/stores misaligned words, which don't do the right thing. @@ -6521,7 +6521,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) int soffset = 0; int cur_reg; int size = 0; - size = mini_type_stack_size_full (inst->inst_vtype, NULL, sig->pinvoke); + size = mini_type_stack_size_full (inst->inst_vtype, NULL, sig->pinvoke && !sig->marshalling_disabled); for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) { if (arm_is_imm12 (doffset)) { ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset); diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 449cf376fbaea3..54d961ab9c8b02 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -1763,7 +1763,7 @@ mini_get_interp_in_wrapper (MonoMethodSignature *sig) * stack, pass this address to the interp_entry and when we return it we use * CEE_MONO_LDNATIVEOBJ */ - return_native_struct = sig->ret->type == MONO_TYPE_VALUETYPE && sig->pinvoke; + return_native_struct = sig->ret->type == MONO_TYPE_VALUETYPE && sig->pinvoke && !sig->marshalling_disabled; /* Create the signature for the wrapper */ csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count * sizeof (MonoType*))); diff --git a/src/mono/mono/mini/mini-mips.c b/src/mono/mono/mini/mini-mips.c index 666c3cd51f936a..3db3a9debab419 100644 --- a/src/mono/mono/mini/mini-mips.c +++ b/src/mono/mono/mini/mini-mips.c @@ -488,7 +488,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit arg_info [0].size = frame_size; for (k = 0; k < param_count; k++) { - size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke); + size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke && !csig->marshalling_disabled); /* ignore alignment for now */ align = 1; @@ -1156,7 +1156,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) alignment = sizeof (target_mgreg_t); } else { klass = mono_class_from_mono_type_internal (sig->params [i]); - if (is_pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (klass, NULL); else size = mono_class_value_size (klass, NULL); @@ -1878,7 +1878,7 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) guint32 size; /* FIXME: alignment? */ - if (call->signature->pinvoke) { + if (call->signature->pinvoke && !call->signature->marshalling_disabled) { size = mono_type_native_stack_size (m_class_get_byval_arg (src->klass), NULL); vtcopy->backend.is_pinvoke = 1; } else { diff --git a/src/mono/mono/mini/mini-ppc.c b/src/mono/mono/mini/mini-ppc.c index df2cd3b6f362b3..48174a3130ac32 100644 --- a/src/mono/mono/mini/mini-ppc.c +++ b/src/mono/mono/mini/mini-ppc.c @@ -248,7 +248,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit for (k = 0; k < param_count; k++) { - if (csig->pinvoke) + if (csig->pinvoke && !csig->marshalling_disabled) size = mono_type_native_stack_size (csig->params [k], (guint32*)&align); else size = mini_type_stack_size (csig->params [k], &align); @@ -1109,7 +1109,7 @@ get_call_info (MonoMethodSignature *sig) MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]); if (simpletype->type == MONO_TYPE_TYPEDBYREF) size = MONO_ABI_SIZEOF (MonoTypedRef); - else if (is_pinvoke) + else if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size (klass, NULL); else size = mono_class_value_size (klass, NULL); @@ -1498,7 +1498,7 @@ mono_arch_allocate_vars (MonoCompile *m) if (inst->opcode != OP_REGVAR) { inst->opcode = OP_REGOFFSET; inst->inst_basereg = frame_reg; - if (sig->pinvoke) { + if (sig->pinvoke && !sig->marshalling_disabled) { size = mono_type_native_stack_size (sig->params [i], (guint32*)&align); inst->backend.is_pinvoke = 1; } else { @@ -1739,7 +1739,7 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) * and 2 byte arguments */ g_assert (ins->klass); - if (call->signature->pinvoke) + if (call->signature->pinvoke && !call->signature->marshalling_disabled) size = mono_class_native_size (ins->klass, NULL); if (size == 2 || size == 1) { int tmpr = mono_alloc_ireg (cfg); @@ -1801,7 +1801,7 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) guint32 size; /* FIXME: alignment? */ - if (call->signature->pinvoke) { + if (call->signature->pinvoke && !call->signature->marshalling_disabled) { size = mono_type_native_stack_size (m_class_get_byval_arg (src->klass), NULL); vtcopy->backend.is_pinvoke = 1; } else { @@ -5036,7 +5036,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) g_assert (ppc_is_imm16 (inst->inst_offset)); g_assert (ppc_is_imm16 (inst->inst_offset + ainfo->vtregs * sizeof (target_mgreg_t))); /* FIXME: what if there is no class? */ - if (sig->pinvoke && mono_class_from_mono_type_internal (inst->inst_vtype)) + if (sig->pinvoke && !sig->marshalling_disabled && mono_class_from_mono_type_internal (inst->inst_vtype)) size = mono_class_native_size (mono_class_from_mono_type_internal (inst->inst_vtype), NULL); for (cur_reg = 0; cur_reg < ainfo->vtregs; ++cur_reg) { if (ainfo->size == 4) { @@ -5055,7 +5055,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) g_assert (ppc_is_imm16 (inst->inst_offset)); g_assert (ppc_is_imm16 (inst->inst_offset + ainfo->vtregs * sizeof (target_mgreg_t))); /* FIXME: what if there is no class? */ - if (sig->pinvoke && mono_class_from_mono_type_internal (inst->inst_vtype)) + if (sig->pinvoke && !sig->marshalling_disabled && mono_class_from_mono_type_internal (inst->inst_vtype)) size = mono_class_native_size (mono_class_from_mono_type_internal (inst->inst_vtype), NULL); for (cur_reg = 0; cur_reg < ainfo->vtregs; ++cur_reg) { #if __APPLE__ diff --git a/src/mono/mono/mini/mini-s390x.c b/src/mono/mono/mini/mini-s390x.c index b3fb3de3ab061e..5e4aca54eadb60 100644 --- a/src/mono/mono/mini/mini-s390x.c +++ b/src/mono/mono/mini/mini-s390x.c @@ -510,7 +510,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, for (k = 0; k < param_count; k++) { - if (csig->pinvoke) + if (csig->pinvoke && !csig->marshalling_disabled) size = mono_type_native_stack_size (csig->params [k], (guint32 *) &align); else size = mini_type_stack_size (csig->params [k], &align); @@ -556,7 +556,7 @@ emit_new_move(MonoCompile *cfg, int dr, MonoInst *ins, MonoInst *src) MonoInst *move; int size; - if (call->signature->pinvoke) { + if (call->signature->pinvoke && !call->signature->marshalling_disabled) { size = mono_type_native_stack_size (m_class_get_byval_arg (src->klass), NULL); vtcopy->backend.is_pinvoke = 1; } else { @@ -1130,7 +1130,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) simpleType = mono_class_enum_basetype_internal (klass)->type; goto enum_retvalue; } - size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke); + size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); cinfo->struct_ret = 1; cinfo->ret.size = size; @@ -1139,7 +1139,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) } case MONO_TYPE_TYPEDBYREF: { MonoClass *klass = mono_class_from_mono_type_internal (sig->ret); - size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke); + size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); cinfo->struct_ret = 1; cinfo->ret.size = size; @@ -1288,7 +1288,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) MonoMarshalType *info; MonoClass *klass = mono_class_from_mono_type_internal (ptype); - if (sig->pinvoke) + if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size(klass, NULL); else size = mono_class_value_size(klass, NULL); @@ -1950,7 +1950,7 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) guint32 size; /* FIXME: alignment? */ - if (call->signature->pinvoke) { + if (call->signature->pinvoke && !call->signature->marshalling_disabled) { size = mono_type_native_stack_size (m_class_get_byval_arg (src->klass), NULL); vtcopy->backend.is_pinvoke = 1; } else { @@ -5814,7 +5814,7 @@ printf("ns: %s k: %s m: %s\n",method->klass->name_space,method->klass->name,meth } else if (ainfo->regtype == RegTypeStructByVal) { int doffset = inst->inst_offset; - size = (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE + size = (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && sig->pinvoke && !sig->marshalling_disabled ? mono_class_native_size(mono_class_from_mono_type_internal (inst->inst_vtype), NULL) : ainfo->size); diff --git a/src/mono/mono/mini/mini-sparc.c b/src/mono/mono/mini/mini-sparc.c index 184477cfc51757..2efe4ba06e74ed 100644 --- a/src/mono/mono/mini/mini-sparc.c +++ b/src/mono/mono/mini/mini-sparc.c @@ -1297,7 +1297,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) arg_type = mini_get_underlying_type (arg_type); if ((i >= sig->hasthis) && (MONO_TYPE_ISSTRUCT(sig->params [i - sig->hasthis]))) - emit_pass_vtype (cfg, call, cinfo, ainfo, arg_type, in, sig->pinvoke); + emit_pass_vtype (cfg, call, cinfo, ainfo, arg_type, in, sig->pinvoke && !sig->marshalling_disabled); else if (!m_type_is_byref (arg_type) && ((arg_type->type == MONO_TYPE_I8) || (arg_type->type == MONO_TYPE_U8))) emit_pass_long (cfg, call, ainfo, in); else if (!m_type_is_byref (arg_type) && (arg_type->type == MONO_TYPE_R8)) @@ -2016,7 +2016,7 @@ emit_vret_token (MonoInst *ins, guint32 *code) * The sparc ABI requires that calls to functions which return a structure * contain an additional unimpl instruction which is checked by the callee. */ - if (call->signature->pinvoke && MONO_TYPE_ISSTRUCT(call->signature->ret)) { + if (call->signature->pinvoke && !call->signature->marshalling_disabled && MONO_TYPE_ISSTRUCT(call->signature->ret)) { if (call->signature->ret->type == MONO_TYPE_TYPEDBYREF) size = mini_type_stack_size (call->signature->ret, NULL); else diff --git a/src/mono/mono/mini/mini-x86.c b/src/mono/mono/mini/mini-x86.c index 52972a5e98ece4..53a83263399bc5 100644 --- a/src/mono/mono/mini/mini-x86.c +++ b/src/mono/mono/mini/mini-x86.c @@ -226,7 +226,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, MonoClass *klass; klass = mono_class_from_mono_type_internal (type); - size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke); + size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); #if defined(TARGET_WIN32) /* @@ -728,7 +728,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit prev_stackarg = 0; for (k = 0; k < param_count; k++) { - size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke); + size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke && !csig->marshalling_disabled); if (storage_in_ireg (cinfo->args [csig->hasthis + k].storage)) { /* not in stack, we'll give it an offset at the end */ @@ -1592,7 +1592,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) align = sizeof (target_mgreg_t); } else { - size = mini_type_stack_size_full (m_class_get_byval_arg (in->klass), &align, sig->pinvoke); + size = mini_type_stack_size_full (m_class_get_byval_arg (in->klass), &align, sig->pinvoke && !sig->marshalling_disabled); } if (size > 0 || ainfo->pass_empty_struct) { diff --git a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs index 9d27d24c46080d..fa92071c917e82 100644 --- a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs +++ b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs @@ -10,7 +10,7 @@ public static class OptionsHelper private const string PriorityOption = "build_property.Priority"; private const string RuntimeFlavorOption = "build_property.RuntimeFlavor"; private const string IsOutOfProcessTestAssemblyOption = "build_metadata.AdditionalFiles.IsOutOfProcessTestAssembly"; - private const string TestFilterOption = "build_metadata.AdditionalFiles.TestFilter"; + private const string TestFilterOption = "build_property.TestFilter"; private const string TestAssemblyRelativePathOption = "build_metadata.AdditionalFiles.TestAssemblyRelativePath"; private const string TestDisplayNameOption = "build_metadata.AdditionalFiles.TestDisplayName"; private const string SingleTestDisplayNameOption = "build_metadata.AdditionalFiles.SingleTestDisplayName"; diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs index 5804b2017ad5f6..988a1dcb2ce0af 100644 --- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs +++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs @@ -361,7 +361,7 @@ private static IEnumerable GetTestMethodInfosForMethod(IMethodSymbol } break; case "Xunit.SkipOnMonoAttribute": - if (options.GlobalOptions.RuntimeFlavor() != "Mono") + if (options.GlobalOptions.RuntimeFlavor().ToLowerInvariant() != "mono") { // If we're building tests not for Mono, we can skip handling the specifics of the SkipOnMonoAttribute. continue; @@ -378,7 +378,7 @@ private static IEnumerable GetTestMethodInfosForMethod(IMethodSymbol testInfos = FilterForSkippedTargetFrameworkMonikers(testInfos, (int)filterAttribute.ConstructorArguments[0].Value!); break; case "Xunit.SkipOnCoreClrAttribute": - if (options.GlobalOptions.RuntimeFlavor() != "CoreCLR") + if (options.GlobalOptions.RuntimeFlavor().ToLowerInvariant() != "coreclr") { // If we're building tests not for CoreCLR, we can skip handling the specifics of the SkipOnCoreClrAttribute. continue; @@ -558,12 +558,12 @@ private static ImmutableArray CreateTestCases(IMethodSymbol method, L private static ImmutableArray FilterForSkippedRuntime(ImmutableArray testInfos, int skippedRuntimeValue, AnalyzerConfigOptionsProvider options) { Xunit.TestRuntimes skippedRuntimes = (Xunit.TestRuntimes)skippedRuntimeValue; - string runtimeFlavor = options.GlobalOptions.RuntimeFlavor(); - if (runtimeFlavor == "Mono" && skippedRuntimes.HasFlag(Xunit.TestRuntimes.Mono)) + string runtimeFlavor = options.GlobalOptions.RuntimeFlavor().ToLowerInvariant(); + if (runtimeFlavor == "mono" && skippedRuntimes.HasFlag(Xunit.TestRuntimes.Mono)) { return ImmutableArray.Empty; } - else if (runtimeFlavor == "CoreCLR" && skippedRuntimes.HasFlag(Xunit.TestRuntimes.CoreCLR)) + else if (runtimeFlavor == "coreclr" && skippedRuntimes.HasFlag(Xunit.TestRuntimes.CoreCLR)) { return ImmutableArray.Empty; } diff --git a/src/tests/Common/tests.targets b/src/tests/Common/tests.targets index ff6ba352e24cc7..a99db7918fd22e 100644 --- a/src/tests/Common/tests.targets +++ b/src/tests/Common/tests.targets @@ -66,17 +66,6 @@ $(XunitArgs) -nocolor - - $(CORE_ROOT)\corerun - $(CORE_ROOT)\corerun.exe - - - dotnet --roll-forward LatestMajor - $(DotnetRoot)/dotnet - true - $(DotnetExecutable) - $(CorerunExecutable) $(XunitConsoleRunner) @(TestAssemblies->'%(Identity)', ' ') $(XunitArgs) + $(DotNetCli) --roll-forward latestmajor $(XunitConsoleRunner) @(TestAssemblies->'%(Identity)', ' ') $(XunitArgs) (() => DisabledRuntimeMarshallingNative.CallWithAutoLayoutStruct(new AutoLayoutStruct())); + } + + [Fact] + public static void StructWithAutoLayoutField() + { + short s = 42; + bool b = true; + AssertThrowsMarshalDirectiveOrTypeLoad(() => DisabledRuntimeMarshallingNative.CallWithAutoLayoutStruct(new SequentialWithAutoLayoutField())); + } + + [Fact] + public static void StructWithNestedAutoLayoutField() + { + short s = 42; + bool b = true; + AssertThrowsMarshalDirectiveOrTypeLoad(() => DisabledRuntimeMarshallingNative.CallWithAutoLayoutStruct(new SequentialWithAutoLayoutNestedField())); + } + + private static void AssertThrowsMarshalDirectiveOrTypeLoad(Action testCode) + { + try + { + testCode(); + return; + } + catch (Exception ex) when(ex is MarshalDirectiveException or TypeLoadException) + { + return; + } + catch (Exception ex) + { + Assert.False(true, $"Expected either a MarshalDirectiveException or a TypeLoadException, but received a '{ex.GetType().FullName}' exception: '{ex.ToString()}'"); + } + Assert.False(true, $"Expected either a MarshalDirectiveException or a TypeLoadException, but received no exception."); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/CMakeLists.txt b/src/tests/Interop/DisabledRuntimeMarshalling/CMakeLists.txt new file mode 100644 index 00000000000000..018819bf330730 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/CMakeLists.txt @@ -0,0 +1,10 @@ +project (DisabledRuntimeMarshallingNative) +include_directories(${INC_PLATFORM_DIR}) +set(SOURCES DisabledRuntimeMarshallingNative.cpp ) + +# add the executable +add_library (DisabledRuntimeMarshallingNative SHARED ${SOURCES}) +target_link_libraries(DisabledRuntimeMarshallingNative ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS DisabledRuntimeMarshallingNative DESTINATION bin) diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshallingNative.cpp b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshallingNative.cpp new file mode 100644 index 00000000000000..e0a0a9e7f32a11 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshallingNative.cpp @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +struct StructWithShortAndBool +{ + bool b; + short s; + // Make sure we don't have any cases where the native code could return a value of this type one way, + // but an invalid managed declaration would expect it differently. This ensures that test failures won't be + // due to crashes from a mismatched return scheme in the calling convention. + int padding; +}; + +struct StructWithWCharAndShort +{ + short s; + WCHAR c; +}; + +extern "C" DLL_EXPORT bool STDMETHODCALLTYPE CheckStructWithShortAndBool(StructWithShortAndBool str, short s, bool b) +{ + return str.s == s && str.b == b; +} + +extern "C" DLL_EXPORT bool STDMETHODCALLTYPE CheckStructWithWCharAndShort(StructWithWCharAndShort str, short s, WCHAR c) +{ + return str.s == s && str.c == c; +} + +using CheckStructWithShortAndBoolCallback = bool (STDMETHODCALLTYPE*)(StructWithShortAndBool, short, bool); + +extern "C" DLL_EXPORT CheckStructWithShortAndBoolCallback STDMETHODCALLTYPE GetStructWithShortAndBoolCallback() +{ + return &CheckStructWithShortAndBool; +} + +extern "C" DLL_EXPORT bool STDMETHODCALLTYPE CallCheckStructWithShortAndBoolCallback(CheckStructWithShortAndBoolCallback cb, StructWithShortAndBool str, short s, bool b) +{ + return cb(str, s, b); +} + +extern "C" DLL_EXPORT BYTE PassThrough(BYTE b) +{ + return b; +} + +extern "C" DLL_EXPORT void Invalid(...) {} + + +extern "C" DLL_EXPORT bool STDMETHODCALLTYPE CheckStructWithShortAndBoolWithVariantBool(StructWithShortAndBool str, short s, VARIANT_BOOL b) +{ + // Specifically use VARIANT_TRUE here as invalid marshalling (in the "disabled runtime marshalling" case) will incorrectly marshal VARAINT_TRUE + // but could accidentally marshal VARIANT_FALSE correctly since it is 0, which is the same representation as a zero or sign extension of the C# false value. + return str.s == s && str.b == (b == VARIANT_TRUE); +} + +using CheckStructWithShortAndBoolWithVariantBoolCallback = bool (STDMETHODCALLTYPE*)(StructWithShortAndBool, short, VARIANT_BOOL); + +extern "C" DLL_EXPORT CheckStructWithShortAndBoolWithVariantBoolCallback STDMETHODCALLTYPE GetStructWithShortAndBoolWithVariantBoolCallback() +{ + return &CheckStructWithShortAndBoolWithVariantBool; +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeAssemblyDisabled.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeAssemblyDisabled.csproj new file mode 100644 index 00000000000000..2de6caeb97d02a --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeAssemblyDisabled.csproj @@ -0,0 +1,13 @@ + + + true + + + + + + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeAssemblyEnabled.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeAssemblyEnabled.csproj new file mode 100644 index 00000000000000..88ce904b51e4bf --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeAssemblyEnabled.csproj @@ -0,0 +1,13 @@ + + + true + + + + + + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeTypeInAssembly.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeTypeInAssembly.csproj new file mode 100644 index 00000000000000..702588382eb337 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeTypeInAssembly.csproj @@ -0,0 +1,11 @@ + + + true + + + + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeTypeInAssembly_ro.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeTypeInAssembly_ro.csproj new file mode 100644 index 00000000000000..b947e14f0b8916 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_Disabled_NativeTypeInAssembly_ro.csproj @@ -0,0 +1,14 @@ + + + true + + None + true + + + + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_NativeAssemblyDisabled.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_NativeAssemblyDisabled.csproj new file mode 100644 index 00000000000000..61ddd72ac618ba --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/DisabledRuntimeMarshalling_NativeAssemblyDisabled.csproj @@ -0,0 +1,14 @@ + + + true + + + + + + + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/FunctionPointers.cs b/src/tests/Interop/DisabledRuntimeMarshalling/FunctionPointers.cs new file mode 100644 index 00000000000000..927c175623629e --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/FunctionPointers.cs @@ -0,0 +1,29 @@ +// 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.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling; + +public unsafe class FunctionPointers +{ + [Fact] + public static void StructWithDefaultNonBlittableFields_DoesNotMarshal() + { + short s = 42; + bool b = true; + Assert.True(DisabledRuntimeMarshallingNative.GetStructWithShortAndBoolCallback()(new StructWithShortAndBool(s, b), s, b)); + } + + [Fact] + public static void StructWithDefaultNonBlittableFields_IgnoresMarshalAsInfo() + { + short s = 41; + bool b = true; + + Assert.False(DisabledRuntimeMarshallingNative.GetStructWithShortAndBoolWithVariantBoolCallback()(new StructWithShortAndBool(s, b), s, b)); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/Native_Default/DisabledRuntimeMarshallingNative.cs b/src/tests/Interop/DisabledRuntimeMarshalling/Native_Default/DisabledRuntimeMarshallingNative.cs new file mode 100644 index 00000000000000..5be5549f7b1570 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/Native_Default/DisabledRuntimeMarshallingNative.cs @@ -0,0 +1,153 @@ +// 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.Runtime.InteropServices; +using Xunit; + +public unsafe class DisabledRuntimeMarshallingNative +{ + public struct StructWithShortAndBool + { + public bool b; + public short s; + int padding; + + public StructWithShortAndBool(short s, bool b) + { + this.s = s; + this.b = b; + this.padding = 0xdeadbee; + } + } + + public struct StructWithShortAndBoolWithMarshalAs + { + [MarshalAs(UnmanagedType.U1)] + bool b; + short s; + int padding; + + public StructWithShortAndBoolWithMarshalAs(short s, bool b) + { + this.s = s; + this.b = b; + this.padding = 0xdeadbee; + } + } + + public struct StructWithWCharAndShort + { + short s; + [MarshalAs(UnmanagedType.U2)] + char c; + + public StructWithWCharAndShort(short s, char c) + { + this.s = s; + this.c = c; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct StructWithWCharAndShortWithMarshalAs + { + short s; + [MarshalAs(UnmanagedType.U2)] + char c; + + public StructWithWCharAndShortWithMarshalAs(short s, char c) + { + this.s = s; + this.c = c; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct StructWithShortAndGeneric + { + short s; + T t; + public StructWithShortAndGeneric(short s, T t) + { + this.s = s; + this.t = t; + } + } + + [StructLayout(LayoutKind.Auto)] + public struct AutoLayoutStruct + { + int i; + } + + public struct SequentialWithAutoLayoutField + { + AutoLayoutStruct auto; + } + + public struct SequentialWithAutoLayoutNestedField + { + SequentialWithAutoLayoutField field; + } + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithShortAndBool(StructWithShortAndBool str, short s, bool b); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithShortAndBool(StructWithShortAndBoolWithMarshalAs str, short s, [MarshalAs(UnmanagedType.Bool)] bool b); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithWCharAndShort(StructWithWCharAndShort str, short s, char c); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithWCharAndShort(StructWithShortAndGeneric str, short s, char c); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithWCharAndShort(StructWithShortAndGeneric str, short s, short c); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithWCharAndShort(StructWithWCharAndShortWithMarshalAs str, short s, [MarshalAs(UnmanagedType.U1)] char c); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern delegate* unmanaged GetStructWithShortAndBoolCallback(); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CallCheckStructWithShortAndBoolCallback(delegate* cb, StructWithShortAndBool str, short s, bool b); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern delegate* unmanaged GetStructWithShortAndBoolWithVariantBoolCallback(); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "PassThrough")] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool GetByteAsBool(byte b); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithAutoLayoutStruct(AutoLayoutStruct s); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithAutoLayoutStruct(SequentialWithAutoLayoutField s); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithAutoLayoutStruct(SequentialWithAutoLayoutNestedField s); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithShortAndBoolWithVariantBool(StructWithShortAndBoolWithMarshalAs str, short s, [MarshalAs(UnmanagedType.VariantBool)] bool b); + + // Apply the UnmanagedFunctionPointer attributes with the default calling conventions so that Mono's AOT compiler + // recognizes that these delegate types are used in interop and should have managed->native thunks generated for them. + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate bool CheckStructWithShortAndBoolCallback(StructWithShortAndBool str, short s, bool b); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate bool CheckStructWithShortAndBoolWithMarshalAsAndVariantBoolCallback(StructWithShortAndBoolWithMarshalAs str, short s, [MarshalAs(UnmanagedType.VariantBool)] bool b); + + [UnmanagedCallersOnly] + public static bool CheckStructWithShortAndBoolManaged(StructWithShortAndBool str, short s, bool b) + { + return str.s == s && str.b == b; + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/Native_Default/DisabledRuntimeMarshallingNative_Default.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/Native_Default/DisabledRuntimeMarshallingNative_Default.csproj new file mode 100644 index 00000000000000..e06e1e391659f1 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/Native_Default/DisabledRuntimeMarshallingNative_Default.csproj @@ -0,0 +1,10 @@ + + + Library + SharedLibrary + true + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/Native_DisabledMarshalling/DisabledRuntimeMarshallingNative.cs b/src/tests/Interop/DisabledRuntimeMarshalling/Native_DisabledMarshalling/DisabledRuntimeMarshallingNative.cs new file mode 100644 index 00000000000000..ed9b121bef6a23 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/Native_DisabledMarshalling/DisabledRuntimeMarshallingNative.cs @@ -0,0 +1,190 @@ +// 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.Runtime.InteropServices; +using Xunit; + +public unsafe class DisabledRuntimeMarshallingNative +{ + public struct StructWithShortAndBool + { + public bool b; + public short s; + int padding; + + public StructWithShortAndBool(short s, bool b) + { + this.s = s; + this.b = b; + this.padding = 0xdeadbee; + } + } + + public struct StructWithShortAndBoolWithMarshalAs + { + [MarshalAs(UnmanagedType.VariantBool)] + bool b; + short s; + int padding; + + public StructWithShortAndBoolWithMarshalAs(short s, bool b) + { + this.s = s; + this.b = b; + this.padding = 0xdeadbee; + } + } + + public struct StructWithWCharAndShort + { + short s; + char c; + + public StructWithWCharAndShort(short s, char c) + { + this.s = s; + this.c = c; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct StructWithWCharAndShortWithMarshalAs + { + short s; + [MarshalAs(UnmanagedType.U1)] + char c; + + public StructWithWCharAndShortWithMarshalAs(short s, char c) + { + this.s = s; + this.c = c; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct StructWithShortAndGeneric + { + short s; + T t; + public StructWithShortAndGeneric(short s, T t) + { + this.s = s; + this.t = t; + } + } + + public struct StructWithString + { + string s; + + public StructWithString(string s) + { + this.s = s; + } + } + + [StructLayout(LayoutKind.Sequential)] + public class LayoutClass + {} + + [StructLayout(LayoutKind.Auto)] + public struct AutoLayoutStruct + { + int i; + } + + public struct SequentialWithAutoLayoutField + { + AutoLayoutStruct auto; + } + + public struct SequentialWithAutoLayoutNestedField + { + SequentialWithAutoLayoutField field; + } + + public enum ByteEnum : byte + { + Value = 42 + } + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithShortAndBool(StructWithShortAndBool str, short s, bool b); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithShortAndBool(StructWithShortAndBoolWithMarshalAs str, short s, [MarshalAs(UnmanagedType.I4)] bool b); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithWCharAndShort(StructWithWCharAndShort str, short s, char c); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithWCharAndShort(StructWithWCharAndShortWithMarshalAs str, short s, char c); + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithWCharAndShort(StructWithShortAndGeneric str, short s, char c); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CheckStructWithWCharAndShort(StructWithShortAndGeneric str, short s, short c); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid", CharSet = CharSet.Ansi)] + public static extern void CheckStringWithAnsiCharSet(string s); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid", CharSet = CharSet.Unicode)] + public static extern void CheckStringWithUnicodeCharSet(string s); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid", CharSet = CharSet.Unicode)] + public static extern string GetStringWithUnicodeCharSet(); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CheckStructWithStructWithString(StructWithString s); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CheckLayoutClass(LayoutClass c); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid", SetLastError = true)] + public static extern void CallWithSetLastError(); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + [LCIDConversion(0)] + public static extern void CallWithLCID(); + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid", PreserveSig = false)] + public static extern int CallWithHResultSwap(); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithAutoLayoutStruct(AutoLayoutStruct s); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithAutoLayoutStruct(SequentialWithAutoLayoutField s); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithAutoLayoutStruct(SequentialWithAutoLayoutNestedField s); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithByRef(ref int i); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "Invalid")] + public static extern void CallWithVarargs(__arglist); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern delegate* unmanaged GetStructWithShortAndBoolCallback(); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern delegate* unmanaged GetStructWithShortAndBoolWithVariantBoolCallback(); + + [DllImport(nameof(DisabledRuntimeMarshallingNative))] + public static extern bool CallCheckStructWithShortAndBoolCallback(delegate* unmanaged cb, StructWithShortAndBool str, short s, bool b); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "PassThrough")] + public static extern bool GetByteAsBool(byte b); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "PassThrough")] + public static extern byte GetEnumUnderlyingValue(ByteEnum b); + + [DllImport(nameof(DisabledRuntimeMarshallingNative), EntryPoint = "CheckStructWithShortAndBoolWithVariantBool")] + [return:MarshalAs(UnmanagedType.U1)] + public static extern bool CheckStructWithShortAndBoolWithVariantBool_FailureExpected(StructWithShortAndBool str, short s, [MarshalAs(UnmanagedType.VariantBool)] bool b); + + // Apply the UnmanagedFunctionPointer attributes with the default calling conventions so that Mono's AOT compiler + // recognizes that these delegate types are used in interop and should have managed->native thunks generated for them. + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate bool CheckStructWithShortAndBoolCallback(StructWithShortAndBool str, short s, bool b); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate bool CheckStructWithShortAndBoolWithVariantBoolCallback(StructWithShortAndBool str, short s, [MarshalAs(UnmanagedType.VariantBool)] bool b); + + [UnmanagedCallersOnly] + public static bool CheckStructWithShortAndBoolManaged(StructWithShortAndBool str, short s, bool b) + { + return str.s == s && str.b == b; + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/Native_DisabledMarshalling/DisabledRuntimeMarshallingNative_DisabledMarshalling.csproj b/src/tests/Interop/DisabledRuntimeMarshalling/Native_DisabledMarshalling/DisabledRuntimeMarshallingNative_DisabledMarshalling.csproj new file mode 100644 index 00000000000000..c3572ce1ae3a54 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/Native_DisabledMarshalling/DisabledRuntimeMarshallingNative_DisabledMarshalling.csproj @@ -0,0 +1,11 @@ + + + Library + SharedLibrary + true + + + + + + diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/Delegates.cs b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/Delegates.cs new file mode 100644 index 00000000000000..a4eb0f33159d0a --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/Delegates.cs @@ -0,0 +1,34 @@ +// 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.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled; + +public unsafe class DelegatesFromExternalAssembly +{ + [Fact] + public static void StructWithDefaultNonBlittableFields_DoesNotMarshal() + { + short s = 42; + bool b = true; + + var callback = Marshal.GetDelegateForFunctionPointer((IntPtr)DisabledRuntimeMarshallingNative.GetStructWithShortAndBoolCallback()); + + Assert.True(callback(new StructWithShortAndBool(s, b), s, b)); + } + + [Fact] + public static void StructWithDefaultNonBlittableFields_IgnoresMarshalAsInfo() + { + short s = 41; + bool b = true; + + var callback = Marshal.GetDelegateForFunctionPointer((IntPtr)DisabledRuntimeMarshallingNative.GetStructWithShortAndBoolWithVariantBoolCallback()); + + Assert.False(callback(new StructWithShortAndBool(s, b), s, b)); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/PInvokes.cs b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/PInvokes.cs new file mode 100644 index 00000000000000..4fa7315d43063d --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/PInvokes.cs @@ -0,0 +1,151 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled; + +public class PInvokes +{ + + [Fact] + public static void StructWithDefaultNonBlittableFields_MarshalAsInfo() + { + short s = 41; + bool b = true; + + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithShortAndBool(new StructWithShortAndBoolWithMarshalAs(s, b), s, b)); + + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithWCharAndShortWithMarshalAs(s, c), s, c)); + } + + [Fact] + public static void Strings_NotSupported() + { + Assert.Throws(() => DisabledRuntimeMarshallingNative.CheckStringWithAnsiCharSet("")); + Assert.Throws(() => DisabledRuntimeMarshallingNative.CheckStringWithUnicodeCharSet("")); + Assert.Throws(() => DisabledRuntimeMarshallingNative.CheckStructWithStructWithString(new StructWithString(""))); + Assert.Throws(() => DisabledRuntimeMarshallingNative.GetStringWithUnicodeCharSet()); + } + + [Fact] + public static void LayoutClass_NotSupported() + { + Assert.Throws(() => DisabledRuntimeMarshallingNative.CheckLayoutClass(new LayoutClass())); + } + + [Fact] + public static void SetLastError_NotSupported() + { + Assert.Throws(() => DisabledRuntimeMarshallingNative.CallWithSetLastError()); + } + + [Fact] + [SkipOnMono("Mono does not support LCIDs at all and it is not worth the effort to add support just to make it throw an exception.")] + public static void LCID_NotSupported() + { + Assert.Throws(() => DisabledRuntimeMarshallingNative.CallWithLCID()); + } + + [Fact] + [SkipOnMono("Mono does not support PreserveSig=False in P/Invokes and it is not worth the effort to add support just to make it throw an exception.")] + public static void PreserveSig_False_NotSupported() + { + Assert.Throws(() => DisabledRuntimeMarshallingNative.CallWithHResultSwap()); + } + + [Fact] + public static void Varargs_NotSupported() + { + AssertThrowsCorrectException(() => DisabledRuntimeMarshallingNative.CallWithVarargs(__arglist(1, 2, 3))); + + static void AssertThrowsCorrectException(Action testCode) + { + try + { + testCode(); + return; + } +#pragma warning disable CS0618 // ExecutionEngineException has an ObsoleteAttribute + catch (Exception ex) when(ex is MarshalDirectiveException or InvalidProgramException or ExecutionEngineException) +#pragma warning restore CS0618 + { + // CoreCLR is expected to throw MarshalDirectiveException + // JIT-enabled Mono is expected to throw an InvalidProgramException + // AOT-only Mono throws an ExecutionEngineException when attempting to JIT the callback that would throw the InvalidProgramException. + return; + } + catch (Exception ex) + { + Assert.False(true, $"Expected either a MarshalDirectiveException, InvalidProgramException, or ExecutionEngineException, but received a '{ex.GetType().FullName}' exception: '{ex.ToString()}'"); + } + Assert.False(true, $"Expected either a MarshalDirectiveException, InvalidProgramException, or ExecutionEngineException, but received no exception."); + } + } + + [Fact] + public static void ByRef_Args_Not_Supported() + { + Assert.Throws(() => + { + int i = 0; + DisabledRuntimeMarshallingNative.CallWithByRef(ref i); + }); + } + + [Fact] + public static void NoBooleanNormalization() + { + byte byteVal = 42; + Assert.Equal(byteVal, Unsafe.As(ref Unsafe.AsRef(DisabledRuntimeMarshallingNative.GetByteAsBool(byteVal)))); + } + + [Fact] + public static void StructWithDefaultNonBlittableFields_DoesNotDoMarshalling() + { + short s = 42; + bool b = true; + + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithShortAndBool(new StructWithShortAndBool(s, b), s, b)); + + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithWCharAndShort(s, c), s, c)); + + Assert.False(DisabledRuntimeMarshallingNative.CheckStructWithShortAndBoolWithVariantBool_FailureExpected(new StructWithShortAndBool(s, b), s, b)); + } + + [Fact] + public static void StructWithNonBlittableGenericInstantiation() + { + short s = 42; + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithShortAndGeneric(s, c), s, c)); + } + + [Fact] + public static void StructWithBlittableGenericInstantiation() + { + short s = 42; + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithShortAndGeneric(s, (short)c), s, (short)c)); + } + + [Fact] + public static void CanUseEnumsWithDisabledMarshalling() + { + Assert.Equal((byte)ByteEnum.Value, DisabledRuntimeMarshallingNative.GetEnumUnderlyingValue(ByteEnum.Value)); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/UnmanagedCallersOnly.cs b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/UnmanagedCallersOnly.cs new file mode 100644 index 00000000000000..f3f963f670d206 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingDisabled/UnmanagedCallersOnly.cs @@ -0,0 +1,23 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled; + +public unsafe class UnmanagedCallersOnly +{ + + [Fact] + public static void UnmanagedCallersOnly_WithNonBlittableParameters_DoesNotMarshal() + { + short s = 41; + bool b = true; + + Assert.True(DisabledRuntimeMarshallingNative.CallCheckStructWithShortAndBoolCallback(&DisabledRuntimeMarshallingNative.CheckStructWithShortAndBoolManaged, new StructWithShortAndBool(s, b), s, b)); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/Delegates.cs b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/Delegates.cs new file mode 100644 index 00000000000000..1e2f1118c88b5f --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/Delegates.cs @@ -0,0 +1,35 @@ +// 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.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingEnabled; + +public unsafe class DelegatesFromExternalAssembly +{ + [Fact] + public static void StructWithDefaultNonBlittableFields() + { + short s = 42; + bool b = true; + + var callback = Marshal.GetDelegateForFunctionPointer((IntPtr)DisabledRuntimeMarshallingNative.GetStructWithShortAndBoolCallback()); + + Assert.False(callback(new StructWithShortAndBool(s, b), s, b)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void StructWithDefaultNonBlittableFields_MarshalAsInfo() + { + short s = 41; + bool b = true; + + var callback = Marshal.GetDelegateForFunctionPointer((IntPtr)DisabledRuntimeMarshallingNative.GetStructWithShortAndBoolWithVariantBoolCallback()); + + Assert.True(callback(new StructWithShortAndBoolWithMarshalAs(s, b), s, b)); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/PInvokes.cs b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/PInvokes.cs new file mode 100644 index 00000000000000..69f91b23917070 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/PInvokes.cs @@ -0,0 +1,96 @@ +// 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.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingEnabled; + +public class PInvokes +{ + public static bool IsWindowsX86Process => OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.X86; + public static bool IsNotWindowsX86Process => !IsWindowsX86Process; + + [ConditionalFact(nameof(IsNotWindowsX86Process))] + public static void StructWithDefaultNonBlittableFields_Bool() + { + short s = 42; + bool b = true; + + // By default, bool is a 4-byte Windows BOOL, which will cause us to incorrectly marshal back the struct from native code. + Assert.False(DisabledRuntimeMarshallingNative.CheckStructWithShortAndBool(new StructWithShortAndBool(s, b), s, b)); + } + + [Fact] + [SkipOnMono("Mono doesn't support marshalling a .NET Char to a C char (only a char16_t).")] + public static void StructWithDefaultNonBlittableFields_Char() + { + short s = 42; + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.False(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithWCharAndShort(s, c), s, c)); + + } + + [ConditionalFact(nameof(IsNotWindowsX86Process))] + public static void StructWithDefaultNonBlittableFields_Bool_MarshalAsInfo() + { + short s = 41; + bool b = true; + + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithShortAndBool(new StructWithShortAndBoolWithMarshalAs(s, b), s, b)); + } + + [Fact] + [SkipOnMono("Mono doesn't support marshalling a .NET Char to a C char (only a char16_t).")] + public static void StructWithDefaultNonBlittableFields_Char_MarshalAsInfo() + { + short s = 41; + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.False(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithWCharAndShortWithMarshalAs(s, c), s, c)); + } + + [ConditionalFact(nameof(IsWindowsX86Process))] + public static void EntryPoint_With_StructWithDefaultNonBlittableFields_NotFound() + { + short s = 42; + bool b = true; + + // By default, bool is a 4-byte Windows BOOL, which will make the calculation of stack space for the stdcall calling convention + // incorrect, causing the entry point to not be found. + Assert.Throws(() => DisabledRuntimeMarshallingNative.CheckStructWithShortAndBool(new StructWithShortAndBool(s, b), s, b)); + } + + [Fact] + [SkipOnMono("Mono supports non-blittable generic instantiations in P/Invokes")] + public static void StructWithNonBlittableGenericInstantiation_Fails() + { + short s = 41; + char c = '✅'; + Assert.Throws(() => DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithShortAndGeneric(s, c), s, c)); + } + + [Fact] + public static void StructWithBlittableGenericInstantiation() + { + short s = 42; + // We use a the "green check mark" character so that we use both bytes and + // have a value that can't be accidentally round-tripped. + char c = '✅'; + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithWCharAndShort(new StructWithShortAndGeneric(s, (short)c), s, (short)c)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void StructWithDefaultNonBlittableFields_MarshalAsInfo_WindowsOnly() + { + short s = 41; + bool b = true; + Assert.True(DisabledRuntimeMarshallingNative.CheckStructWithShortAndBoolWithVariantBool(new StructWithShortAndBoolWithMarshalAs(s, b), s, b)); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/UnmanagedCallersOnly.cs b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/UnmanagedCallersOnly.cs new file mode 100644 index 00000000000000..6f94ad67f6b975 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/PInvokeAssemblyMarshallingEnabled/UnmanagedCallersOnly.cs @@ -0,0 +1,27 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; +using static DisabledRuntimeMarshallingNative; + +namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled; + +public unsafe class UnmanagedCallersOnly +{ + + [Fact] + public static void UnmanagedCallersOnly_Defined_InDisabledAssembly_WithNonBlittableParameters_Fails() + { + short s = 41; + bool b = true; + + Assert.Throws(() => + { + delegate* unmanaged cb = &DisabledRuntimeMarshallingNative.CheckStructWithShortAndBoolManaged; + cb(new StructWithShortAndBool(s, b), s, b); + }); + } +} diff --git a/src/tests/Interop/DisabledRuntimeMarshalling/RuntimeMarshallingDisabledAttribute.cs b/src/tests/Interop/DisabledRuntimeMarshalling/RuntimeMarshallingDisabledAttribute.cs new file mode 100644 index 00000000000000..63834880490378 --- /dev/null +++ b/src/tests/Interop/DisabledRuntimeMarshalling/RuntimeMarshallingDisabledAttribute.cs @@ -0,0 +1,5 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + +[assembly:DisableRuntimeMarshalling] diff --git a/src/tests/Interop/common/xplatform.h b/src/tests/Interop/common/xplatform.h index cb86b1559fec57..00f9a08b12e632 100644 --- a/src/tests/Interop/common/xplatform.h +++ b/src/tests/Interop/common/xplatform.h @@ -155,6 +155,16 @@ typedef struct tagDEC { #define FALSE 0 #endif +typedef USHORT VARIANT_BOOL; + +#ifndef VARIANT_TRUE +#define VARIANT_TRUE -1 +#endif + +#ifndef VARIANT_FALSE +#define VARIANT_FALSE 0 +#endif + #ifndef __IUnknown_INTERFACE_DEFINED__ #define __IUnknown_INTERFACE_DEFINED__ @@ -175,12 +185,12 @@ IUnknown #endif // __IUnknown_INTERFACE_DEFINED__ -typedef /* [v1_enum] */ +typedef /* [v1_enum] */ enum TrustLevel { BaseTrust = 0, PartialTrust = ( BaseTrust + 1 ) , - FullTrust = ( PartialTrust + 1 ) + FullTrust = ( PartialTrust + 1 ) } TrustLevel; // AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90 diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 22eb44b80f59f1..7db70d2d030f9a 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -2795,6 +2795,10 @@ https://github.com/dotnet/runtime/issues/57512 + + + This test includes an intentionally-invalid UnmanagedCallersOnly method. Invalid UnmanagedCallersOnly methods cause failures at AOT-time. +