public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld) { if (structure == null) { throw new ArgumentNullException(nameof(structure)); } if (ptr == IntPtr.Zero) { throw new ArgumentNullException(nameof(ptr)); } if (fDeleteOld) { DestroyStructure(ptr, structure.GetType()); } RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) { throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(structure)); } IntPtr marshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) { marshalStub = IntPtr.Zero; } } else { marshalStub = RuntimeAugments.InteropCallbacks.GetStructMarshalStub(structureTypeHandle); } if (marshalStub != IntPtr.Zero) { if (structureTypeHandle.IsValueType()) { ((delegate * < ref byte, ref byte, void >)marshalStub)(ref structure.GetRawData(), ref *(byte *)ptr); } else { ((delegate * < object, ref byte, void >)marshalStub)(structure, ref *(byte *)ptr); } } else { nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); Buffer.Memmove(ref *(byte *)ptr, ref structure.GetRawData(), size); } }
internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) { RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; // Boxed struct start at offset 1 (EEType* at offset 0) while class start at offset 0 int offset = structureTypeHandle.IsValueType() ? 1 : 0; IntPtr unmarshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { unmarshalStub = IntPtr.Zero; } } else { unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); } if (unmarshalStub != IntPtr.Zero) { InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { CalliIntrinsics.Call <int>( unmarshalStub, (void *)ptr, // unsafe (no need to adjust as it is always struct) ((void *)((IntPtr *)unboxedStructPtr + offset)) // safe (need to adjust offset as it could be class) ); }); } else { int structSize = Marshal.SizeOf(structure); InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { InteropExtensions.Memcpy( (IntPtr)((IntPtr *)unboxedStructPtr + offset), // safe (need to adjust offset as it could be class) ptr, // unsafe (no need to adjust as it is always struct) structSize ); }); } }
public override int GetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle) { if (TryGetStructUnsafeStructSize(structureTypeHandle, out int size)) { return(size); } // IsBlittable() checks whether the type contains GC references. It is approximate check with false positives. // This fallback path will return incorrect answer for types that do not contain GC references, but that are // not actually blittable; e.g. for types with bool fields. if (structureTypeHandle.IsBlittable() && structureTypeHandle.IsValueType()) { return(structureTypeHandle.GetValueTypeSize()); } throw new MissingInteropDataException(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)); }
private static int SizeOf(Type t) { Debug.Assert(t != null, "t"); if (t.TypeHandle.IsGenericType()) { throw new ArgumentException(SR.Argument_NeedNonGenericType, "t"); } RuntimeTypeHandle typeHandle = t.TypeHandle; if (!(typeHandle.IsBlittable() && typeHandle.IsValueType())) { throw new ArgumentException(SR.Argument_NeedStructWithNoRefs); } return(typeHandle.GetValueTypeSize()); }
internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) { RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; IntPtr unmarshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { unmarshalStub = IntPtr.Zero; } } else { unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); } if (unmarshalStub != IntPtr.Zero) { if (structureTypeHandle.IsValueType()) { CalliIntrinsics.Call( unmarshalStub, ref *(byte *)ptr, ref structure.GetRawData()); } else { CalliIntrinsics.Call( unmarshalStub, ref *(byte *)ptr, structure); } } else { nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); fixed(byte *pDest = &structure.GetRawData()) { Buffer.Memmove(pDest, (byte *)ptr, size); } } }
internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) { RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; // Boxed struct start at offset 1 (EEType* at offset 0) while class start at offset 0 nuint offset = structureTypeHandle.IsValueType() ? (nuint)sizeof(IntPtr) : 0; IntPtr unmarshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { unmarshalStub = IntPtr.Zero; } } else { unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); } ref byte dest = ref Unsafe.AddByteOffset(ref Unsafe.As<IntPtr, byte>(ref structure.m_pEEType), offset);
internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) { RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; IntPtr unmarshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { unmarshalStub = IntPtr.Zero; } } else { unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); } if (unmarshalStub != IntPtr.Zero) { if (structureTypeHandle.IsValueType()) { ((delegate * < ref byte, ref byte, void >)unmarshalStub)(ref *(byte *)ptr, ref structure.GetRawData()); } else { ((delegate * < ref byte, object, void >)unmarshalStub)(ref *(byte *)ptr, structure); } } else { nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); Buffer.Memmove(ref structure.GetRawData(), ref *(byte *)ptr, size); } }
private protected unsafe void CheckArguments( Span <object?> copyOfParameters, IntPtr *byrefParameters, Span <ParameterCopyBackAction> shouldCopyBack, ReadOnlySpan <object?> parameters, RuntimeType[] sigTypes, Binder?binder, CultureInfo?culture, BindingFlags invokeAttr ) { Debug.Assert(parameters.Length > 0); ParameterInfo[]? paramInfos = null; for (int i = 0; i < parameters.Length; i++) { ParameterCopyBackAction copyBackArg = default; bool isValueType = false; object? arg = parameters[i]; RuntimeType sigType = sigTypes[i]; // Convert a Type.Missing to the default value. if (ReferenceEquals(arg, Type.Missing)) { paramInfos ??= GetParametersNoCopy(); ParameterInfo paramInfo = paramInfos[i]; if (paramInfo.DefaultValue == DBNull.Value) { throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); } arg = paramInfo.DefaultValue; if (sigType.IsNullableOfT) { copyBackArg = ParameterCopyBackAction.CopyNullable; if (arg is not null) { // For nullable Enum types, the ParameterInfo.DefaultValue returns a raw value which // needs to be parsed to the Enum type, for more info: https://github.com/dotnet/runtime/issues/12924 Type argumentType = sigType.GetGenericArguments()[0]; if (argumentType.IsEnum) { arg = Enum.ToObject(argumentType, arg); } } } else { copyBackArg = ParameterCopyBackAction.Copy; } } if (arg is null) { // Fast path for null reference types. isValueType = RuntimeTypeHandle.IsValueType(sigType); if (isValueType || RuntimeTypeHandle.IsByRef(sigType)) { isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } } else { RuntimeType argType = (RuntimeType)arg.GetType(); if (ReferenceEquals(argType, sigType)) { // Fast path when the value's type matches the signature type. isValueType = RuntimeTypeHandle.IsValueType(argType); } else if (sigType.TryByRefFastPath(ref arg, ref isValueType)) { // Fast path when the value's type matches the signature type of a byref parameter. copyBackArg = ParameterCopyBackAction.Copy; } else { // Slow path that supports type conversions. isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } } // We need to perform type safety validation against the incoming arguments, but we also need // to be resilient against the possibility that some other thread (or even the binder itself!) // may mutate the array after we've validated the arguments but before we've properly invoked // the method. The solution is to copy the arguments to a different, not-user-visible buffer // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are // considered user-visible to threads which may still be holding on to returned instances. // This separate array is also used to hold default values when 'null' is specified for value // types, and also used to hold the results from conversions such as from Int16 to Int32. For // compat, these default values and conversions are not be applied to the incoming arguments. shouldCopyBack[i] = copyBackArg; copyOfParameters[i] = arg; if (isValueType) { #if !MONO // Temporary until Mono is updated. Debug.Assert(arg != null); Debug.Assert( arg.GetType() == sigType || (sigType.IsPointer && (arg.GetType() == typeof(IntPtr) || arg.GetType() == typeof(UIntPtr))) || (sigType.IsByRef && arg.GetType() == RuntimeTypeHandle.GetElementType(sigType)) || ((sigType.IsEnum || arg.GetType().IsEnum) && RuntimeType.GetUnderlyingType((RuntimeType)arg.GetType()) == RuntimeType.GetUnderlyingType(sigType))); #endif ByReference valueTypeRef = ByReference.Create(ref copyOfParameters[i] !.GetRawData()); *(ByReference *)(byrefParameters + i) = valueTypeRef; } else { ByReference objRef = ByReference.Create(ref copyOfParameters[i]); *(ByReference *)(byrefParameters + i) = objRef; } } }
private protected unsafe void CheckArguments( Span <object?> copyOfParameters, IntPtr *byrefParameters, Span <bool> shouldCopyBack, ReadOnlySpan <object?> parameters, RuntimeType[] sigTypes, Binder?binder, CultureInfo?culture, BindingFlags invokeAttr ) { Debug.Assert(!parameters.IsEmpty); ParameterInfo[]? paramInfos = null; for (int i = 0; i < parameters.Length; i++) { bool copyBackArg = false; bool isValueType; object? arg = parameters[i]; RuntimeType sigType = sigTypes[i]; if (arg is null) { // Fast path that avoids calling CheckValue() for reference types. isValueType = RuntimeTypeHandle.IsValueType(sigType); if (isValueType || RuntimeTypeHandle.IsByRef(sigType)) { isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } } else if (ReferenceEquals(arg.GetType(), sigType)) { // Fast path that avoids calling CheckValue() when argument value matches the signature type. isValueType = RuntimeTypeHandle.IsValueType(sigType); } else { paramInfos ??= GetParametersNoCopy(); ParameterInfo paramInfo = paramInfos[i]; if (!ReferenceEquals(arg, Type.Missing)) { isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } else { if (paramInfo.DefaultValue == DBNull.Value) { throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); } arg = paramInfo.DefaultValue; if (ReferenceEquals(arg?.GetType(), sigType)) { isValueType = RuntimeTypeHandle.IsValueType(sigType); } else { isValueType = sigType.CheckValue(ref arg, ref copyBackArg, binder, culture, invokeAttr); } } } // We need to perform type safety validation against the incoming arguments, but we also need // to be resilient against the possibility that some other thread (or even the binder itself!) // may mutate the array after we've validated the arguments but before we've properly invoked // the method. The solution is to copy the arguments to a different, not-user-visible buffer // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are // considered user-visible to threads which may still be holding on to returned instances. // This separate array is also used to hold default values when 'null' is specified for value // types, and also used to hold the results from conversions such as from Int16 to Int32; these // default values and conversions should not be applied to the incoming arguments. shouldCopyBack[i] = copyBackArg; copyOfParameters[i] = arg; if (isValueType) { #if DEBUG // Once Mono has managed conversion logic, VerifyValueType() can be lifted here as Asserts. sigType.VerifyValueType(arg); #endif ByReference <byte> valueTypeRef = new(ref copyOfParameters[i] !.GetRawData()); *(ByReference <byte> *)(byrefParameters + i) = valueTypeRef; } else { ByReference <object?> objRef = new(ref copyOfParameters[i]); *(ByReference <object?> *)(byrefParameters + i) = objRef; } } }
public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld) { if (structure == null) { throw new ArgumentNullException(nameof(structure)); } if (ptr == IntPtr.Zero) { throw new ArgumentNullException(nameof(ptr)); } if (fDeleteOld) { DestroyStructure(ptr, structure.GetType()); } RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) { throw new ArgumentException(nameof(structure), SR.Argument_NeedNonGenericObject); } // Boxed struct start at offset 1 (EEType* at offset 0) while class start at offset 0 int offset = structureTypeHandle.IsValueType() ? 1 : 0; IntPtr marshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) { marshalStub = IntPtr.Zero; } } else { marshalStub = RuntimeAugments.InteropCallbacks.GetStructMarshalStub(structureTypeHandle); } if (marshalStub != IntPtr.Zero) { InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { CalliIntrinsics.Call <int>( marshalStub, ((void *)((IntPtr *)unboxedStructPtr + offset)), // safe (need to adjust offset as it could be class) (void *)ptr // unsafe (no need to adjust as it is always struct) ); }); } else { int structSize = Marshal.SizeOf(structure); InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { InteropExtensions.Memcpy( ptr, // unsafe (no need to adjust as it is always struct) (IntPtr)((IntPtr *)unboxedStructPtr + offset), // safe (need to adjust offset as it could be class) structSize ); }); } }