Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
<Compile Include="$(BclSourcesRoot)\System\GC.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileLoadException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileNotFoundException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\Stream.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Math.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\MathF.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\MulticastDelegate.CoreCLR.cs" />
Expand Down
43 changes: 43 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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;
using System.Runtime.InteropServices;

namespace System.IO
{
public abstract unsafe partial class Stream
{
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Stream_HasOverriddenSlow")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool HasOverriddenSlow(MethodTable* pMT, [MarshalAs(UnmanagedType.Bool)] bool isRead);

private bool HasOverriddenBeginEndRead()
{
MethodTable* pMT = RuntimeHelpers.GetMethodTable(this);
bool res = pMT->AuxiliaryData->HasCheckedStreamOverride
? pMT->AuxiliaryData->IsStreamOverriddenRead
: HasOverriddenReadSlow(pMT);
GC.KeepAlive(this);
return res;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool HasOverriddenReadSlow(MethodTable* pMT)
=> HasOverriddenSlow(pMT, isRead: true);
}

private bool HasOverriddenBeginEndWrite()
{
MethodTable* pMT = RuntimeHelpers.GetMethodTable(this);
bool res = pMT->AuxiliaryData->HasCheckedStreamOverride
? pMT->AuxiliaryData->IsStreamOverriddenWrite
: HasOverriddenWriteSlow(pMT);
GC.KeepAlive(this);
return res;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool HasOverriddenWriteSlow(MethodTable* pMT)
=> HasOverriddenSlow(pMT, isRead: false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,10 @@ internal unsafe struct MethodTableAuxiliaryData
private const uint enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002; // Whether we have checked the overridden Equals or GetHashCode
private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004; // Is any field type or sub field type overridden Equals or GetHashCode

private const uint enum_flag_HasCheckedStreamOverride = 0x0400;
private const uint enum_flag_StreamOverriddenRead = 0x0800;
private const uint enum_flag_StreamOverriddenWrite = 0x1000;

public bool HasCheckedCanCompareBitsOrUseFastGetHashCode => (Flags & enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode) != 0;

public bool CanCompareBitsOrUseFastGetHashCode
Expand All @@ -824,6 +828,26 @@ public bool CanCompareBitsOrUseFastGetHashCode
}
}

public bool HasCheckedStreamOverride => (Flags & enum_flag_HasCheckedStreamOverride) != 0;

public bool IsStreamOverriddenRead
{
get
{
Debug.Assert(HasCheckedStreamOverride);
return (Flags & enum_flag_StreamOverriddenRead) != 0;
}
}

public bool IsStreamOverriddenWrite
{
get
{
Debug.Assert(HasCheckedStreamOverride);
return (Flags & enum_flag_StreamOverriddenWrite) != 0;
}
}

public RuntimeType? ExposedClassObject
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
<Compile Include="System\ComAwareWeakReference.NativeAot.cs" />
<Compile Include="System\InvokeUtils.cs" />
<Compile Include="System\IO\FileLoadException.NativeAot.cs" />
<Compile Include="System\IO\Stream.NativeAot.cs" />
<Compile Include="System\RuntimeMethodHandle.cs" />
<Compile Include="System\Diagnostics\Debug.NativeAot.cs" />
<Compile Include="System\Diagnostics\Debugger.cs" />
Expand Down Expand Up @@ -210,7 +211,7 @@
<Compile Include="System\Runtime\InteropServices\Marshal.NativeAot.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal.Com.cs" Condition="'$(FeatureCominterop)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\MemoryMarshal.NativeAot.cs" />
<Compile Include="System\Runtime\InteropServices\TrackerObjectManager.NativeAot.cs" Condition="'$(FeatureComWrappers)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\TrackerObjectManager.NativeAot.cs" Condition="'$(FeatureComWrappers)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\ObjectiveCMarshal.NativeAot.cs" Condition="'$(FeatureObjCMarshal)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\UnsafeGCHandle.cs" />
<Compile Include="System\Runtime\Intrinsics\X86\X86Base.NativeAot.cs" Condition="'$(SupportsX86Intrinsics)' == 'true'" />
Expand Down Expand Up @@ -286,7 +287,7 @@
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Ole32\Interop.CoGetContextToken.cs">
<Link>Interop\Windows\Ole32\Interop.CoGetContextToken.cs</Link>
</Compile>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\OleAut32\Interop.VariantClear.cs">
<Link>Interop\Windows\OleAut32\Interop.VariantClear.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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;

namespace System.IO
{
public abstract partial class Stream
{
[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndRead();

[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndWrite();
}
}
80 changes: 29 additions & 51 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1873,71 +1873,49 @@ extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo_NoCacheLookup(void* fromTypeHnd,
return ret;
}

static MethodTable * g_pStreamMT;
static WORD g_slotBeginRead, g_slotEndRead;
static WORD g_slotBeginWrite, g_slotEndWrite;

static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot)
static bool HasOverriddenStreamMethod(MethodTable* streamMT, MethodTable* pMT, WORD slot)
{
CONTRACTL{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(streamMT != NULL);
PRECONDITION(pMT != NULL);
} CONTRACTL_END;

PCODE actual = pMT->GetRestoredSlot(slot);
PCODE base = g_pStreamMT->GetRestoredSlot(slot);
if (actual == base)
return false;
PCODE base = streamMT->GetRestoredSlot(slot);

// If CoreLib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
// to detect match reliably
if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
// If the PCODEs match, then there is no override.
if (actual == base)
return false;

return true;
// If CoreLib is JITed, the slots can be patched and thus we need to compare
// the actual MethodDescs to detect match reliably.
return MethodTable::GetMethodDescForSlotAddress(actual) != MethodTable::GetMethodDescForSlotAddress(base);
}

FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream)
extern "C" BOOL QCALLTYPE Stream_HasOverriddenSlow(MethodTable* pMT, BOOL isRead)
{
FCALL_CONTRACT;

if (stream == NULL)
FC_RETURN_BOOL(TRUE);

if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0)
{
HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
g_pStreamMT = CoreLibBinder::GetClass(CLASS__STREAM);
g_slotBeginRead = CoreLibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot();
g_slotEndRead = CoreLibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot();
HELPER_METHOD_FRAME_END();
}

MethodTable * pMT = stream->GetMethodTable();
QCALL_CONTRACT;
_ASSERTE(pMT != NULL);

FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead));
}
FCIMPLEND
BOOL readOverride = FALSE;
BOOL writeOverride = FALSE;

FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndWrite, Object *stream)
{
FCALL_CONTRACT;
BEGIN_QCALL;

if (stream == NULL)
FC_RETURN_BOOL(TRUE);
MethodTable* pStreamMT = CoreLibBinder::GetClass(CLASS__STREAM);
WORD slotBeginRead = CoreLibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot();
WORD slotEndRead = CoreLibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot();
WORD slotBeginWrite = CoreLibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot();
WORD slotEndWrite = CoreLibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot();

if (g_pStreamMT == NULL || g_slotBeginWrite == 0 || g_slotEndWrite == 0)
{
HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
g_pStreamMT = CoreLibBinder::GetClass(CLASS__STREAM);
g_slotBeginWrite = CoreLibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot();
g_slotEndWrite = CoreLibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot();
HELPER_METHOD_FRAME_END();
}
// Check the current MethodTable for Stream overrides and set state on the MethodTable.
readOverride = HasOverriddenStreamMethod(pStreamMT, pMT, slotBeginRead) || HasOverriddenStreamMethod(pStreamMT, pMT, slotEndRead);
writeOverride = HasOverriddenStreamMethod(pStreamMT, pMT, slotBeginWrite) || HasOverriddenStreamMethod(pStreamMT, pMT, slotEndWrite);
pMT->GetAuxiliaryDataForWrite()->SetStreamOverrideState(readOverride, writeOverride);

MethodTable * pMT = stream->GetMethodTable();
END_QCALL;

FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite));
return isRead ? readOverride : writeOverride;
}
FCIMPLEND
8 changes: 2 additions & 6 deletions src/coreclr/vm/comutilnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,8 @@ extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodT
extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd);
extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize, MethodTable** fieldMT);

