Skip to content

Commit bf9fab2

Browse files
committed
Uptake GC Registration for core (not mono)
1 parent 8e7ace7 commit bf9fab2

21 files changed

+1138
-280
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<EnableDefaultItems>false</EnableDefaultItems>
@@ -155,6 +155,8 @@
155155
<Compile Include="$(BclSourcesRoot)\System\Reflection\AssemblyName.CoreCLR.cs" />
156156
<Compile Include="$(BclSourcesRoot)\System\Reflection\Associates.cs" />
157157
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInfo.CoreCLR.cs" />
158+
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInvoker.CoreCLR.cs" />
159+
<Compile Include="$(BclSourcesRoot)\System\Reflection\DynamicMethodInvoker.cs" />
158160
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\AssemblyBuilder.cs" />
159161
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\AssemblyBuilderData.cs" />
160162
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\ConstructorBuilder.cs" />
@@ -179,6 +181,7 @@
179181
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\TypeBuilder.cs" />
180182
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\TypeBuilderInstantiation.cs" />
181183
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\XXXOnTypeBuilderInstantiation.cs" />
184+
<Compile Include="$(BclSourcesRoot)\System\Reflection\FieldAccessor.CoreCLR.cs" />
182185
<Compile Include="$(BclSourcesRoot)\System\Reflection\FieldInfo.CoreCLR.cs" />
183186
<Compile Include="$(BclSourcesRoot)\System\Reflection\LoaderAllocator.cs" />
184187
<Compile Include="$(BclSourcesRoot)\System\Reflection\MdConstant.cs" />
@@ -188,6 +191,7 @@
188191
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\AssemblyExtensions.cs" />
189192
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\MetadataUpdater.cs" />
190193
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
194+
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodInvoker.CoreCLR.cs" />
191195
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
192196
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
193197
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
@@ -319,7 +323,7 @@
319323

320324
<Import Project="CreateRuntimeRootILLinkDescriptorFile.targets" />
321325

322-
<Target Name="CreateRuntimeRootIlLinkDescFile" BeforeTargets="CoreCompile" DependsOnTargets="_CreateILLinkRuntimeRootDescriptorFile"/>
326+
<Target Name="CreateRuntimeRootIlLinkDescFile" BeforeTargets="CoreCompile" DependsOnTargets="_CreateILLinkRuntimeRootDescriptorFile" />
323327

324328
<Import Project="$(RepositoryEngineeringDir)codeOptimization.targets" />
325329

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics;
5+
6+
namespace System.Reflection
7+
{
8+
internal partial class ConstructorInvoker
9+
{
10+
[DebuggerStepThrough]
11+
[DebuggerHidden]
12+
public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
13+
{
14+
// Todo: add strategy for calling IL Emit-based version
15+
return InvokeNonEmitUnsafe(obj, args, invokeAttr);
16+
}
17+
18+
[DebuggerStepThrough]
19+
[DebuggerHidden]
20+
private unsafe object InvokeNonEmitUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
21+
{
22+
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
23+
{
24+
bool rethrow = false;
25+
26+
try
27+
{
28+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _constructorInfo.Signature, isConstructor: obj is null, out rethrow)!;
29+
}
30+
catch (OutOfMemoryException)
31+
{
32+
throw; // Re-throw for backward compatibility.
33+
}
34+
catch (Exception ex) when (!rethrow)
35+
{
36+
throw new TargetInvocationException(ex);
37+
}
38+
}
39+
else
40+
{
41+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _constructorInfo.Signature, isConstructor: obj is null, out _)!;
42+
}
43+
}
44+
}
45+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics;
5+
using System.Runtime.CompilerServices;
6+
7+
namespace System.Reflection
8+
{
9+
internal sealed partial class DynamicMethodInvoker
10+
{
11+
private readonly bool _hasRefs;
12+
13+
public Signature Signature { get; }
14+
15+
public DynamicMethodInvoker(Signature signature)
16+
{
17+
Signature = signature;
18+
19+
RuntimeType[] sigTypes = signature.Arguments;
20+
for (int i = 0; i < sigTypes.Length; i++)
21+
{
22+
if (sigTypes[i].IsByRef)
23+
{
24+
_hasRefs = true;
25+
break;
26+
}
27+
}
28+
}
29+
30+
public bool HasRefs => _hasRefs;
31+
32+
public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
33+
{
34+
// Todo: add strategy for calling IL Emit-based version
35+
return InvokeNonEmitUnsafe(obj, args, invokeAttr);
36+
}
37+
38+
private unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, BindingFlags invokeAttr)
39+
{
40+
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
41+
{
42+
bool rethrow = false;
43+
44+
try
45+
{
46+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false, out rethrow);
47+
}
48+
catch (Exception e) when (!rethrow)
49+
{
50+
throw new TargetInvocationException(e);
51+
}
52+
}
53+
else
54+
{
55+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false, out _);
56+
}
57+
}
58+
}
59+
}

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Runtime.Loader;
99
using System.Text;
1010
using System.Threading;
11+
using static System.Runtime.CompilerServices.RuntimeHelpers;
1112

