|
// 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);
}
}
}
|