internal unsafe static uint GetTokenReference(Type type, bool throw_exception = true) { if (type.IsGenericType) { type = type.GetGenericTypeDefinition(); } var asm_name = type.Module.Assembly.GetName().Name; // First check if there's a full token reference to this type var token = GetFullTokenReference(asm_name, type.Module.MetadataToken, type.MetadataToken); if (token != uint.MaxValue) { return(token); } // If type.Module.MetadataToken != 1, then the token must be a full token, which is not the case because we've already checked, so throw an exception. if (type.Module.MetadataToken != 1) { if (!throw_exception) { return(Runtime.INVALID_TOKEN_REF); } throw ErrorHelper.CreateError(8025, $"Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because its module's metadata token is {type.Module.MetadataToken} when expected 1."); } var map = Runtime.options->RegistrationMap; // Find the assembly index in our list of registered assemblies. int assembly_index = -1; for (int i = 0; i < map->assembly_count; i++) { var name_ptr = Marshal.ReadIntPtr(map->assembly, (int)i * IntPtr.Size); if (Runtime.StringEquals(name_ptr, asm_name)) { assembly_index = i; break; } } // If the assembly isn't registered, then the token must be a full token (which it isn't, because we've already checked). if (assembly_index == -1) { if (!throw_exception) { return(Runtime.INVALID_TOKEN_REF); } throw ErrorHelper.CreateError(8025, $"Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because the assembly couldn't be found in the list of registered assemblies."); } if (assembly_index > 127) { if (!throw_exception) { return(Runtime.INVALID_TOKEN_REF); } throw ErrorHelper.CreateError(8025, $"Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because the assembly index {assembly_index} is not valid (must be <= 127)."); } return((uint)((type.MetadataToken << 8) + (assembly_index << 1))); }
internal static IntPtr GetBlockForDelegate(MethodInfo minfo, object @delegate, uint token_ref, string signature) { if (@delegate is null) { return(IntPtr.Zero); } if (!(@delegate is Delegate)) { throw ErrorHelper.CreateError(8016, $"Unable to convert delegate to block for the return value for the method {minfo.DeclaringType.FullName}.{minfo.Name}, because the input isn't a delegate, it's a {@delegate.GetType ().FullName}. {Constants.PleaseFileBugReport}"); } Type delegateProxyType = GetDelegateProxyType(minfo, token_ref, out var baseMethod); if (baseMethod is null) { baseMethod = minfo; // 'baseMethod' is only used in error messages, and if it's null, we just use the closest alternative we have (minfo). } if (delegateProxyType == null) { throw ErrorHelper.CreateError(8012, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: DelegateType is null. {Constants.PleaseFileBugReport}"); } var delegateProxyField = delegateProxyType.GetField("Handler", BindingFlags.NonPublic | BindingFlags.Static); if (delegateProxyField is null) { throw ErrorHelper.CreateError(8013, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: DelegateType ({delegateProxyType.FullName}) specifies a type without a 'Handler' field. {Constants.PleaseFileBugReport}"); } var handlerDelegate = delegateProxyField.GetValue(null); if (handlerDelegate is null) { throw ErrorHelper.CreateError(8014, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: The DelegateType's ({delegateProxyType.FullName}) 'Handler' field is null. {Constants.PleaseFileBugReport}"); } if (!(handlerDelegate is Delegate)) { throw ErrorHelper.CreateError(8015, $"Invalid DelegateProxyAttribute for the return value for the method {baseMethod.DeclaringType.FullName}.{baseMethod.Name}: The DelegateType's ({delegateProxyType.FullName}) 'Handler' field is not a delegate, it's a {handlerDelegate.GetType ().FullName}. {Constants.PleaseFileBugReport}"); } // We now have the information we need to create the block. // Note that we must create a heap-allocated block, so we // start off by creating a stack-allocated block, and then // call _Block_copy, which will create a heap-allocated block // with the proper reference count. BlockLiteral block = new BlockLiteral(); if (signature is null) { if (Runtime.DynamicRegistrationSupported) { block.SetupBlock((Delegate)handlerDelegate, (Delegate)@delegate); } else { throw ErrorHelper.CreateError(8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName})."); } } else { block.SetupBlockImpl((Delegate)handlerDelegate, (Delegate)@delegate, true, signature); } var rv = _Block_copy(ref block); block.CleanupBlock(); return(rv); }
internal static IntPtr GetBlockForDelegate(MethodInfo minfo, object @delegate, string signature) { if (@delegate == null) { return(IntPtr.Zero); } if (!(@delegate is Delegate)) { throw ErrorHelper.CreateError(8016, "Unable to convert delegate to block for the return value for the method {0}.{1}, because the input isn't a delegate, it's a {1}. Please file a bug at http://bugzilla.xamarin.com.", minfo.DeclaringType.FullName, minfo.Name, @delegate.GetType().FullName); } var baseMethod = minfo.GetBaseDefinition(); var delegateProxies = baseMethod.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(DelegateProxyAttribute), false); if (delegateProxies.Length == 0) { throw ErrorHelper.CreateError(8011, "Unable to locate the delegate to block conversion attribute ([DelegateProxy]) for the return value for the method {0}.{1}. Please file a bug at http://bugzilla.xamarin.com.", baseMethod.DeclaringType.FullName, baseMethod.Name); } var delegateProxy = (DelegateProxyAttribute)delegateProxies [0]; if (delegateProxy.DelegateType == null) { throw ErrorHelper.CreateError(8012, "Invalid DelegateProxyAttribute for the return value for the method {0}.{1}: DelegateType is null. Please file a bug at http://bugzilla.xamarin.com.", baseMethod.DeclaringType.FullName, baseMethod.Name); } var delegateProxyField = delegateProxy.DelegateType.GetField("Handler", BindingFlags.NonPublic | BindingFlags.Static); if (delegateProxyField == null) { throw ErrorHelper.CreateError(8013, "Invalid DelegateProxyAttribute for the return value for the method {0}.{1}: DelegateType ({2}) specifies a type without a 'Handler' field. Please file a bug at http://bugzilla.xamarin.com.", baseMethod.DeclaringType.FullName, baseMethod.Name, delegateProxy.DelegateType.FullName); } var handlerDelegate = delegateProxyField.GetValue(null); if (handlerDelegate == null) { throw ErrorHelper.CreateError(8014, "Invalid DelegateProxyAttribute for the return value for the method {0}.{1}: The DelegateType's ({2}) 'Handler' field is null. Please file a bug at http://bugzilla.xamarin.com.", baseMethod.DeclaringType.FullName, baseMethod.Name, delegateProxy.DelegateType.FullName); } if (!(handlerDelegate is Delegate)) { throw ErrorHelper.CreateError(8015, "Invalid DelegateProxyAttribute for the return value for the method {0}.{1}: The DelegateType's ({2}) 'Handler' field is not a delegate, it's a {3}. Please file a bug at http://bugzilla.xamarin.com.", baseMethod.DeclaringType.FullName, baseMethod.Name, delegateProxy.DelegateType.FullName, handlerDelegate.GetType().FullName); } // We now have the information we need to create the block. // Note that we must create a heap-allocated block, so we // start off by creating a stack-allocated block, and then // call _Block_copy, which will create a heap-allocated block // with the proper reference count. BlockLiteral block = new BlockLiteral(); if (signature == null) { if (Runtime.DynamicRegistrationSupported) { block.SetupBlock((Delegate)handlerDelegate, (Delegate)@delegate); } else { throw ErrorHelper.CreateError(8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName})."); } } else { block.SetupBlockImpl((Delegate)handlerDelegate, (Delegate)@delegate, true, signature); } var rv = _Block_copy(ref block); block.CleanupBlock(); return(rv); }
// Return value: NULL or a MonoObject* that must be released with xamarin_mono_object_safe_release. // Any MonoObject* ref parameters must also be retained and must be released with xamarin_mono_object_release. static object InvokeMethod(MethodBase method, object instance, IntPtr native_parameters) { var methodParameters = method.GetParameters(); var parameters = new object [methodParameters.Length]; var inputParameters = new object [methodParameters.Length]; var nativeParameters = new IntPtr [methodParameters.Length]; // Copy native array of void* to managed array of IntPtr to make the subsequent code simpler. unsafe { IntPtr *nativeParams = (IntPtr *)native_parameters; for (var i = 0; i < methodParameters.Length; i++) { nativeParameters [i] = nativeParams [i]; } } // Log our input log_coreclr($"InvokeMethod ({method.DeclaringType.FullName}::{method}, {instance}, 0x{native_parameters.ToString ("x")})"); for (var i = 0; i < methodParameters.Length; i++) { var nativeParam = nativeParameters [i]; var p = methodParameters [i]; var paramType = p.ParameterType; if (paramType.IsByRef) { paramType = paramType.GetElementType(); } log_coreclr($" Argument #{i + 1}: Type = {p.ParameterType.FullName} IsByRef: {p.ParameterType.IsByRef} IsOut: {p.IsOut} IsClass: {paramType.IsClass} IsInterface: {paramType.IsInterface} NativeParameter: 0x{nativeParam.ToString ("x")}"); } // Process the arguments, and convert to what MethodBase.Invoke expects for (var i = 0; i < methodParameters.Length; i++) { var nativeParam = nativeParameters [i]; var p = methodParameters [i]; var paramType = p.ParameterType; var isByRef = paramType.IsByRef; if (isByRef) { paramType = paramType.GetElementType(); } log_coreclr($" Marshalling #{i + 1}: IntPtr => 0x{nativeParam.ToString ("x")} => {p.ParameterType.FullName} [...]"); if (paramType == typeof(IntPtr)) { log_coreclr($" IntPtr"); if (isByRef) { if (p.IsOut) { parameters [i] = Marshal.AllocHGlobal(IntPtr.Size); } else { parameters [i] = nativeParam == IntPtr.Zero ? IntPtr.Zero : Marshal.ReadIntPtr(nativeParam); } } else { parameters [i] = nativeParam == IntPtr.Zero ? IntPtr.Zero : Marshal.ReadIntPtr(nativeParam); } log_coreclr($" => 0x{((IntPtr) parameters [i]).ToString ("x")}"); } else if (paramType.IsClass || paramType.IsInterface || (paramType.IsValueType && IsNullable(paramType))) { log_coreclr($" IsClass/IsInterface/IsNullable IsByRef: {isByRef} IsOut: {p.IsOut} ParameterType: {paramType}"); if (nativeParam != IntPtr.Zero) { unsafe { MonoObject *mono_obj = (MonoObject *)nativeParam; // dereference if it's a byref type if (isByRef) { mono_obj = *(MonoObject **)mono_obj; } // get the object parameters [i] = GetMonoObjectTarget(mono_obj); } } log_coreclr($" => {(parameters [i] == null ? "<null>" : parameters [i].GetType ().FullName)}"); } else if (paramType.IsValueType) { log_coreclr($" IsValueType IsByRef: {isByRef} IsOut: {p.IsOut} nativeParam: 0x{nativeParam.ToString ("x")} ParameterType: {paramType}"); if (nativeParam != IntPtr.Zero) { // We need to unwrap nullable types and enum types to their underlying struct type. var structType = paramType; Type enumType = null; if (IsNullable(structType)) { structType = Nullable.GetUnderlyingType(structType); } if (structType.IsEnum) { enumType = structType; structType = Enum.GetUnderlyingType(structType); } // convert the pointer to the corresponding structure var vt = PtrToStructure(nativeParam, structType); // convert the structure to the enum type if that's what we need if (enumType != null) { vt = Enum.ToObject(enumType, vt); } parameters [i] = vt; } log_coreclr($" => {(parameters [i] == null ? "<null>" : parameters [i].ToString ())}"); } else { throw ErrorHelper.CreateError(8037, Errors.MX8037 /* Don't know how to marshal the parameter of type {p.ParameterType.FullName} for parameter {p.Name} in call to {method} */, p.ParameterType.FullName, p.Name, method); } } // Make a copy of the array of parameters, so that we can figure out if there were any ref parameters that the method modified. parameters.CopyTo(inputParameters, 0); // Call the actual method log_coreclr($" Invoking..."); object rv = null; try { rv = method.Invoke(instance, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, parameters, null); } catch (TargetInvocationException tie) { var ex = tie.InnerException ?? tie; // This will re-throw the original exception and preserve the stacktrace. ExceptionDispatchInfo.Capture(ex).Throw(); } // Copy any byref parameters back out again var byrefParameterCount = 0; for (var i = 0; i < methodParameters.Length; i++) { var p = methodParameters [i]; if (!p.IsOut && !p.ParameterType.IsByRef) { continue; } byrefParameterCount++; log_coreclr($" Marshalling #{i + 1} back (Type: {p.ParameterType.FullName}) value: {(parameters [i] == null ? "<null>" : parameters [i].GetType ().FullName)}"); var parameterType = p.ParameterType.GetElementType(); var isMonoObject = parameterType.IsClass || parameterType.IsInterface || (parameterType.IsValueType && IsNullable(parameterType)); var nativeParam = nativeParameters [i]; if (nativeParam == IntPtr.Zero) { log_coreclr($" No output pointer was provided."); continue; } if (parameters [i] == inputParameters [i]) { log_coreclr($" The argument didn't change, no marshalling required"); if (parameters [i] != null && parameterType != typeof(IntPtr) && isMonoObject) { // byref parameters must be retained xamarin_mono_object_retain(Marshal.ReadIntPtr(nativeParam)); } continue; } if (parameterType == typeof(IntPtr)) { Marshal.WriteIntPtr(nativeParam, (IntPtr)parameters [i]); log_coreclr($" IntPtr: 0x{((IntPtr) parameters [i]).ToString ("x")} => Type: {parameters [i]?.GetType ()} nativeParam: 0x{nativeParam.ToString ("x")}"); } else if (isMonoObject) { var ptr = GetMonoObject(parameters [i]); Marshal.WriteIntPtr(nativeParam, ptr); log_coreclr($" IsClass/IsInterface/IsNullable: {(parameters [i] == null ? "<null>" : parameters [i].GetType ().FullName)} nativeParam: 0x{nativeParam.ToString ("x")} -> MonoObject: 0x{ptr.ToString ("x")}"); } else if (parameterType.IsValueType) { StructureToPtr(parameters [i], nativeParam); log_coreclr($" IsValueType: {(parameters [i] == null ? "<null>" : parameters [i].ToString ())} nativeParam: 0x{nativeParam.ToString ("x")}"); } else { throw ErrorHelper.CreateError(8038, Errors.MX8038 /* Don't know how to marshal back the parameter of type {p.ParameterType.FullName} for parameter {p.Name} in call to {method} */, p.ParameterType.FullName, p.Name, method); } } // we're done! log_coreclr($" Invoke complete with {byrefParameterCount} ref parameters and return value of type {rv?.GetType ()}"); return(rv); }