|
| 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(®); |
| 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(®); |
| 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 | +} |
0 commit comments