Skip to content

Commit

Permalink
Initial unification of DynamicMethod to be shareable between runtimes (
Browse files Browse the repository at this point in the history
  • Loading branch information
marek-safar committed Sep 22, 2022
1 parent a2b1811 commit e465230
Show file tree
Hide file tree
Showing 21 changed files with 679 additions and 1,098 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\ConstructorBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\CustomAttributeBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicILGenerator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethod.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethod.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EnumBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EventBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\FieldBuilder.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal DynamicILGenerator(DynamicMethod method, byte[] methodSignature, int si

internal void GetCallableMethod(RuntimeModule module, DynamicMethod dm)
{
dm.m_methodHandle = ModuleHandle.GetDynamicMethod(dm,
dm._methodHandle = ModuleHandle.GetDynamicMethod(dm,
module,
m_methodBuilder.Name,
(byte[])m_scope[m_methodSigToken]!,
Expand Down Expand Up @@ -590,7 +590,7 @@ internal DynamicResolver(DynamicILGenerator ilGenerator)
m_scope = ilGenerator.m_scope;

m_method = (DynamicMethod)ilGenerator.m_methodBuilder;
m_method.m_resolver = this;
m_method._resolver = this;
}

internal DynamicResolver(DynamicILInfo dynamicILInfo)
Expand All @@ -602,7 +602,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)
m_scope = dynamicILInfo.DynamicScope;

m_method = dynamicILInfo.DynamicMethod;
m_method.m_resolver = this;
m_method._resolver = this;
}

//
Expand All @@ -628,7 +628,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)
if (method == null)
return;

if (method.m_methodHandle == null)
if (method._methodHandle == null)
return;

DestroyScout scout;
Expand All @@ -645,7 +645,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)

// We can never ever have two active destroy scouts for the same method. We need to initialize the scout
// outside the try/reregister block to avoid possibility of reregistration for finalization with active scout.
scout.m_methodHandle = method.m_methodHandle.Value;
scout.m_methodHandle = method._methodHandle.Value;
}

private sealed class DestroyScout
Expand Down Expand Up @@ -687,12 +687,12 @@ internal enum SecurityControlFlags

SecurityControlFlags flags = SecurityControlFlags.Default;

if (m_method.m_restrictedSkipVisibility)
if (m_method._restrictedSkipVisibility)
flags |= SecurityControlFlags.RestrictedSkipVisibilityChecks;
else if (m_method.m_skipVisibility)
else if (m_method._skipVisibility)
flags |= SecurityControlFlags.SkipVisibilityChecks;

typeOwner = m_method.m_typeOwner;
typeOwner = m_method._typeOwner;

securityControlFlags = (int)flags;