class StreamNative {
public:
static FCDECL1(FC_BOOL_RET, HasOverriddenBeginEndRead, Object *stream);
static FCDECL1(FC_BOOL_RET, HasOverriddenBeginEndWrite, Object *stream);
};

BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt);

extern "C" BOOL QCALLTYPE Stream_HasOverriddenSlow(MethodTable* pMT, BOOL isRead);

#endif // _COMUTILNATIVE_H_
6 changes: 0 additions & 6 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,6 @@ FCFuncStart(gGCHandleFuncs)
FCFuncElement("InternalCompareExchange", MarshalNative::GCHandleInternalCompareExchange)
FCFuncEnd()

FCFuncStart(gStreamFuncs)
FCFuncElement("HasOverriddenBeginEndRead", StreamNative::HasOverriddenBeginEndRead)
FCFuncElement("HasOverriddenBeginEndWrite", StreamNative::HasOverriddenBeginEndWrite)
FCFuncEnd()

FCFuncStart(gComAwareWeakReferenceFuncs)
FCFuncElement("HasInteropInfo", ComAwareWeakReferenceNative::HasInteropInfo)
FCFuncEnd()
Expand Down Expand Up @@ -502,7 +497,6 @@ FCClassElement("RuntimeType", "System", gSystem_RuntimeType)
FCClassElement("RuntimeTypeHandle", "System", gCOMTypeHandleFuncs)

FCClassElement("Signature", "System", gSignatureNative)
FCClassElement("Stream", "System.IO", gStreamFuncs)
FCClassElement("String", "System", gStringFuncs)
FCClassElement("StubHelpers", "System.StubHelpers", gStubHelperFuncs)
FCClassElement("Thread", "System.Threading", gThreadFuncs)
Expand Down
60 changes: 36 additions & 24 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,30 +326,23 @@ struct MethodTableAuxiliaryData
// TO BE UPDATED IN ORDER TO ENSURE THAT METHODTABLES DUPLICATED FOR GENERIC INSTANTIATIONS
// CARRY THE CORRECT INITIAL FLAGS.

enum_flag_Initialized = 0x0001,
enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002, // Whether we have checked the overridden Equals or GetHashCode
enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004, // Is any field type or sub field type overridden Equals or GetHashCode

enum_flag_Initialized = 0x0001,
enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002, // Whether we have checked the overridden Equals or GetHashCode
enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004, // Is any field type or sub field type overridden Equals or GetHashCode
enum_flag_IsTlsIndexAllocated = 0x0008,
enum_flag_HasApproxParent = 0x0010,
#ifdef _DEBUG
// The MethodTable is in the right state to be published, and will be inevitably.
// Currently DEBUG only as it does not affect behavior in any way in a release build
enum_flag_IsPublished = 0x0020,
#endif
enum_flag_MayHaveOpenInterfaceInInterfaceMap = 0x0020,
enum_flag_IsNotFullyLoaded = 0x0040,
enum_flag_DependenciesLoaded = 0x0080, // class and all dependencies loaded up to CLASS_LOADED_BUT_NOT_VERIFIED

enum_flag_IsInitError = 0x0100,
enum_flag_IsStaticDataAllocated = 0x0200, // When this is set, if the class can be marked as initialized without any further code execution it will be.
// unum_unused = 0x0400,
enum_flag_IsTlsIndexAllocated = 0x0800,
enum_flag_MayHaveOpenInterfaceInInterfaceMap = 0x1000,
// enum_unused = 0x2000,

