Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert stream override checks to QCall #107117

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
41 changes: 41 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,41 @@
// 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 = "StreamNative_HasOverriddenSlow")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool HasOverriddenSlow(MethodTable* pMT, [MarshalAs(UnmanagedType.Bool)] bool isRead);

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

[MethodImpl(MethodImplOptions.NoInlining)]
private static bool HasOverriddenWriteSlow(MethodTable* pMT) => HasOverriddenSlow(pMT, isRead: false);

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

private bool HasOverriddenBeginEndWrite()
{
MethodTable* pMT = RuntimeHelpers.GetMethodTable(this);
bool res = pMT->AuxiliaryData->HasCheckedStreamOverride
? pMT->AuxiliaryData->IsStreamOverriddenWrite
: HasOverriddenWriteSlow(pMT);
GC.KeepAlive(this);
return res;
}
}
}
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();
}
}
50 changes: 20 additions & 30 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1898,46 +1898,36 @@ static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot)
return true;
}

FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream)
extern "C" BOOL QCALLTYPE StreamNative_HasOverriddenSlow(MethodTable* pMT, BOOL isRead)
{
FCALL_CONTRACT;
QCALL_CONTRACT;
_ASSERTE(pMT != NULL);

if (stream == NULL)
FC_RETURN_BOOL(TRUE);
BOOL readOverride = FALSE;
BOOL writeOverride = FALSE;

if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0)
BEGIN_QCALL;

// Check for needed details
if (g_pStreamMT == NULL
|| g_slotBeginRead == 0
|| g_slotEndRead == 0
|| g_slotBeginWrite == 0
|| g_slotEndWrite == 0)
{
HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
g_pStreamMT = CoreLibBinder::GetClass(CLASS__STREAM);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
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();

FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead));
}
FCIMPLEND

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

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

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();
}

MethodTable * pMT = stream->GetMethodTable();
// Check the current type and update state.
readOverride = HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead);
writeOverride = HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite);
pMT->GetAuxiliaryDataForWrite()->SetStreamOverrideState(readOverride, writeOverride);

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 StreamNative_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 @@ -458,11 +458,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 @@ -509,7 +504,6 @@ FCClassElement("RuntimeTypeHandle", "System", gCOMTypeHandleFuncs)

FCClassElement("Signature", "System", gSignatureNative)
FCClassElement("StackTrace", "System.Diagnostics", gDiagnosticsStackTrace)
FCClassElement("Stream", "System.IO", gStreamFuncs)
FCClassElement("String", "System", gStringFuncs)
FCClassElement("StubHelpers", "System.StubHelpers", gStubHelperFuncs)
FCClassElement("Thread", "System.Threading", gThreadFuncs)
Expand Down
61 changes: 37 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,17 @@ 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 +407,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 +489,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 +528,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 +537,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 +3061,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
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ static const Entry s_QCall[] =
DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode)
DllImportEntry(TypeHandle_CanCastTo_NoCacheLookup)
DllImportEntry(ValueType_GetHashCodeStrategy)
DllImportEntry(StreamNative_HasOverriddenSlow)
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
DllImportEntry(RuntimeTypeHandle_MakePointer)
DllImportEntry(RuntimeTypeHandle_MakeByRef)
DllImportEntry(RuntimeTypeHandle_MakeSZArray)
Expand Down
10 changes: 1 addition & 9 deletions src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace System.IO
{
public abstract class Stream : MarshalByRefObject, IDisposable, IAsyncDisposable
public abstract partial class Stream : MarshalByRefObject, IDisposable, IAsyncDisposable
{
public static readonly Stream Null = new NullStream();

Expand Down Expand Up @@ -447,14 +447,6 @@ private async ValueTask<int> ReadAtLeastAsyncCore(Memory<byte> buffer, int minim
return totalRead;
}

[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndRead();

[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndWrite();

private Task<int> BeginEndReadAsync(byte[] buffer, int offset, int count)
{
if (!HasOverriddenBeginEndRead())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Tracing\EventPipe.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Tracing\NativeRuntimeEventSource.Threading.NativeSinks.Mono.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileLoadException.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\Stream.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Assembly.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\AssemblyName.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInvoker.Mono.cs" />
Expand Down
Loading
Loading