Expand Down Expand Up @@ -884,7 +884,7 @@ internal DynamicILInfo(DynamicMethod method, byte[] methodSignature)
#region Internal Methods
internal void GetCallableMethod(RuntimeModule module, DynamicMethod dm)
{
dm.m_methodHandle = ModuleHandle.GetDynamicMethod(dm,
dm._methodHandle = ModuleHandle.GetDynamicMethod(dm,
module, m_method.Name, (byte[])m_scope[m_methodSignature]!, new DynamicResolver(this));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
// 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.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Text;
using System.Threading;
using static System.Runtime.CompilerServices.RuntimeHelpers;

namespace System.Reflection.Emit
{
public sealed partial class DynamicMethod : MethodInfo
{
private RuntimeType[] _parameterTypes;
internal IRuntimeMethodInfo? _methodHandle;
private RuntimeType _returnType;
private DynamicILGenerator? _ilGenerator;
private DynamicILInfo? _dynamicILInfo;
private bool _initLocals;
private Module _module;
internal bool _skipVisibility;
internal RuntimeType? _typeOwner;
private MethodInvoker? _invoker;
private Signature? _signature;

// 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
// the corresponding RTDynamicMethod using Exception.TargetSite, StackFrame.GetMethod, etc.
// If we allowed use of RTDynamicMethod, the creator of the DynamicMethod would
// not be able to bound access to the DynamicMethod. Hence, we need to ensure that
// we do not allow direct use of RTDynamicMethod.
private RTDynamicMethod _dynMethod;

// needed to keep the object alive during jitting
// assigned by the DynamicResolver ctor
internal DynamicResolver? _resolver;

internal bool _restrictedSkipVisibility;

//
// Delegate and method creation
//

public sealed override Delegate CreateDelegate(Type delegateType)
{
if (_restrictedSkipVisibility)
{
// Compile the method since accessibility checks are done as part of compilation.
GetMethodDescriptor();
IRuntimeMethodInfo? methodHandle = _methodHandle;
System.Runtime.CompilerServices.RuntimeHelpers.CompileMethod(methodHandle != null ? methodHandle.Value : RuntimeMethodHandleInternal.EmptyHandle);
GC.KeepAlive(methodHandle);
}

MulticastDelegate d = (MulticastDelegate)Delegate.CreateDelegateNoSecurityCheck(delegateType, null, GetMethodDescriptor());
// stash this MethodInfo by brute force.
d.StoreDynamicMethod(GetMethodInfo());
return d;
}

public sealed override Delegate CreateDelegate(Type delegateType, object? target)
{
if (_restrictedSkipVisibility)
{
// Compile the method since accessibility checks are done as part of compilation
GetMethodDescriptor();
IRuntimeMethodInfo? methodHandle = _methodHandle;
System.Runtime.CompilerServices.RuntimeHelpers.CompileMethod(methodHandle != null ? methodHandle.Value : RuntimeMethodHandleInternal.EmptyHandle);
GC.KeepAlive(methodHandle);
}

MulticastDelegate d = (MulticastDelegate)Delegate.CreateDelegateNoSecurityCheck(delegateType, target, GetMethodDescriptor());
// stash this MethodInfo by brute force.
d.StoreDynamicMethod(GetMethodInfo());
return d;
}

// This is guaranteed to return a valid handle
internal RuntimeMethodHandle GetMethodDescriptor()
{
if (_methodHandle == null)
{
lock (this)
{
if (_methodHandle == null)
{
if (_dynamicILInfo != null)
_dynamicILInfo.GetCallableMethod((RuntimeModule)_module, this);
else
{
if (_ilGenerator == null || _ilGenerator.ILOffset == 0)
throw new InvalidOperationException(SR.Format(SR.InvalidOperation_BadEmptyMethodBody, Name));

_ilGenerator.GetCallableMethod((RuntimeModule)_module, this);
}
}
}
}
return new RuntimeMethodHandle(_methodHandle!);
}

private MethodInvoker Invoker
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _invoker ??= new MethodInvoker(this, Signature);
}
}

internal Signature Signature
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
[MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path
Signature LazyCreateSignature()
{
Debug.Assert(_methodHandle != null);
Debug.Assert(_parameterTypes != null);

Signature newSig = new Signature(_methodHandle, _parameterTypes, _returnType, CallingConvention);
Volatile.Write(ref _signature, newSig);
return newSig;
}

return _signature ?? LazyCreateSignature();
}
}

public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
{
if ((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs)
throw new NotSupportedException(SR.NotSupported_CallToVarArg);

//
// We do not demand any permission here because the caller already has access
// to the current DynamicMethod object, and it could just as easily emit another
// Transparent DynamicMethod to call the current DynamicMethod.
//

_ = GetMethodDescriptor();
// ignore obj since it's a static method

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

object? retValue;

unsafe
{
if (argCount == 0)
{
retValue = Invoker.InlinedInvoke(obj, args: default, invokeAttr);
}
else if (argCount > MaxStackAllocArgCount)
{
Debug.Assert(parameters != null);
retValue = InvokeWithManyArguments(this, argCount, obj, invokeAttr, binder, parameters, culture);
}
else
{
Debug.Assert(parameters != null);
StackAllocedArguments argStorage = default;
Span<object?> copyOfParameters = new(ref argStorage._arg0, argCount);
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount);

StackAllocatedByRefs byrefStorage = default;
IntPtr* pByRefStorage = (IntPtr*)&byrefStorage;

CheckArguments(
copyOfParameters,
pByRefStorage,
shouldCopyBackParameters,
parameters,
Signature.Arguments,
binder,
culture,
invokeAttr);

retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);

// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
for (int i = 0; i < argCount; i++)
{
ParameterCopyBackAction action = shouldCopyBackParameters[i];
if (action != ParameterCopyBackAction.None)
{
if (action == ParameterCopyBackAction.Copy)
{
parameters[i] = copyOfParameters[i];
}
else
{
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
Debug.Assert(copyOfParameters[i] != null);
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
}
}
}
}
}

GC.KeepAlive(this);
return retValue;
}

