Skip to content

Commit

Permalink
Implement Stream.HasOverridenBeginEndXXX (#72252)
Browse files Browse the repository at this point in the history
Fixes dotnet/corert#3251.

`Stream.HasOverriddenBeginEndRead`/`Stream.HasOverriddenBeginEndWrite` are magic methods that call into the runtime on both CoreCLR and Mono to find out whether `Stream.BeginRead`/`EndRead`/`BeginWrite`/`EndWrite` are overriden on the current class.

Since we don't have a runtime in NativeAOT, implement this in the compiler. The answer to this question is expressible in IL: load function pointer to the method virtually and non-virtually and compare. I'm not calling into `FunctionPointerOps` because I can't imagine a scenario where this wouldn't work, but I can be persuaded to call to `FunctionPointerOps` to do the comparison.

Should make us pass all System.IO libraries tests.
  • Loading branch information
MichalStrehovsky authored Jul 16, 2022
1 parent 96cac6b commit fae5314
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 18 deletions.
6 changes: 6 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method)
}
}
break;
case "Stream":
{
if (owningType.Namespace == "System.IO")
return StreamIntrinsics.EmitIL(method);
}
break;
}

return null;
Expand Down
56 changes: 56 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/StreamIntrinsics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace Internal.IL.Stubs
{
/// <summary>
/// Provides method bodies for System.IO.Stream intrinsics.
/// </summary>
public static class StreamIntrinsics
{
public static MethodIL EmitIL(MethodDesc method)
{
Debug.Assert(((MetadataType)method.OwningType).Name == "Stream");

bool isRead = method.Name == "HasOverriddenBeginEndRead";
if (!isRead && method.Name != "HasOverriddenBeginEndWrite")
return null;

TypeDesc streamClass = method.OwningType;
MethodDesc beginMethod = streamClass.GetMethod(isRead ? "BeginRead" : "BeginWrite", null);
MethodDesc endMethod = streamClass.GetMethod(isRead ? "EndRead" : "EndWrite", null);

ILEmitter emitter = new ILEmitter();
ILCodeStream codestream = emitter.NewCodeStream();

ILCodeLabel lOverriden = emitter.NewCodeLabel();

ILToken beginMethodToken = emitter.NewToken(beginMethod);
codestream.EmitLdArg(0);
codestream.Emit(ILOpcode.ldvirtftn, beginMethodToken);
codestream.Emit(ILOpcode.ldftn, beginMethodToken);
codestream.Emit(ILOpcode.bne_un, lOverriden);

ILToken endMethodToken = emitter.NewToken(endMethod);
codestream.EmitLdArg(0);
codestream.Emit(ILOpcode.ldvirtftn, endMethodToken);
codestream.Emit(ILOpcode.ldftn, endMethodToken);
codestream.Emit(ILOpcode.bne_un, lOverriden);

codestream.EmitLdc(0);
codestream.Emit(ILOpcode.ret);

codestream.EmitLabel(lOverriden);
codestream.EmitLdc(1);
codestream.Emit(ILOpcode.ret);

return emitter.Link(method);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder,

case ReadyToRunHelperId.ResolveVirtualFunction:
{
// Not tested
encoder.EmitINT3();

MethodDesc targetMethod = (MethodDesc)Target;
if (targetMethod.OwningType.IsInterface)
{
// Not tested
encoder.EmitINT3();

encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod));
encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
<Link>IL\Stubs\MethodBaseGetCurrentMethodThunk.Sorting.cs</Link>
</Compile>
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\RuntimeHelpersIntrinsics.cs" Link="IL\Stubs\RuntimeHelpersIntrinsics.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\StreamIntrinsics.cs" Link="IL\Stubs\StreamIntrinsics.cs" />
<Compile Include="..\..\Common\TypeSystem\IL\Stubs\TypeGetTypeMethodThunk.cs">
<Link>IL\Stubs\TypeGetTypeMethodThunk.cs</Link>
</Compile>
Expand Down
16 changes: 2 additions & 14 deletions src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,17 +449,13 @@ private async ValueTask<int> ReadAtLeastAsyncCore(Memory<byte> buffer, int minim
return totalRead;
}

#if NATIVEAOT // TODO: https://github.com/dotnet/corert/issues/3251
private static bool HasOverriddenBeginEndRead() => true;

private static bool HasOverriddenBeginEndWrite() => true;
#else
[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndRead();

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

private Task<int> BeginEndReadAsync(byte[] buffer, int offset, int count)
{
Expand Down Expand Up @@ -1227,9 +1223,6 @@ public override int ReadByte()

public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
#if NATIVEAOT
throw new NotImplementedException(); // TODO: https://github.com/dotnet/corert/issues/3251
#else
bool overridesBeginRead = _stream.HasOverriddenBeginEndRead();

lock (_stream)
Expand All @@ -1244,7 +1237,6 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy
_stream.BeginRead(buffer, offset, count, callback, state) :
_stream.BeginReadInternal(buffer, offset, count, callback, state, serializeAsynchronously: true, apm: true);
}
#endif
}

public override int EndRead(IAsyncResult asyncResult)
Expand Down Expand Up @@ -1302,9 +1294,6 @@ public override void WriteByte(byte b)

public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
#if NATIVEAOT
throw new NotImplementedException(); // TODO: https://github.com/dotnet/corert/issues/3251
#else
bool overridesBeginWrite = _stream.HasOverriddenBeginEndWrite();

lock (_stream)
Expand All @@ -1319,7 +1308,6 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As
_stream.BeginWrite(buffer, offset, count, callback, state) :
_stream.BeginWriteInternal(buffer, offset, count, callback, state, serializeAsynchronously: true, apm: true);
}
#endif
}

public override void EndWrite(IAsyncResult asyncResult)
Expand Down
1 change: 0 additions & 1 deletion src/libraries/tests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,6 @@
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Globalization\tests\System.Globalization.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Globalization\tests\NlsTests\System.Globalization.Nls.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Globalization.Calendars\tests\CalendarTestWithConfigSwitch\System.Globalization.CalendarsWithConfigSwitch.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.IO\tests\System.IO.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Linq.Queryable\tests\System.Linq.Queryable.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Management\tests\System.Management.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Net.WebSockets\tests\System.Net.WebSockets.Tests.csproj" />
Expand Down

0 comments on commit fae5314

Please sign in to comment.