protected override TypeDesc MarshalArgument(TypeDesc managedType, ILEmitter emitter, ILCodeStream marshallingCodeStream, ILCodeStream unmarshallingCodeStream) { var safeHandleType = PInvokeMethodData.SafeHandleType; var vAddRefed = emitter.NewLocal(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.Boolean)); var vSafeHandle = emitter.NewLocal(managedType); marshallingCodeStream.EmitStLoc(vSafeHandle); marshallingCodeStream.EmitLdLoc(vSafeHandle); marshallingCodeStream.EmitLdLoca(vAddRefed); marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( safeHandleType.GetKnownMethod("DangerousAddRef", null))); marshallingCodeStream.EmitLdLoc(vSafeHandle); marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( safeHandleType.GetKnownMethod("DangerousGetHandle", null))); // TODO: This should be inside finally block and only executed it the handle was addrefed unmarshallingCodeStream.EmitLdLoc(vSafeHandle); unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( safeHandleType.GetKnownMethod("DangerousRelease", null))); return(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr)); }
protected override void AllocManagedToNative(ILCodeStream codeStream) { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeLabel lNull = emitter.NewCodeLabel(); codeStream.EmitLdc(0); codeStream.Emit(ILOpcode.conv_i); StoreNativeValue(codeStream); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.brfalse, lNull); TypeDesc nativeStructType = InteropStateManager.GetStructMarshallingNativeType(ManagedType); ILLocalVariable lNativeType = emitter.NewLocal(nativeStructType); codeStream.EmitLdLoca(lNativeType); codeStream.Emit(ILOpcode.initobj, emitter.NewToken(nativeStructType)); codeStream.EmitLdLoca(lNativeType); StoreNativeValue(codeStream); codeStream.EmitLabel(lNull); }
protected override void EmitMarshalArgumentManagedToNative() { ILEmitter emitter = _ilCodeStreams.Emitter; ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream; ILCodeStream unmarshallingCodeStream = _ilCodeStreams.UnmarshallingCodestream; // we don't support [IN,OUT] together yet, either IN or OUT Debug.Assert(!(PInvokeParameterMetadata.Out && PInvokeParameterMetadata.In)); var safeHandleType = PInvokeMethodData.SafeHandleType; if (Out) { // 1) If this is an output parameter we need to preallocate a SafeHandle to wrap the new native handle value. We // must allocate this before the native call to avoid a failure point when we already have a native resource // allocated. We must allocate a new SafeHandle even if we have one on input since both input and output native // handles need to be tracked and released by a SafeHandle. // 2) Initialize a local IntPtr that will be passed to the native call. // 3) After the native call, the new handle value is written into the output SafeHandle and that SafeHandle // is propagated back to the caller. Debug.Assert(ManagedParameterType is ByRefType); var vOutArg = emitter.NewLocal(ManagedParameterType); marshallingCodeStream.EmitStLoc(vOutArg); TypeDesc resolvedType = ((ByRefType)ManagedParameterType).ParameterType; var nativeType = PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr).MakeByRefType(); var vOutValue = emitter.NewLocal(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr)); var vSafeHandle = emitter.NewLocal(resolvedType); marshallingCodeStream.Emit(ILOpcode.newobj, emitter.NewToken(resolvedType.GetDefaultConstructor())); marshallingCodeStream.EmitStLoc(vSafeHandle); marshallingCodeStream.EmitLdLoca(vOutValue); unmarshallingCodeStream.EmitLdLoc(vSafeHandle); unmarshallingCodeStream.EmitLdLoc(vOutValue); unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( PInvokeMethodData.SafeHandleType.GetKnownMethod("SetHandle", null))); unmarshallingCodeStream.EmitLdLoc(vOutArg); unmarshallingCodeStream.EmitLdLoc(vSafeHandle); unmarshallingCodeStream.Emit(ILOpcode.stind_i); NativeParameterType = nativeType; } else { var vAddRefed = emitter.NewLocal(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.Boolean)); var vSafeHandle = emitter.NewLocal(ManagedParameterType); marshallingCodeStream.EmitStLoc(vSafeHandle); marshallingCodeStream.EmitLdLoc(vSafeHandle); marshallingCodeStream.EmitLdLoca(vAddRefed); marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( safeHandleType.GetKnownMethod("DangerousAddRef", null))); marshallingCodeStream.EmitLdLoc(vSafeHandle); marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( safeHandleType.GetKnownMethod("DangerousGetHandle", null))); // TODO: This should be inside finally block and only executed it the handle was addrefed unmarshallingCodeStream.EmitLdLoc(vSafeHandle); unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( safeHandleType.GetKnownMethod("DangerousRelease", null))); NativeParameterType = PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr); } }
/// <summary> /// Provides method bodies for intrinsics recognized by the compiler that /// are specialized per instantiation. It can return null if the intrinsic /// is not recognized. /// </summary> private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) { Debug.Assert(method.IsIntrinsic); MetadataType owningType = method.OwningType.GetTypeDefinition() as MetadataType; if (owningType == null) { return(null); } string methodName = method.Name; switch (owningType.Name) { case "Activator": { TypeSystemContext context = owningType.Context; if (methodName == "CreateInstance" && method.Signature.Length == 0 && method.HasInstantiation && method.Instantiation[0] is TypeDesc activatedType && activatedType != context.UniversalCanonType && activatedType.IsValueType && activatedType.GetParameterlessConstructor() == null) { ILEmitter emit = new ILEmitter(); ILCodeStream codeStream = emit.NewCodeStream(); var t = emit.NewLocal(context.GetSignatureVariable(0, method: true)); codeStream.EmitLdLoca(t); codeStream.Emit(ILOpcode.initobj, emit.NewToken(context.GetSignatureVariable(0, method: true))); codeStream.EmitLdLoc(t); codeStream.Emit(ILOpcode.ret); return(new InstantiatedMethodIL(method, emit.Link(method.GetMethodDefinition()))); } } break; case "RuntimeHelpers": { if (owningType.Namespace == "System.Runtime.CompilerServices") { return(RuntimeHelpersIntrinsics.EmitIL(method)); } } break; case "Comparer`1": { if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") { return(ComparerIntrinsics.EmitComparerCreate(method)); } } break; case "EqualityComparer`1": { if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") { return(ComparerIntrinsics.EmitEqualityComparerCreate(method)); } } break; case "ComparerHelpers": { if (owningType.Namespace != "Internal.IntrinsicSupport") { return(null); } if (methodName == "EnumOnlyCompare") { //calls CompareTo for underlyingType to avoid boxing TypeDesc elementType = method.Instantiation[0]; if (!elementType.IsEnum) { return(null); } TypeDesc underlyingType = elementType.UnderlyingType; TypeDesc returnType = method.Context.GetWellKnownType(WellKnownType.Int32); MethodDesc underlyingCompareToMethod = underlyingType.GetKnownMethod("CompareTo", new MethodSignature( MethodSignatureFlags.None, genericParameterCount: 0, returnType: returnType, parameters: new TypeDesc[] { underlyingType })); ILEmitter emitter = new ILEmitter(); var codeStream = emitter.NewCodeStream(); codeStream.EmitLdArga(0); codeStream.EmitLdArg(1); codeStream.Emit(ILOpcode.call, emitter.NewToken(underlyingCompareToMethod)); codeStream.Emit(ILOpcode.ret); return(emitter.Link(method)); } } break; case "EqualityComparerHelpers": { if (owningType.Namespace != "Internal.IntrinsicSupport") { return(null); } if (methodName == "EnumOnlyEquals") { // EnumOnlyEquals would basically like to do this: // static bool EnumOnlyEquals<T>(T x, T y) where T: struct => x == y; // This is not legal though. // We don't want to do this: // static bool EnumOnlyEquals<T>(T x, T y) where T: struct => x.Equals(y); // Because it would box y. // So we resort to some per-instantiation magic. TypeDesc elementType = method.Instantiation[0]; if (!elementType.IsEnum) { return(null); } ILOpcode convInstruction; if (((DefType)elementType).InstanceFieldSize.AsInt <= 4) { convInstruction = ILOpcode.conv_i4; } else { Debug.Assert(((DefType)elementType).InstanceFieldSize.AsInt == 8); convInstruction = ILOpcode.conv_i8; } return(new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldarg_0, (byte)convInstruction, (byte)ILOpcode.ldarg_1, (byte)convInstruction, (byte)ILOpcode.prefix1, unchecked ((byte)ILOpcode.ceq), (byte)ILOpcode.ret, }, Array.Empty <LocalVariableDefinition>(), null)); } else if (methodName == "GetComparerForReferenceTypesOnly") { TypeDesc elementType = method.Instantiation[0]; if (!elementType.IsRuntimeDeterminedSubtype && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) && !elementType.IsGCPointer) { return(new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.ret }, Array.Empty <LocalVariableDefinition>(), null)); } } else if (methodName == "StructOnlyEquals") { TypeDesc elementType = method.Instantiation[0]; if (!elementType.IsRuntimeDeterminedSubtype && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) && !elementType.IsGCPointer) { Debug.Assert(elementType.IsValueType); TypeSystemContext context = elementType.Context; MetadataType helperType = context.SystemModule.GetKnownType("Internal.IntrinsicSupport", "EqualityComparerHelpers"); MethodDesc methodToCall; if (elementType.IsEnum) { methodToCall = helperType.GetKnownMethod("EnumOnlyEquals", null).MakeInstantiatedMethod(elementType); } else if (elementType.IsNullable && ComparerIntrinsics.ImplementsIEquatable(elementType.Instantiation[0])) { methodToCall = helperType.GetKnownMethod("StructOnlyEqualsNullable", null).MakeInstantiatedMethod(elementType.Instantiation[0]); } else if (ComparerIntrinsics.ImplementsIEquatable(elementType)) { methodToCall = helperType.GetKnownMethod("StructOnlyEqualsIEquatable", null).MakeInstantiatedMethod(elementType); } else { methodToCall = helperType.GetKnownMethod("StructOnlyNormalEquals", null).MakeInstantiatedMethod(elementType); } return(new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1, (byte)ILOpcode.call, 1, 0, 0, 0, (byte)ILOpcode.ret }, Array.Empty <LocalVariableDefinition>(), new object[] { methodToCall })); } } } break; } return(null); }