From 3e435f83f62b2ff3ebd037fea4def863490d84f6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Apr 2021 13:38:42 -0700 Subject: [PATCH 1/2] [release/5.0] Use adjusted parent layout size to handle a non-zero-sized class inheriting from a zero-sized class that inherits from another zero-sized class. Fixes handling of zero-sized parent types that have zero-sized parent types. --- src/coreclr/src/vm/classlayoutinfo.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/classlayoutinfo.cpp b/src/coreclr/src/vm/classlayoutinfo.cpp index bdef0e40596f34..aa282e74d22fc3 100644 --- a/src/coreclr/src/vm/classlayoutinfo.cpp +++ b/src/coreclr/src/vm/classlayoutinfo.cpp @@ -690,14 +690,13 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( } BYTE parentManagedAlignmentRequirement = 0; - UINT32 parentSize = pParentMT->GetNumInstanceFieldBytes(); if (pParentMT && (pParentMT->IsManagedSequential() || (pParentMT->GetClass()->HasExplicitFieldOffsetLayout() && pParentMT->IsBlittable()))) { parentManagedAlignmentRequirement = pParentLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; } CalculateSizeAndFieldOffsets( - parentSize, + cbAdjustedParentLayoutNativeSize, cInstanceFields, fExplicitOffsets, pSortArray.Ptr(), From b4e78ed0222ad86881cc9694839cf3bc3ee73d1b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 9 Apr 2021 11:26:45 -0700 Subject: [PATCH 2/2] Revert "[release/5.0] When marshalling a layout class, fall-back to dynamically marshalling the type if it doesn't match the static type in the signature. (#50138)" (#50883) (#51021) This reverts commit b71bb5971cc204fee3f482843ad26ebc7bc212a9. --- src/coreclr/src/vm/corelib.h | 5 + src/coreclr/src/vm/ilmarshalers.cpp | 129 +++++++++++++++++- src/coreclr/src/vm/ilmarshalers.h | 1 + src/coreclr/src/vm/metasig.h | 4 + src/coreclr/src/vm/mlinfo.cpp | 15 +- .../Interop/LayoutClass/LayoutClassNative.cpp | 32 +++-- .../Interop/LayoutClass/LayoutClassTest.cs | 113 ++++++++++++++- 7 files changed, 276 insertions(+), 23 deletions(-) diff --git a/src/coreclr/src/vm/corelib.h b/src/coreclr/src/vm/corelib.h index 75d13d2e52985d..ad892439138409 100644 --- a/src/coreclr/src/vm/corelib.h +++ b/src/coreclr/src/vm/corelib.h @@ -488,6 +488,11 @@ DEFINE_METHOD(MARSHAL, ALLOC_CO_TASK_MEM, AllocCoTa DEFINE_METHOD(MARSHAL, FREE_CO_TASK_MEM, FreeCoTaskMem, SM_IntPtr_RetVoid) DEFINE_FIELD(MARSHAL, SYSTEM_MAX_DBCS_CHAR_SIZE, SystemMaxDBCSCharSize) +DEFINE_METHOD(MARSHAL, STRUCTURE_TO_PTR, StructureToPtr, SM_Obj_IntPtr_Bool_RetVoid) +DEFINE_METHOD(MARSHAL, PTR_TO_STRUCTURE, PtrToStructure, SM_IntPtr_Obj_RetVoid) +DEFINE_METHOD(MARSHAL, DESTROY_STRUCTURE, DestroyStructure, SM_IntPtr_Type_RetVoid) +DEFINE_METHOD(MARSHAL, SIZEOF_TYPE, SizeOf, SM_Type_RetInt) + DEFINE_CLASS(NATIVELIBRARY, Interop, NativeLibrary) DEFINE_METHOD(NATIVELIBRARY, LOADLIBRARYCALLBACKSTUB, LoadLibraryCallbackStub, SM_Str_AssemblyBase_Bool_UInt_RetIntPtr) diff --git a/src/coreclr/src/vm/ilmarshalers.cpp b/src/coreclr/src/vm/ilmarshalers.cpp index 0e74014a45f158..f224d7c477c7a4 100644 --- a/src/coreclr/src/vm/ilmarshalers.cpp +++ b/src/coreclr/src/vm/ilmarshalers.cpp @@ -2149,14 +2149,30 @@ void ILLayoutClassPtrMarshalerBase::EmitConvertSpaceCLRToNative(ILCodeStream* ps EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* pTypeMismatchedLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, pTypeMismatchedLabel); + DWORD sizeLocal = pslILEmit->NewLocal(LocalDesc(ELEMENT_TYPE_I4)); + pslILEmit->EmitLDC(uNativeSize); + if (emittedTypeCheck) + { + ILCodeLabel* pHaveSizeLabel = pslILEmit->NewCodeLabel(); + pslILEmit->EmitBR(pHaveSizeLabel); + pslILEmit->EmitLabel(pTypeMismatchedLabel); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitCALL(METHOD__MARSHAL__SIZEOF_TYPE, 1, 1); + pslILEmit->EmitLabel(pHaveSizeLabel); + } + pslILEmit->EmitSTLOC(sizeLocal); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitCALL(METHOD__MARSHAL__ALLOC_CO_TASK_MEM, 1, 1); pslILEmit->EmitDUP(); // for INITBLK EmitStoreNativeValue(pslILEmit); // initialize local block we just allocated pslILEmit->EmitLDC(0); - pslILEmit->EmitLDC(uNativeSize); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitINITBLK(); pslILEmit->EmitLabel(pNullRefLabel); @@ -2180,15 +2196,30 @@ void ILLayoutClassPtrMarshalerBase::EmitConvertSpaceCLRToNativeTemp(ILCodeStream EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* pTypeMismatchedLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, pTypeMismatchedLabel); + DWORD sizeLocal = pslILEmit->NewLocal(LocalDesc(ELEMENT_TYPE_I4)); pslILEmit->EmitLDC(uNativeSize); + if (emittedTypeCheck) + { + ILCodeLabel* pHaveSizeLabel = pslILEmit->NewCodeLabel(); + pslILEmit->EmitBR(pHaveSizeLabel); + pslILEmit->EmitLabel(pTypeMismatchedLabel); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitCALL(METHOD__MARSHAL__SIZEOF_TYPE, 1, 1); + pslILEmit->EmitLabel(pHaveSizeLabel); + } + pslILEmit->EmitSTLOC(sizeLocal); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitLOCALLOC(); pslILEmit->EmitDUP(); // for INITBLK EmitStoreNativeValue(pslILEmit); // initialize local block we just allocated pslILEmit->EmitLDC(0); - pslILEmit->EmitLDC(uNativeSize); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitINITBLK(); pslILEmit->EmitLabel(pNullRefLabel); @@ -2264,7 +2295,24 @@ void ILLayoutClassPtrMarshalerBase::EmitClearNativeTemp(ILCodeStream* pslILEmit) } } +bool ILLayoutClassPtrMarshalerBase::EmitExactTypeCheck(ILCodeStream* pslILEmit, ILCodeLabel* isNotMatchingTypeLabel) +{ + STANDARD_VM_CONTRACT; + if (m_pargs->m_pMT->IsSealed()) + { + // If the provided type cannot be derived from, then we don't need to emit the type check. + return false; + } + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitLDTOKEN(pslILEmit->GetToken(m_pargs->m_pMT)); + pslILEmit->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + pslILEmit->EmitCALLVIRT(pslILEmit->GetToken(CoreLibBinder::GetMethod(METHOD__OBJECT__EQUALS)), 1, 1); + pslILEmit->EmitBRFALSE(isNotMatchingTypeLabel); + + return true; +} void ILLayoutClassPtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit) { @@ -2281,6 +2329,9 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* psl pslILEmit->EmitLDC(uNativeSize); pslILEmit->EmitINITBLK(); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + MethodDesc* pStructMarshalStub = NDirect::CreateStructMarshalILStub(m_pargs->m_pMT); EmitLoadManagedValue(pslILEmit); @@ -2290,6 +2341,18 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* psl EmitLoadCleanupWorkList(pslILEmit); pslILEmit->EmitCALL(pslILEmit->GetToken(pStructMarshalStub), 4, 0); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadManagedValue(pslILEmit); + EmitLoadNativeValue(pslILEmit); + pslILEmit->EmitLDC(0); + pslILEmit->EmitCALL(METHOD__MARSHAL__STRUCTURE_TO_PTR, 3, 0); + } + pslILEmit->EmitLabel(pNullRefLabel); } @@ -2302,6 +2365,9 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* psl EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + MethodDesc* pStructMarshalStub = NDirect::CreateStructMarshalILStub(m_pargs->m_pMT); EmitLoadManagedValue(pslILEmit); @@ -2311,6 +2377,15 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* psl EmitLoadCleanupWorkList(pslILEmit); pslILEmit->EmitCALL(pslILEmit->GetToken(pStructMarshalStub), 4, 0); + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__MARSHAL__PTR_TO_STRUCTURE, 2, 0); + } pslILEmit->EmitLabel(pNullRefLabel); } @@ -2318,6 +2393,10 @@ void ILLayoutClassPtrMarshaler::EmitClearNativeContents(ILCodeStream * pslILEmit { STANDARD_VM_CONTRACT; + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + ILCodeLabel* cleanedUpLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + MethodDesc* pStructMarshalStub = NDirect::CreateStructMarshalILStub(m_pargs->m_pMT); EmitLoadManagedValue(pslILEmit); @@ -2327,6 +2406,19 @@ void ILLayoutClassPtrMarshaler::EmitClearNativeContents(ILCodeStream * pslILEmit EmitLoadCleanupWorkList(pslILEmit); pslILEmit->EmitCALL(pslILEmit->GetToken(pStructMarshalStub), 4, 0); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(cleanedUpLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitCALL(METHOD__MARSHAL__DESTROY_STRUCTURE, 2, 0); + } + + pslILEmit->EmitLabel(cleanedUpLabel); } @@ -2341,6 +2433,9 @@ void ILBlittablePtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslIL EmitLoadNativeValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); // dest EmitLoadManagedValue(pslILEmit); @@ -2349,6 +2444,17 @@ void ILBlittablePtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslIL pslILEmit->EmitLDC(uNativeSize); // size pslILEmit->EmitCPBLK(); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadManagedValue(pslILEmit); + EmitLoadNativeValue(pslILEmit); + pslILEmit->EmitLDC(0); + pslILEmit->EmitCALL(METHOD__MARSHAL__STRUCTURE_TO_PTR, 3, 0); + } pslILEmit->EmitLabel(pNullRefLabel); } @@ -2363,6 +2469,9 @@ void ILBlittablePtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslIL EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + EmitLoadManagedValue(pslILEmit); pslILEmit->EmitLDFLDA(fieldDef); // dest @@ -2371,12 +2480,26 @@ void ILBlittablePtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslIL pslILEmit->EmitLDC(uNativeSize); // size pslILEmit->EmitCPBLK(); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__MARSHAL__PTR_TO_STRUCTURE, 2, 0); + } + pslILEmit->EmitLabel(pNullRefLabel); } bool ILBlittablePtrMarshaler::CanMarshalViaPinning() { - return IsCLRToNative(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags) && !IsFieldMarshal(m_dwMarshalFlags); + return IsCLRToNative(m_dwMarshalFlags) && + !IsByref(m_dwMarshalFlags) && + !IsFieldMarshal(m_dwMarshalFlags) && + m_pargs->m_pMT->IsSealed(); // We can't marshal via pinning if we might need to marshal differently at runtime. See calls to EmitExactTypeCheck where we check the runtime type of the object being marshalled. } void ILBlittablePtrMarshaler::EmitMarshalViaPinning(ILCodeStream* pslILEmit) diff --git a/src/coreclr/src/vm/ilmarshalers.h b/src/coreclr/src/vm/ilmarshalers.h index c08f4f3efc49be..21b552eb76f780 100644 --- a/src/coreclr/src/vm/ilmarshalers.h +++ b/src/coreclr/src/vm/ilmarshalers.h @@ -2915,6 +2915,7 @@ class ILLayoutClassPtrMarshalerBase : public ILMarshaler bool NeedsClearNative() override; void EmitClearNative(ILCodeStream* pslILEmit) override; void EmitClearNativeTemp(ILCodeStream* pslILEmit) override; + bool EmitExactTypeCheck(ILCodeStream* pslILEmit, ILCodeLabel* isNotMatchingTypeLabel); }; class ILLayoutClassPtrMarshaler : public ILLayoutClassPtrMarshalerBase diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index 179dcd206378e5..4915f7d6b0fa4f 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -604,6 +604,10 @@ DEFINE_METASIG_T(SM(Array_Int_Array_Int_Int_RetVoid, C(ARRAY) i C(ARRAY) i i, v) DEFINE_METASIG_T(SM(Array_Int_Obj_RetVoid, C(ARRAY) i j, v)) DEFINE_METASIG_T(SM(Array_Int_PtrVoid_RetRefObj, C(ARRAY) i P(v), r(j))) +DEFINE_METASIG(SM(Obj_IntPtr_Bool_RetVoid, j I F, v)) +DEFINE_METASIG(SM(IntPtr_Obj_RetVoid, I j, v)) +DEFINE_METASIG_T(SM(IntPtr_Type_RetVoid, I C(TYPE), v)) + // Undefine macros in case we include the file again in the compilation unit #undef DEFINE_METASIG diff --git a/src/coreclr/src/vm/mlinfo.cpp b/src/coreclr/src/vm/mlinfo.cpp index 36b07edcfce33c..3606df2c84874a 100644 --- a/src/coreclr/src/vm/mlinfo.cpp +++ b/src/coreclr/src/vm/mlinfo.cpp @@ -1231,6 +1231,8 @@ MarshalInfo::MarshalInfo(Module* pModule, m_pMT = NULL; m_pMD = pMD; m_onInstanceMethod = onInstanceMethod; + // [Compat] For backward compatibility reasons, some marshalers imply [In, Out] behavior when marked as [In], [Out], or not marked with either. + BOOL byValAlwaysInOut = FALSE; #ifdef FEATURE_COMINTEROP m_fDispItf = FALSE; @@ -2007,6 +2009,7 @@ MarshalInfo::MarshalInfo(Module* pModule, } m_type = IsFieldScenario() ? MARSHAL_TYPE_BLITTABLE_LAYOUTCLASS : MARSHAL_TYPE_BLITTABLEPTR; m_args.m_pMT = m_pMT; + byValAlwaysInOut = TRUE; } else if (m_pMT->HasLayout()) { @@ -2514,10 +2517,16 @@ MarshalInfo::MarshalInfo(Module* pModule, } } - // If neither IN nor OUT are true, this signals the URT to use the default - // rules. - if (!m_in && !m_out) + if (!m_byref && byValAlwaysInOut) { + // Some marshalers expect [In, Out] behavior with [In], [Out], or no directional attributes. + m_in = TRUE; + m_out = TRUE; + } + else if (!m_in && !m_out) + { + // If neither IN nor OUT are true, this signals the URT to use the default + // rules. if (m_byref || (mtype == ELEMENT_TYPE_CLASS && !(sig.IsStringType(pModule, pTypeContext)) diff --git a/src/tests/Interop/LayoutClass/LayoutClassNative.cpp b/src/tests/Interop/LayoutClass/LayoutClassNative.cpp index 0bf0af967f0637..59c4645afba321 100644 --- a/src/tests/Interop/LayoutClass/LayoutClassNative.cpp +++ b/src/tests/Interop/LayoutClass/LayoutClassNative.cpp @@ -5,7 +5,16 @@ #include typedef void *voidPtr; - + +struct EmptyBase +{ +}; + +struct DerivedSeqClass : public EmptyBase +{ + int a; +}; + struct SeqClass { int a; @@ -14,7 +23,7 @@ struct SeqClass }; struct ExpClass -{ +{ int a; int padding; //padding needs to be added here as we have added 8 byte offset. union @@ -47,32 +56,35 @@ DLL_EXPORT BOOL STDMETHODCALLTYPE SimpleSeqLayoutClassByRef(SeqClass* p) } extern "C" -DLL_EXPORT BOOL STDMETHODCALLTYPE SimpleExpLayoutClassByRef(ExpClass* p) +DLL_EXPORT BOOL STDMETHODCALLTYPE DerivedSeqLayoutClassByRef(EmptyBase* p, int expected) { - if((p->a != 0) || (p->udata.i != 10)) + if(((DerivedSeqClass*)p)->a != expected) { - printf("FAIL: p->a=%d, p->udata.i=%d\n",p->a,p->udata.i); + printf("FAIL: p->a=%d, expected %d\n", ((DerivedSeqClass*)p)->a, expected); return FALSE; } return TRUE; } extern "C" -DLL_EXPORT BOOL STDMETHODCALLTYPE SimpleBlittableSeqLayoutClassByRef(BlittableClass* p) +DLL_EXPORT BOOL STDMETHODCALLTYPE SimpleExpLayoutClassByRef(ExpClass* p) { - if(p->a != 10) + if((p->a != 0) || (p->udata.i != 10)) { - printf("FAIL: p->a=%d\n", p->a); + printf("FAIL: p->a=%d, p->udata.i=%d\n",p->a,p->udata.i); return FALSE; } return TRUE; } extern "C" -DLL_EXPORT BOOL STDMETHODCALLTYPE SimpleBlittableSeqLayoutClassByOutAttr(BlittableClass* p) +DLL_EXPORT BOOL STDMETHODCALLTYPE SimpleBlittableSeqLayoutClass_UpdateField(BlittableClass* p) { - if(!SimpleBlittableSeqLayoutClassByRef(p)) + if(p->a != 10) + { + printf("FAIL: p->a=%d\n", p->a); return FALSE; + } p->a++; return TRUE; diff --git a/src/tests/Interop/LayoutClass/LayoutClassTest.cs b/src/tests/Interop/LayoutClass/LayoutClassTest.cs index 32ecfcb3548f93..81048a22b332d8 100644 --- a/src/tests/Interop/LayoutClass/LayoutClassTest.cs +++ b/src/tests/Interop/LayoutClass/LayoutClassTest.cs @@ -8,7 +8,23 @@ namespace PInvokeTests { [StructLayout(LayoutKind.Sequential)] - public class SeqClass + public class EmptyBase + { + } + + [StructLayout(LayoutKind.Sequential)] + public class SeqDerivedClass : EmptyBase + { + public int a; + + public SeqDerivedClass(int _a) + { + a = _a; + } + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class SeqClass { public int a; public bool b; @@ -78,6 +94,17 @@ public Blittable(int _a) } } + [StructLayout(LayoutKind.Sequential)] + public sealed class SealedBlittable + { + public int a; + + public SealedBlittable(int _a) + { + a = _a; + } + } + public struct NestedLayout { public SeqClass value; @@ -96,18 +123,35 @@ public struct RecursiveTestStruct class StructureTests { + private const string SimpleBlittableSeqLayoutClass_UpdateField = nameof(SimpleBlittableSeqLayoutClass_UpdateField); + [DllImport("LayoutClassNative")] private static extern bool SimpleSeqLayoutClassByRef(SeqClass p); [DllImport("LayoutClassNative")] - private static extern bool SimpleExpLayoutClassByRef(ExpClass p); + private static extern bool DerivedSeqLayoutClassByRef(EmptyBase p, int expected); [DllImport("LayoutClassNative")] + private static extern bool SimpleExpLayoutClassByRef(ExpClass p); + + [DllImport("LayoutClassNative", EntryPoint = SimpleBlittableSeqLayoutClass_UpdateField)] private static extern bool SimpleBlittableSeqLayoutClassByRef(Blittable p); - [DllImport("LayoutClassNative")] + [DllImport("LayoutClassNative", EntryPoint = SimpleBlittableSeqLayoutClass_UpdateField)] + private static extern bool SimpleBlittableSeqLayoutClassByInAttr([In] Blittable p); + + [DllImport("LayoutClassNative", EntryPoint = SimpleBlittableSeqLayoutClass_UpdateField)] private static extern bool SimpleBlittableSeqLayoutClassByOutAttr([Out] Blittable p); + [DllImport("LayoutClassNative", EntryPoint = SimpleBlittableSeqLayoutClass_UpdateField)] + private static extern bool SealedBlittableSeqLayoutClassByRef(SealedBlittable p); + + [DllImport("LayoutClassNative", EntryPoint = SimpleBlittableSeqLayoutClass_UpdateField)] + private static extern bool SealedBlittableSeqLayoutClassByInAttr([In] SealedBlittable p); + + [DllImport("LayoutClassNative", EntryPoint = SimpleBlittableSeqLayoutClass_UpdateField)] + private static extern bool SealedBlittableSeqLayoutClassByOutAttr([Out] SealedBlittable p); + [DllImport("LayoutClassNative")] private static extern bool SimpleNestedLayoutClassByValue(NestedLayout p); @@ -123,6 +167,15 @@ public static void SequentialClass() Assert.IsTrue(SimpleSeqLayoutClassByRef(p)); } + public static void DerivedClassWithEmptyBase() + { + Console.WriteLine($"Running {nameof(DerivedClassWithEmptyBase)}..."); + + string s = "before"; + var p = new SeqDerivedClass(42); + Assert.IsTrue(DerivedSeqLayoutClassByRef(p, 42)); + } + public static void ExplicitClass() { Console.WriteLine($"Running {nameof(ExplicitClass)}..."); @@ -131,25 +184,66 @@ public static void ExplicitClass() Assert.IsTrue(SimpleExpLayoutClassByRef(p)); } + private static void ValidateBlittableClassInOut(Func pinvoke) + { + int a = 10; + int expected = a + 1; + Blittable p = new Blittable(a); + Assert.IsTrue(pinvoke(p)); + Assert.AreEqual(expected, p.a); + } + public static void BlittableClass() { + // [Compat] Marshalled with [In, Out] behaviour by default Console.WriteLine($"Running {nameof(BlittableClass)}..."); + ValidateBlittableClassInOut(SimpleBlittableSeqLayoutClassByRef); + } - Blittable p = new Blittable(10); - Assert.IsTrue(SimpleBlittableSeqLayoutClassByRef(p)); + public static void BlittableClassByInAttr() + { + // [Compat] Marshalled with [In, Out] behaviour even when only [In] is specified + Console.WriteLine($"Running {nameof(BlittableClassByInAttr)}..."); + ValidateBlittableClassInOut(SimpleBlittableSeqLayoutClassByInAttr); } public static void BlittableClassByOutAttr() { + // [Compat] Marshalled with [In, Out] behaviour even when only [Out] is specified Console.WriteLine($"Running {nameof(BlittableClassByOutAttr)}..."); + ValidateBlittableClassInOut(SimpleBlittableSeqLayoutClassByOutAttr); + } + private static void ValidateSealedBlittableClassInOut(Func pinvoke) + { int a = 10; int expected = a + 1; - Blittable p = new Blittable(a); - Assert.IsTrue(SimpleBlittableSeqLayoutClassByOutAttr(p)); + SealedBlittable p = new SealedBlittable(a); + Assert.IsTrue(pinvoke(p)); Assert.AreEqual(expected, p.a); } + public static void SealedBlittableClass() + { + // [Compat] Marshalled with [In, Out] behaviour by default + Console.WriteLine($"Running {nameof(SealedBlittableClass)}..."); + ValidateSealedBlittableClassInOut(SealedBlittableSeqLayoutClassByRef); + } + + public static void SealedBlittableClassByInAttr() + { + // [Compat] Marshalled with [In, Out] behaviour even when only [In] is specified + Console.WriteLine($"Running {nameof(SealedBlittableClassByOutAttr)}..."); + ValidateSealedBlittableClassInOut(SealedBlittableSeqLayoutClassByInAttr); + } + + public static void SealedBlittableClassByOutAttr() + { + // [Compat] Marshalled with [In, Out] behaviour even when only [Out] is specified + Console.WriteLine($"Running {nameof(SealedBlittableClassByOutAttr)}..."); + ValidateSealedBlittableClassInOut(SealedBlittableSeqLayoutClassByOutAttr); + } + public static void NestedLayoutClass() { Console.WriteLine($"Running {nameof(NestedLayoutClass)}..."); @@ -175,9 +269,14 @@ public static int Main(string[] argv) try { SequentialClass(); + DerivedClassWithEmptyBase(); ExplicitClass(); BlittableClass(); + SealedBlittableClass(); + BlittableClassByInAttr(); + SealedBlittableClassByInAttr(); BlittableClassByOutAttr(); + SealedBlittableClassByOutAttr(); NestedLayoutClass(); RecursiveNativeLayout(); }