Skip to content

Commit e465230

Browse files
authored
Initial unification of DynamicMethod to be shareable between runtimes (#75829)
1 parent a2b1811 commit e465230

File tree

21 files changed

+679
-1098
lines changed

21 files changed

+679
-1098
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\ConstructorBuilder.cs" />
163163
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\CustomAttributeBuilder.cs" />
164164
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicILGenerator.cs" />
165-
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethod.cs" />
165+
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethod.CoreCLR.cs" />
166166
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EnumBuilder.cs" />
167167
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EventBuilder.cs" />
168168
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\FieldBuilder.cs" />

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ internal DynamicILGenerator(DynamicMethod method, byte[] methodSignature, int si
2323

2424
internal void GetCallableMethod(RuntimeModule module, DynamicMethod dm)
2525
{
26-
dm.m_methodHandle = ModuleHandle.GetDynamicMethod(dm,
26+
dm._methodHandle = ModuleHandle.GetDynamicMethod(dm,
2727
module,
2828
m_methodBuilder.Name,
2929
(byte[])m_scope[m_methodSigToken]!,
@@ -590,7 +590,7 @@ internal DynamicResolver(DynamicILGenerator ilGenerator)
590590
m_scope = ilGenerator.m_scope;
591591

592592
m_method = (DynamicMethod)ilGenerator.m_methodBuilder;
593-
m_method.m_resolver = this;
593+
m_method._resolver = this;
594594
}
595595

596596
internal DynamicResolver(DynamicILInfo dynamicILInfo)
@@ -602,7 +602,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)
602602
m_scope = dynamicILInfo.DynamicScope;
603603

604604
m_method = dynamicILInfo.DynamicMethod;
605-
m_method.m_resolver = this;
605+
m_method._resolver = this;
606606
}
607607

608608
//
@@ -628,7 +628,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)
628628
if (method == null)
629629
return;
630630

631-
if (method.m_methodHandle == null)
631+
if (method._methodHandle == null)
632632
return;
633633

634634
DestroyScout scout;
@@ -645,7 +645,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)
645645

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

651651
private sealed class DestroyScout
@@ -687,12 +687,12 @@ internal enum SecurityControlFlags
687687

688688
SecurityControlFlags flags = SecurityControlFlags.Default;
689689

690-
if (m_method.m_restrictedSkipVisibility)
690+
if (m_method._restrictedSkipVisibility)
691691
flags |= SecurityControlFlags.RestrictedSkipVisibilityChecks;
692-
else if (m_method.m_skipVisibility)
692+
else if (m_method._skipVisibility)
693693
flags |= SecurityControlFlags.SkipVisibilityChecks;
694694

695-
typeOwner = m_method.m_typeOwner;
695+
typeOwner = m_method._typeOwner;
696696