#ifdef _DEBUG
enum_flag_ParentMethodTablePointerValid = 0x4000,
enum_flag_HasInjectedInterfaceDuplicates = 0x8000,
#endif
enum_flag_HasCheckedStreamOverride = 0x0400,
enum_flag_StreamOverriddenRead = 0x0800,
enum_flag_StreamOverriddenWrite = 0x1000,
// unused enum = 0x2000,
// unused enum = 0x4000,
// unused enum = 0x8000,
};
union
{
Expand All @@ -369,6 +362,16 @@ struct MethodTableAuxiliaryData
RUNTIMETYPEHANDLE m_hExposedClassObject;

#ifdef _DEBUG
enum
{
// The MethodTable is in the right state to be published, and will be inevitably.
// Currently DEBUG only as it does not affect behavior in any way in a release build
enum_flagDebug_IsPublished = 0x2000,
enum_flagDebug_ParentMethodTablePointerValid = 0x4000,
enum_flagDebug_HasInjectedInterfaceDuplicates = 0x8000,
};
DWORD m_dwFlagsDebug;

// to avoid verify same method table too many times when it's not changing, we cache the GC count
// on which the method table is verified. When fast GC STRESS is turned on, we only verify the MT if
// current GC count is bigger than the number. Note most thing which will invalidate a MT will require a
Expand Down Expand Up @@ -403,13 +406,13 @@ struct MethodTableAuxiliaryData
{
LIMITED_METHOD_DAC_CONTRACT;

return (m_dwFlags & enum_flag_ParentMethodTablePointerValid);
return (m_dwFlagsDebug & enum_flagDebug_ParentMethodTablePointerValid);
}
inline void SetParentMethodTablePointerValid()
{
LIMITED_METHOD_CONTRACT;

m_dwFlags |= enum_flag_ParentMethodTablePointerValid;
m_dwFlagsDebug |= enum_flagDebug_ParentMethodTablePointerValid;
}
#endif

Expand Down Expand Up @@ -485,6 +488,15 @@ struct MethodTableAuxiliaryData
}
#endif

inline void SetStreamOverrideState(BOOL read, BOOL write)
{
LONG streamOverride =
enum_flag_HasCheckedStreamOverride
| (read ? enum_flag_StreamOverriddenRead : 0)
| (write ? enum_flag_StreamOverriddenWrite : 0);
InterlockedOr((LONG*)&m_dwFlags, streamOverride);
}

inline RUNTIMETYPEHANDLE GetExposedClassObjectHandle() const
{
LIMITED_METHOD_CONTRACT;
Expand Down Expand Up @@ -515,7 +527,7 @@ struct MethodTableAuxiliaryData
void SetIsPublished()
{
LIMITED_METHOD_CONTRACT;
m_dwFlags |= (MethodTableAuxiliaryData::enum_flag_IsPublished);
m_dwFlagsDebug |= (MethodTableAuxiliaryData::enum_flagDebug_IsPublished);
}
#endif

Expand All @@ -524,7 +536,7 @@ struct MethodTableAuxiliaryData
bool IsPublished() const
{
LIMITED_METHOD_CONTRACT;
return (VolatileLoad(&m_dwFlags) & enum_flag_IsPublished);
return (VolatileLoad(&m_dwFlagsDebug) & enum_flagDebug_IsPublished);
}
#endif // _DEBUG

Expand Down Expand Up @@ -3048,12 +3060,12 @@ public :
inline BOOL Debug_HasInjectedInterfaceDuplicates() const
{
LIMITED_METHOD_CONTRACT;
return (GetAuxiliaryData()->m_dwFlags & MethodTableAuxiliaryData::enum_flag_HasInjectedInterfaceDuplicates) != 0;
return (GetAuxiliaryData()->m_dwFlagsDebug & MethodTableAuxiliaryData::enum_flagDebug_HasInjectedInterfaceDuplicates) != 0;
}
inline void Debug_SetHasInjectedInterfaceDuplicates()
{
LIMITED_METHOD_CONTRACT;
GetAuxiliaryDataForWrite()->m_dwFlags |= MethodTableAuxiliaryData::enum_flag_HasInjectedInterfaceDuplicates;
GetAuxiliaryDataForWrite()->m_dwFlagsDebug |= MethodTableAuxiliaryData::enum_flagDebug_HasInjectedInterfaceDuplicates;
}
#endif // _DEBUG

Expand Down
Loading