| File: IL\Stubs\PInvokeILEmitter.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Net; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.CompilerServices; using ILCompiler; using Internal.TypeSystem; using Internal.TypeSystem.Interop; using Internal.JitInterface; using Debug = System.Diagnostics.Debug; namespace Internal.IL.Stubs { /// <summary> /// Provides method bodies for PInvoke methods /// </summary> public struct PInvokeILEmitter { private readonly MethodDesc _targetMethod; private readonly Marshaller[] _marshallers; private readonly PInvokeMetadata _importMetadata; private static readonly ConditionalWeakTable<TypeSystemContext, ConcurrentDictionary<MethodDesc, PInvokeTargetNativeMethod>> s_contexts = new(); private PInvokeILEmitter(MethodDesc targetMethod) { Debug.Assert(targetMethod.IsPInvoke); _targetMethod = targetMethod; _importMetadata = targetMethod.GetPInvokeMethodMetadata(); _marshallers = Marshaller.GetMarshallersForMethod(targetMethod); } private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) { ILEmitter emitter = ilCodeStreams.Emitter; ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream; TypeSystemContext context = _targetMethod.Context; TypeDesc nativeReturnType = _marshallers[0].NativeParameterType; TypeDesc[] nativeParameterTypes = new TypeDesc[_marshallers.Length - 1]; for (int i = 1; i < _marshallers.Length; i++) { nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; } MethodSignature nativeSig = new MethodSignature( _targetMethod.Signature.Flags, 0, nativeReturnType, nativeParameterTypes); var rawTargetMethod = AllocateTargetNativeMethod(_targetMethod, nativeSig); callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(rawTargetMethod)); static PInvokeTargetNativeMethod AllocateTargetNativeMethod(MethodDesc targetMethod, MethodSignature nativeSigArg) { var contextMethods = s_contexts.GetOrCreateValue(targetMethod.Context); if (contextMethods.TryGetValue(targetMethod, out var pinvokeTargetMethod)) { return pinvokeTargetMethod; } return contextMethods.GetOrAdd(targetMethod, new PInvokeTargetNativeMethod(targetMethod, nativeSigArg)); } } private MethodIL EmitIL() { if (!_importMetadata.Flags.PreserveSig) throw new NotSupportedException(); if (MarshalHelpers.ShouldCheckForPendingException(_targetMethod.Context.Target, _importMetadata)) throw new NotSupportedException(); if (_targetMethod.IsUnmanagedCallersOnly) throw new NotSupportedException(); if (_targetMethod.HasCustomAttribute("System.Runtime.InteropServices", "LCIDConversionAttribute")) throw new NotSupportedException(); if (_importMetadata.Flags.SetLastError) throw new NotSupportedException(); PInvokeILCodeStreams pInvokeILCodeStreams = new PInvokeILCodeStreams(); ILEmitter emitter = pInvokeILCodeStreams.Emitter; ILCodeStream marshallingCodestream = pInvokeILCodeStreams.MarshallingCodeStream; ILCodeStream unmarshallingCodestream = pInvokeILCodeStreams.UnmarshallingCodestream; ILCodeStream cleanupCodestream = pInvokeILCodeStreams.CleanupCodeStream; // Marshalling is wrapped in a finally block to guarantee cleanup ILExceptionRegionBuilder tryFinally = emitter.NewFinallyRegion(); marshallingCodestream.BeginTry(tryFinally); cleanupCodestream.BeginHandler(tryFinally); // Marshal the arguments for (int i = 0; i < _marshallers.Length; i++) { _marshallers[i].EmitMarshallingIL(pInvokeILCodeStreams); } EmitPInvokeCall(pInvokeILCodeStreams); ILCodeLabel lReturn = emitter.NewCodeLabel(); unmarshallingCodestream.Emit(ILOpcode.leave, lReturn); unmarshallingCodestream.EndTry(tryFinally); cleanupCodestream.Emit(ILOpcode.endfinally); cleanupCodestream.EndHandler(tryFinally); cleanupCodestream.EmitLabel(lReturn); _marshallers[0].LoadReturnValue(cleanupCodestream); cleanupCodestream.Emit(ILOpcode.ret); return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod)); } public static MethodIL EmitIL(MethodDesc method) { try { return new PInvokeILEmitter(method).EmitIL(); } catch (NotSupportedException) { throw new RequiresRuntimeJitException(method); } catch (InvalidProgramException) { throw new RequiresRuntimeJitException(method); } } } public sealed class PInvokeILStubMethodIL : ILStubMethodIL { public bool IsMarshallingRequired { get; } public PInvokeILStubMethodIL(ILStubMethodIL methodIL) : base(methodIL) { IsMarshallingRequired = Marshaller.IsMarshallingRequired(methodIL.OwningMethod); } } }