697697
securityControlFlags = (int)flags;
698698

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

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
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.Diagnostics.CodeAnalysis;
6+
using System.Globalization;
7+
using System.Runtime.InteropServices;
8+
using System.Runtime.CompilerServices;
9+
using System.Runtime.Loader;
10+
using System.Text;
11+
using System.Threading;
12+
using static System.Runtime.CompilerServices.RuntimeHelpers;
13+
14+
namespace System.Reflection.Emit
15+
{
16+
public sealed partial class DynamicMethod : MethodInfo
17+
{
18+
private RuntimeType[] _parameterTypes;
19+
internal IRuntimeMethodInfo? _methodHandle;
20+
private RuntimeType _returnType;
21+
private DynamicILGenerator? _ilGenerator;
22+
private DynamicILInfo? _dynamicILInfo;
23+
private bool _initLocals;
24+
private Module _module;
25+
internal bool _skipVisibility;
26+
internal RuntimeType? _typeOwner;
27+
private MethodInvoker? _invoker;
28+
private Signature? _signature;
29+
30+
// We want the creator of the DynamicMethod to control who has access to the
31+
// DynamicMethod (just like we do for delegates). However, a user can get to
32+
// the corresponding RTDynamicMethod using Exception.TargetSite, StackFrame.GetMethod, etc.
33+
// If we allowed use of RTDynamicMethod, the creator of the DynamicMethod would
34+
// not be able to bound access to the DynamicMethod. Hence, we need to ensure that
35+
// we do not allow direct use of RTDynamicMethod.
36+
private RTDynamicMethod _dynMethod;
37+
38+
// needed to keep the object alive during jitting
39+
// assigned by the DynamicResolver ctor
40+
internal DynamicResolver? _resolver;
41+
42+
internal bool _restrictedSkipVisibility;
43+
44+
//
45+
// Delegate and method creation
46+
//
47+
48+
public sealed override Delegate CreateDelegate(Type delegateType)
49+
{
50+
if (_restrictedSkipVisibility)
51+
{
52+
// Compile the method since accessibility checks are done as part of compilation.
53+
GetMethodDescriptor();
54+
IRuntimeMethodInfo? methodHandle = _methodHandle;
55+
System.Runtime.CompilerServices.RuntimeHelpers.CompileMethod(methodHandle != null ? methodHandle.Value : RuntimeMethodHandleInternal.EmptyHandle);
56+
GC.KeepAlive(methodHandle);
57+
}
58+
59+
MulticastDelegate d = (MulticastDelegate)Delegate.CreateDelegateNoSecurityCheck(delegateType, null, GetMethodDescriptor());
60+
// stash this MethodInfo by brute force.
61+
d.StoreDynamicMethod(GetMethodInfo());
62+
return d;
63+
}
64+
65+
public sealed override Delegate CreateDelegate(Type delegateType, object? target)
66+
{
67+
if (_restrictedSkipVisibility)
68+
{
69+
// Compile the method since accessibility checks are done as part of compilation
70+
GetMethodDescriptor();
71+
IRuntimeMethodInfo? methodHandle = _methodHandle;
72+
System.Runtime.CompilerServices.RuntimeHelpers.CompileMethod(methodHandle != null ? methodHandle.Value : RuntimeMethodHandleInternal.EmptyHandle);
73+
GC.KeepAlive(methodHandle);
74+
}
75+
76+
MulticastDelegate d = (MulticastDelegate)Delegate.CreateDelegateNoSecurityCheck(delegateType, target, GetMethodDescriptor());
77+
// stash this MethodInfo by brute force.
78+
d.StoreDynamicMethod(GetMethodInfo());
79+
return d;
80+
}
81+
82+
// This is guaranteed to return a valid handle
83+
internal RuntimeMethodHandle GetMethodDescriptor()
84+
{
85+
if (_methodHandle == null)
86+
{
87+
lock (this)
88+
{
89+
if (_methodHandle == null)
90+
{
91+
if (_dynamicILInfo != null)
92+
_dynamicILInfo.GetCallableMethod((RuntimeModule)_module, this);
93+
else
94+
{
95+
if (_ilGenerator == null || _ilGenerator.ILOffset == 0)
96+
throw new InvalidOperationException(SR.Format(SR.InvalidOperation_BadEmptyMethodBody, Name));
97+
98+
_ilGenerator.GetCallableMethod((RuntimeModule)_module, this);
99+
}
100+
}
101+
}
102+
}
103+
return new RuntimeMethodHandle(_methodHandle!);
104+
}
105+
106+
private MethodInvoker Invoker
107+
{
108+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
109+
get
110+
{
111+
return _invoker ??= new MethodInvoker(this, Signature);
112+
}
113+
}
114+
115+
internal Signature Signature
116+
{
117+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
118+
get
119+
{
120+
[MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path
121+
Signature LazyCreateSignature()
122+
{
123+
Debug.Assert(_methodHandle != null);
124+
Debug.Assert(_parameterTypes != null);
125+
126+
Signature newSig = new Signature(_methodHandle, _parameterTypes, _returnType, CallingConvention);
127+
Volatile.Write(ref _signature, newSig);
128+
return newSig;
129+
}
130+
131+
return _signature ?? LazyCreateSignature();
132+
}
133+
}
134+
135+
public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
136+
{
137+
if ((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs)
138+
throw new NotSupportedException(SR.NotSupported_CallToVarArg);
139+
140+
//
141+
// We do not demand any permission here because the caller already has access
142+
// to the current DynamicMethod object, and it could just as easily emit another
143+
// Transparent DynamicMethod to call the current DynamicMethod.
144+
//
145+
146+
_ = GetMethodDescriptor();
147+
// ignore obj since it's a static method
148+
149+
// verify arguments
150+
int argCount = (parameters != null) ? parameters.Length : 0;
151+
if (Signature.Arguments.Length != argCount)
152+
throw new TargetParameterCountException(SR.Arg_ParmCnt);
153+
154+
object? retValue;
155+
156+
unsafe
157+
{
158+
if (argCount == 0)
159+
{
160+
retValue = Invoker.InlinedInvoke(obj, args: default, invokeAttr);
161+
}
162+
else if (argCount > MaxStackAllocArgCount)
163+
{
164+
Debug.Assert(parameters != null);
165+
retValue = InvokeWithManyArguments(this, argCount, obj, invokeAttr, binder, parameters, culture);
166+
}
167+
else
168+
{
169+
Debug.Assert(parameters != null);
170+
StackAllocedArguments argStorage = default;
171+
Span<object?> copyOfParameters = new(ref argStorage._arg0, argCount);
172+
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount);
173+
174+
StackAllocatedByRefs byrefStorage = default;
175+
IntPtr* pByRefStorage = (IntPtr*)&byrefStorage;
176+
177+
CheckArguments(
178+
copyOfParameters,
179+
pByRefStorage,
180+
shouldCopyBackParameters,
181+
parameters,
182+
Signature.Arguments,
183+
binder,
184+
culture,
185+
invokeAttr);
186+
187+
retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
188+
189+
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
190+
for (int i = 0; i < argCount; i++)
191+
{
192+
ParameterCopyBackAction action = shouldCopyBackParameters[i];
193+
if (action != ParameterCopyBackAction.None)
194+
{
195+
if (action == ParameterCopyBackAction.Copy)
196+
{
197+
parameters[i] = copyOfParameters[i];
198+
}
199+
else
200+
{
201+
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
202+
Debug.Assert(copyOfParameters[i] != null);
203+
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
204+
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
205+
}
206+
}
207+
}
208+
}
209+
}
210+
211+
GC.KeepAlive(this);
212+
return retValue;
213+
}
214+
215+
// Slower path that does a heap alloc for copyOfParameters and registers byrefs to those objects.
216+
// This is a separate method to support better performance for the faster paths.
217+
private static unsafe object? InvokeWithManyArguments(
218+
DynamicMethod mi,
219+
int argCount,
220+
object? obj,
221+
BindingFlags invokeAttr,
222+
Binder? binder,
223+
object?[] parameters,
224+
CultureInfo? culture)
225+
{
226+
object[] objHolder = new object[argCount];
227+
Span<object?> copyOfParameters = new(objHolder, 0, argCount);
228+
229+
// We don't check a max stack size since we are invoking a method which
230+
// naturally requires a stack size that is dependent on the arg count\size.
231+
IntPtr* pByRefStorage = stackalloc IntPtr[argCount];
232+
NativeMemory.Clear(pByRefStorage, (uint)(argCount * sizeof(IntPtr)));
233+
234+
ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount];
235+
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(copyBackActions, argCount);
236+
237+
GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true);
238+
239+
object? retValue;
240+
try
241+
{
242+
RegisterForGCReporting(&reg);
243+
mi.CheckArguments(
244+
copyOfParameters,
245+
pByRefStorage,
246+
shouldCopyBackParameters,
247+
parameters,
248+
mi.Signature.Arguments,
249+
binder,
250+
culture,
251+
invokeAttr);
252+
253+
retValue = mi.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
254+
}
255+
finally
256+
{
257+
UnregisterForGCReporting(&reg);
258+
}
259+
260+
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
261+
for (int i = 0; i < argCount; i++)
262+
{
263+
ParameterCopyBackAction action = shouldCopyBackParameters[i];
264+
if (action != ParameterCopyBackAction.None)
265+
{
266+
if (action == ParameterCopyBackAction.Copy)
267+
{
268+
parameters[i] = copyOfParameters[i];
269+
}
270+
else
271+
{
272+
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
273+
Debug.Assert(copyOfParameters[i] != null);
274+
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
275+
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
276+
}
277+
}
278+
}
279+
280+
return retValue;
281+
}
282+
283+
public DynamicILInfo GetDynamicILInfo()
284+
{
285+
if (_dynamicILInfo == null)
286+
{
287+
byte[] methodSignature = SignatureHelper.GetMethodSigHelper(
288+
null, CallingConvention, ReturnType, null, null, _parameterTypes, null, null).GetSignature(true);
289+
_dynamicILInfo = new DynamicILInfo(this, methodSignature);
290+
}
291+
return _dynamicILInfo;
292+
}
293+
294+
public ILGenerator GetILGenerator(int streamSize)
295+
{
296+
if (_ilGenerator == null)
297+
{
298+
byte[] methodSignature = SignatureHelper.GetMethodSigHelper(
299+
null, CallingConvention, ReturnType, null, null, _parameterTypes, null, null).GetSignature(true);
300+
_ilGenerator = new DynamicILGenerator(this, methodSignature, streamSize);
301+
}
302+
return _ilGenerator;
303+
}
304+
}
305+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ internal int InternalGetConstructorToken(ConstructorInfo con, bool usingRef)
299299

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

302-
private static RuntimeModule GetRuntimeModuleFromModule(Module? m)
302+
internal static RuntimeModule GetRuntimeModuleFromModule(Module? m)
303303
{
304304
ModuleBuilder? mb = m as ModuleBuilder;
305305
if (mb != null)

0 commit comments

Comments
 (0)