Skip to content

Commit

Permalink
Uptake GC Registration for core (not mono)
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter committed Mar 13, 2022
1 parent 8e7ace7 commit bf9fab2
Show file tree
Hide file tree
Showing 21 changed files with 1,138 additions and 280 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<EnableDefaultItems>false</EnableDefaultItems>
Expand Down Expand Up @@ -155,6 +155,8 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\AssemblyName.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Associates.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInvoker.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\DynamicMethodInvoker.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\AssemblyBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\AssemblyBuilderData.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\ConstructorBuilder.cs" />
Expand All @@ -179,6 +181,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\TypeBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\TypeBuilderInstantiation.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\XXXOnTypeBuilderInstantiation.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\FieldAccessor.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\FieldInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\LoaderAllocator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MdConstant.cs" />
Expand All @@ -188,6 +191,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\AssemblyExtensions.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\MetadataUpdater.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodInvoker.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
Expand Down Expand Up @@ -319,7 +323,7 @@

<Import Project="CreateRuntimeRootILLinkDescriptorFile.targets" />

<Target Name="CreateRuntimeRootIlLinkDescFile" BeforeTargets="CoreCompile" DependsOnTargets="_CreateILLinkRuntimeRootDescriptorFile"/>
<Target Name="CreateRuntimeRootIlLinkDescFile" BeforeTargets="CoreCompile" DependsOnTargets="_CreateILLinkRuntimeRootDescriptorFile" />

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Reflection
{
internal partial class ConstructorInvoker
{
[DebuggerStepThrough]
[DebuggerHidden]
public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
{
// Todo: add strategy for calling IL Emit-based version
return InvokeNonEmitUnsafe(obj, args, invokeAttr);
}

[DebuggerStepThrough]
[DebuggerHidden]
private unsafe object InvokeNonEmitUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
{
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
{
bool rethrow = false;

try
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _constructorInfo.Signature, isConstructor: obj is null, out rethrow)!;
}
catch (OutOfMemoryException)
{
throw; // Re-throw for backward compatibility.
}
catch (Exception ex) when (!rethrow)
{
throw new TargetInvocationException(ex);
}
}
else
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, _constructorInfo.Signature, isConstructor: obj is null, out _)!;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace System.Reflection
{
internal sealed partial class DynamicMethodInvoker
{
private readonly bool _hasRefs;

public Signature Signature { get; }

public DynamicMethodInvoker(Signature signature)
{
Signature = signature;

RuntimeType[] sigTypes = signature.Arguments;
for (int i = 0; i < sigTypes.Length; i++)
{
if (sigTypes[i].IsByRef)
{
_hasRefs = true;
break;
}
}
}

public bool HasRefs => _hasRefs;

public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
{
// Todo: add strategy for calling IL Emit-based version
return InvokeNonEmitUnsafe(obj, args, invokeAttr);
}

private unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, BindingFlags invokeAttr)
{
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
{
bool rethrow = false;

try
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false, out rethrow);
}
catch (Exception e) when (!rethrow)
{
throw new TargetInvocationException(e);
}
}
else
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false, out _);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Runtime.Loader;
using System.Text;
using System.Threading;
using static System.Runtime.CompilerServices.RuntimeHelpers;

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

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

public override bool IsSecurityTransparent => false;

private DynamicMethodInvoker Invoker
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
_invoker ??= new DynamicMethodInvoker(
new Signature(m_methodHandle!, m_parameterTypes!, m_returnType, CallingConvention));

return _invoker;
}
}