1213
namespace System.Reflection.Emit
1314
{
@@ -22,6 +23,7 @@ public sealed class DynamicMethod : MethodInfo
2223
private RuntimeModule m_module = null!;
2324
internal bool m_skipVisibility;
2425
internal RuntimeType? m_typeOwner; // can be null
26+
private DynamicMethodInvoker? _invoker;
2527

2628
// We want the creator of the DynamicMethod to control who has access to the
2729
// DynamicMethod (just like we do for delegates). However, a user can get to
@@ -417,6 +419,18 @@ internal RuntimeMethodHandle GetMethodDescriptor()
417419

418420
public override bool IsSecurityTransparent => false;
419421

422+
private DynamicMethodInvoker Invoker
423+
{
424+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
425+
get
426+
{
427+
_invoker ??= new DynamicMethodInvoker(
428+
new Signature(m_methodHandle!, m_parameterTypes!, m_returnType, CallingConvention));
429+
430+
return _invoker;
431+
}
432+
}
433+
420434
public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
421435
{
422436
if ((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs)
@@ -431,67 +445,80 @@ internal RuntimeMethodHandle GetMethodDescriptor()
431445
_ = GetMethodDescriptor();
432446
// ignore obj since it's a static method
433447

434-
// create a signature object
435-
Signature sig = new Signature(
436-
this.m_methodHandle!, m_parameterTypes, m_returnType, CallingConvention);
437-
438448
// verify arguments
439-
int formalCount = sig.Arguments.Length;
440-
int actualCount = (parameters != null) ? parameters.Length : 0;
441-
if (formalCount != actualCount)
449+
int argCount = (parameters != null) ? parameters.Length : 0;
450+
if (Invoker.Signature.Arguments.Length != argCount)
442451
throw new TargetParameterCountException(SR.Arg_ParmCnt);
443452

444-
// if we are here we passed all the previous checks. Time to look at the arguments
445-
StackAllocedArguments stackArgs = default;
446-
Span<object?> arguments = default;
447-
bool copyBack = false;
448-
if (actualCount != 0)
453+
object? retValue;
454+
455+
unsafe
449456
{
450-
// Adopt the MethodInvoker pattern here instead if the IL Emit perf gain is necessary.
451-
bool HasRefs()
457+
if (argCount == 0)
452458
{
453-
for (int i = 0; i < sig.Arguments.Length; i++)
459+
retValue = Invoker.InvokeUnsafe(obj, args: default, invokeAttr);
460+
}
461+
else
462+
{
463+
Debug.Assert(parameters != null);
464+
Span<object?> parametersOut;
465+
bool copyBack = Invoker.HasRefs;
466+
467+
if (argCount <= MaxStackAllocArgCount)
454468
{
455-
if (sig.Arguments[i].IsByRef)
469+
StackAllocatedByRefs byrefStorage = default;
470+
IntPtr* unsafeParameters = (IntPtr*)&byrefStorage;
471+
StackAllocedArguments argStorage = default;
472+
parametersOut = new Span<object?>(ref argStorage._arg0, argCount);
473+
474+
CheckArguments(
475+
ref parametersOut,
476+
unsafeParameters,
477+
ref copyBack,
478+
parameters,
479+
Invoker.Signature.Arguments,
480+
binder,
481+
culture,
482+
invokeAttr);
483+
484+
retValue = Invoker.InvokeUnsafe(obj, unsafeParameters, invokeAttr);
485+
}
486+
else
487+
{
488+
parametersOut = new Span<object?>(new object[argCount]);
489+
IntPtr* unsafeParameters = stackalloc IntPtr[argCount];
490+
GCFrameRegistration reg = new(unsafeParameters, (uint)argCount, areByRefs: true);
491+
492+
try
456493
{
457-
return true;
494+
RegisterForGCReporting(&reg);
495+
CheckArguments(
496+
ref parametersOut,
497+
unsafeParameters,
498+
ref copyBack,
499+
parameters,
500+
Invoker.Signature.Arguments,
501+
binder,
502+
culture,
503+
invokeAttr);
504+
505+
retValue = Invoker.InvokeUnsafe(obj, unsafeParameters, invokeAttr);
506+
}
507+
finally
508+
{
509+
UnregisterForGCReporting(&reg);
458510
}
459511
}
460512

461-
return false;
462-
}
463-
464-
copyBack = HasRefs();
465-
arguments = CheckArguments(ref stackArgs, parameters!, ref copyBack, sig.Arguments, binder, culture, invokeAttr);
466-
}
467-
468-
object? retValue;
469-
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
470-
if (wrapExceptions)
471-
{
472-
bool rethrow = false;
473-
474-
try
475-
{
476-
retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, constructor: false, out rethrow);
477-
}
478-
catch (Exception ex) when (rethrow == false)
479-
{
480-
throw new TargetInvocationException(ex);
481-
}
482-
}
483-
else
484-
{
485-
retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, constructor: false, out _);
486-
}
487-
488-
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
489-
// n.b. cannot use Span<T>.CopyTo, as parameters.GetType() might not actually be typeof(object[])
490-
if (copyBack)
491-
{
492-
for (int index = 0; index < arguments.Length; index++)
493-
{
494-
parameters![index] = arguments[index];
513+
if (copyBack)
514+
{
515+
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
516+
// n.b. cannot use Span<T>.CopyTo, as parameters.GetType() might not actually be typeof(object[])
517+
for (int i = 0; i < argCount; i++)
518+
{
519+
parameters[i] = parametersOut[i];
520+
}
521+
}
495522
}
496523
}
497524

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
namespace System.Reflection
7+
{
8+
internal partial class FieldAccessor
9+
{
10+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11+
public object? InvokeGetter(object? obj)
12+
{
13+
// Todo: add strategy for calling IL Emit-based version
14+
return InvokeGetterNonEmit(obj);
15+
}
16+
17+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
18+
public void InvokeSetter(object? obj, object? value)
19+
{
20+
// Todo: add strategy for calling IL Emit-based version
21+
InvokeSetterNonEmit(obj, value);
22+
}
23+
24+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
25+
internal object? InvokeGetterNonEmit(object? obj)
26+
{
27+
RuntimeType? declaringType = _fieldInfo.DeclaringType as RuntimeType;
28+
RuntimeType fieldType = (RuntimeType)_fieldInfo.FieldType;
29+
bool domainInitialized = false;
30+
31+
if (declaringType == null)
32+
{
33+
return RuntimeFieldHandle.GetValue(_fieldInfo, obj, fieldType, null, ref domainInitialized);
34+
}
35+
else
36+
{
37+
domainInitialized = declaringType.DomainInitialized;
38+
object? retVal = RuntimeFieldHandle.GetValue(_fieldInfo, obj, fieldType, declaringType, ref domainInitialized);
39+
declaringType.DomainInitialized = domainInitialized;
40+
return retVal;
41+
}
42+
}
43+
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45+
internal void InvokeSetterNonEmit(object? obj, object? value)
46+
{
47+
RuntimeType? declaringType = _fieldInfo.DeclaringType as RuntimeType;
48+
RuntimeType fieldType = (RuntimeType)_fieldInfo.FieldType;
49+
bool domainInitialized = false;
50+
51+
if (declaringType == null)
52+
{
53+
RuntimeFieldHandle.SetValue(
54+
_fieldInfo,
55+
obj,
56+
value,
57+
fieldType,
58+
_fieldInfo.Attributes,
59+
declaringType: null,
60+
ref domainInitialized);
61+
}
62+
else
63+
{
64+
domainInitialized = declaringType.DomainInitialized;
65+
66+
RuntimeFieldHandle.SetValue(
67+
_fieldInfo,
68+
obj,
69+
value,
70+
fieldType,
71+
_fieldInfo.Attributes,
72+
declaringType,
73+
ref domainInitialized);
74+
75+
declaringType.DomainInitialized = domainInitialized;
76+
}
77+
}
78+
79+
}
80+
}

0 commit comments

Comments
 (0)