// Slower path that does a heap alloc for copyOfParameters and registers byrefs to those objects.
// This is a separate method to support better performance for the faster paths.
private static unsafe object? InvokeWithManyArguments(
DynamicMethod mi,
int argCount,
object? obj,
BindingFlags invokeAttr,
Binder? binder,
object?[] parameters,
CultureInfo? culture)
{
object[] objHolder = new object[argCount];
Span<object?> copyOfParameters = new(objHolder, 0, argCount);

// We don't check a max stack size since we are invoking a method which
// naturally requires a stack size that is dependent on the arg count\size.
IntPtr* pByRefStorage = stackalloc IntPtr[argCount];
NativeMemory.Clear(pByRefStorage, (uint)(argCount * sizeof(IntPtr)));

ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount];
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(copyBackActions, argCount);

GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true);

object? retValue;
try
{
RegisterForGCReporting(&reg);
mi.CheckArguments(
copyOfParameters,
pByRefStorage,
shouldCopyBackParameters,
parameters,
mi.Signature.Arguments,
binder,
culture,
invokeAttr);

retValue = mi.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
}
finally
{
UnregisterForGCReporting(&reg);
}

// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
for (int i = 0; i < argCount; i++)
{
ParameterCopyBackAction action = shouldCopyBackParameters[i];
if (action != ParameterCopyBackAction.None)
{
if (action == ParameterCopyBackAction.Copy)
{
parameters[i] = copyOfParameters[i];
}
else
{
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
Debug.Assert(copyOfParameters[i] != null);
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
}
}
}

return retValue;
}

public DynamicILInfo GetDynamicILInfo()
{
if (_dynamicILInfo == null)
{
byte[] methodSignature = SignatureHelper.GetMethodSigHelper(
null, CallingConvention, ReturnType, null, null, _parameterTypes, null, null).GetSignature(true);
_dynamicILInfo = new DynamicILInfo(this, methodSignature);
}
return _dynamicILInfo;
}

public ILGenerator GetILGenerator(int streamSize)
{
if (_ilGenerator == null)
{
byte[] methodSignature = SignatureHelper.GetMethodSigHelper(
null, CallingConvention, ReturnType, null, null, _parameterTypes, null, null).GetSignature(true);
_ilGenerator = new DynamicILGenerator(this, methodSignature, streamSize);
}
return _ilGenerator;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ internal int InternalGetConstructorToken(ConstructorInfo con, bool usingRef)

protected override ModuleHandle GetModuleHandleImpl() => new ModuleHandle(InternalModule);

private static RuntimeModule GetRuntimeModuleFromModule(Module? m)
internal static RuntimeModule GetRuntimeModuleFromModule(Module? m)
{
ModuleBuilder? mb = m as ModuleBuilder;
if (mb != null)
Expand Down
Loading

0 comments on commit e465230

Please sign in to comment.