public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
{
if ((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs)
Expand All @@ -431,67 +445,80 @@ internal RuntimeMethodHandle GetMethodDescriptor()
_ = GetMethodDescriptor();
// ignore obj since it's a static method

// create a signature object
Signature sig = new Signature(
this.m_methodHandle!, m_parameterTypes, m_returnType, CallingConvention);

// verify arguments
int formalCount = sig.Arguments.Length;
int actualCount = (parameters != null) ? parameters.Length : 0;
if (formalCount != actualCount)
int argCount = (parameters != null) ? parameters.Length : 0;
if (Invoker.Signature.Arguments.Length != argCount)
throw new TargetParameterCountException(SR.Arg_ParmCnt);

// if we are here we passed all the previous checks. Time to look at the arguments
StackAllocedArguments stackArgs = default;
Span<object?> arguments = default;
bool copyBack = false;
if (actualCount != 0)
object? retValue;

unsafe
{
// Adopt the MethodInvoker pattern here instead if the IL Emit perf gain is necessary.
bool HasRefs()
if (argCount == 0)
{
for (int i = 0; i < sig.Arguments.Length; i++)
retValue = Invoker.InvokeUnsafe(obj, args: default, invokeAttr);
}
else
{
Debug.Assert(parameters != null);
Span<object?> parametersOut;
bool copyBack = Invoker.HasRefs;

if (argCount <= MaxStackAllocArgCount)
{
if (sig.Arguments[i].IsByRef)
StackAllocatedByRefs byrefStorage = default;
IntPtr* unsafeParameters = (IntPtr*)&byrefStorage;
StackAllocedArguments argStorage = default;
parametersOut = new Span<object?>(ref argStorage._arg0, argCount);

CheckArguments(
ref parametersOut,
unsafeParameters,
ref copyBack,
parameters,
Invoker.Signature.Arguments,
binder,
culture,
invokeAttr);

retValue = Invoker.InvokeUnsafe(obj, unsafeParameters, invokeAttr);
}
else
{
parametersOut = new Span<object?>(new object[argCount]);
IntPtr* unsafeParameters = stackalloc IntPtr[argCount];
GCFrameRegistration reg = new(unsafeParameters, (uint)argCount, areByRefs: true);

try
{
return true;
RegisterForGCReporting(&reg);
CheckArguments(
ref parametersOut,
unsafeParameters,
ref copyBack,
parameters,
Invoker.Signature.Arguments,
binder,
culture,
invokeAttr);

retValue = Invoker.InvokeUnsafe(obj, unsafeParameters, invokeAttr);
}
finally
{
UnregisterForGCReporting(&reg);
}
}

return false;
}

copyBack = HasRefs();
arguments = CheckArguments(ref stackArgs, parameters!, ref copyBack, sig.Arguments, binder, culture, invokeAttr);
}

object? retValue;
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
if (wrapExceptions)
{
bool rethrow = false;

try
{
retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, constructor: false, out rethrow);
}
catch (Exception ex) when (rethrow == false)
{
throw new TargetInvocationException(ex);
}
}
else
{
retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, constructor: false, out _);
}

// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
// n.b. cannot use Span<T>.CopyTo, as parameters.GetType() might not actually be typeof(object[])
if (copyBack)
{
for (int index = 0; index < arguments.Length; index++)
{
parameters![index] = arguments[index];
if (copyBack)
{
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
// n.b. cannot use Span<T>.CopyTo, as parameters.GetType() might not actually be typeof(object[])
for (int i = 0; i < argCount; i++)
{
parameters[i] = parametersOut[i];
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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.Reflection
{
internal partial class FieldAccessor
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object? InvokeGetter(object? obj)
{
// Todo: add strategy for calling IL Emit-based version
return InvokeGetterNonEmit(obj);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeSetter(object? obj, object? value)
{
// Todo: add strategy for calling IL Emit-based version
InvokeSetterNonEmit(obj, value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal object? InvokeGetterNonEmit(object? obj)
{
RuntimeType? declaringType = _fieldInfo.DeclaringType as RuntimeType;
RuntimeType fieldType = (RuntimeType)_fieldInfo.FieldType;
bool domainInitialized = false;

if (declaringType == null)
{
return RuntimeFieldHandle.GetValue(_fieldInfo, obj, fieldType, null, ref domainInitialized);
}
else
{
domainInitialized = declaringType.DomainInitialized;
object? retVal = RuntimeFieldHandle.GetValue(_fieldInfo, obj, fieldType, declaringType, ref domainInitialized);
declaringType.DomainInitialized = domainInitialized;
return retVal;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InvokeSetterNonEmit(object? obj, object? value)
{
RuntimeType? declaringType = _fieldInfo.DeclaringType as RuntimeType;
RuntimeType fieldType = (RuntimeType)_fieldInfo.FieldType;
bool domainInitialized = false;

if (declaringType == null)
{
RuntimeFieldHandle.SetValue(
_fieldInfo,
obj,
value,
fieldType,
_fieldInfo.Attributes,
declaringType: null,
ref domainInitialized);
}
else
{
domainInitialized = declaringType.DomainInitialized;

RuntimeFieldHandle.SetValue(
_fieldInfo,
obj,
value,
fieldType,
_fieldInfo.Attributes,
declaringType,
ref domainInitialized);

declaringType.DomainInitialized = domainInitialized;
}
}

}
}
Loading

0 comments on commit bf9fab2

Please sign in to comment.