public void RegisterMethod(Type type, MethodInfo minfo, ExportAttribute ea) { if (!IsNSObject(type)) { throw new ArgumentException(string.Format("Cannot register methods on '{0}'; it does not inherit from NSObject.", type.FullName)); } if (!minfo.IsStatic && type != minfo.DeclaringType) { throw new ArgumentException(string.Format("Cannot register the instance method '{0}' on the type '{1}'. The type to connect to ('{2}') must match the method's type ('{1}').", minfo.Name, type.FullName, minfo.DeclaringType.FullName)); } List <Exception> exceptions = null; var objcType = RegisterType(type, ref exceptions); var method = new ObjCMethod(this, objcType, minfo); if (method.SetExportAttribute(ea, ref exceptions)) { if (exceptions == null) { objcType.Add(method, ref exceptions); if (exceptions == null) { RegisterMethod(method); } } } if (exceptions != null) { throw exceptions.Count == 1 ? exceptions [0] : new AggregateException(exceptions); } }
// // WriteFacetAsMethod // public void WriteFacetAsMethod(MethodFacet facet, Dictionary <string, object> options = null) { ObjCMethod method = new ObjCMethod(this, facet, options); if (!method.IsValid) { return; } // write method headerdoc info if (OutputFileType == OutputType.Interface) { string tab = " "; string tab2 = " "; WriteLine($""); WriteLine($"/**"); PushIndent(tab); WriteLine($"Managed method."); WriteLine($"@textblock"); WriteLine($"Name"); PushIndent(tab2); WriteLine($"{(method.IsConstructorMethod ? ".ctor" : method.MonoMethodName)}"); PopIndent(); WriteLine($""); WriteLine($"Params"); PushIndent(tab2); Write($"{WriteFacetTypeInfo(facet.Parameters)}"); PopIndent(); if (facet.IsGenericMethodDefinition) { WriteLine($""); WriteLine($"Generics"); PushIndent(tab2); WriteLine($"{WriteFacetTypeInfo(facet.GenericMethodDefinitionGenericTypeArguments)}"); PopIndent(); } WriteLine($""); WriteLine($"Return"); PushIndent(tab2); WriteLine($"{WriteFacetTypeInfo(facet)}"); PopIndent(); WriteLine($"@/textblock"); PopIndent(); WriteLine($"*/"); } else { WriteLine($""); } // normalise the return type for use in invocation api string invokeApiObjCReturnTypeDecl = NormaliseObjCTypeDecl(method.ObjCTypeDecl, ObjCTypeDeclNormalisation.InvokeApiReturnType); // write method declaration WriteLine($"{method.ObjCMethodType} ({invokeApiObjCReturnTypeDecl}){method.ObjCMethodName}{method.ObjCMethodParameters}{LT}"); // write method body if (OutputFileType == OutputType.Implementation) { // generate type warnings GenerateTypeWarnings(facet); GenerateTypeWarnings(facet.Parameters); WriteLine("{"); PushTabIndent(); // method return type is void if (method.ObjCTypeDecl == "void") { WriteNz(method.ReferencePreProcess); WriteLine($"{method.GetExpression};"); WriteNz(method.ReferencePostProcess); } // method is a constructor else if (method.IsConstructorMethod) { WriteNz(method.ReferencePreProcess); WriteLine($"{invokeApiObjCReturnTypeDecl} object = {method.GetExpression};"); WriteNz(method.ReferencePostProcess); WriteLine("return object;"); } // non constructor method returning a value else { WriteNz(method.ReferencePreProcess); WriteLine($"MonoObject *{ManagedVariableName} = {method.GetExpression};"); WriteNz(method.ReferencePostProcess); WriteLine($"return {method.ManagedValueToObjC};"); } PopIndent(); WriteLine("}"); } }
bool RegisterMethod(ObjCMethod method) { IntPtr reg_handle; IntPtr tramp; reg_handle = (method.IsStatic && !method.IsCategoryInstance) ? Class.object_getClass(method.DeclaringType.Handle) : method.DeclaringType.Handle; switch (method.Trampoline) { case Trampoline.Constructor: tramp = Method.ConstructorTrampoline; break; case Trampoline.Double: tramp = Method.DoubleTrampoline; break; case Trampoline.Long: tramp = Method.LongTrampoline; break; case Trampoline.Normal: tramp = Method.Trampoline; break; case Trampoline.Release: tramp = Method.ReleaseTrampoline; break; case Trampoline.Retain: tramp = Method.RetainTrampoline; break; case Trampoline.Single: tramp = Method.SingleTrampoline; break; case Trampoline.Static: tramp = Method.StaticTrampoline; break; case Trampoline.StaticDouble: tramp = Method.StaticDoubleTrampoline; break; case Trampoline.StaticLong: tramp = Method.StaticLongTrampoline; break; case Trampoline.StaticSingle: tramp = Method.StaticSingleTrampoline; break; case Trampoline.StaticStret: tramp = Method.StaticStretTrampoline; break; case Trampoline.Stret: tramp = Method.StretTrampoline; break; case Trampoline.X86_DoubleABI_StaticStretTrampoline: tramp = Method.X86_DoubleABI_StaticStretTrampoline; break; case Trampoline.X86_DoubleABI_StretTrampoline: tramp = Method.X86_DoubleABI_StretTrampoline; break; #if MONOMAC case Trampoline.CopyWithZone1: tramp = Method.CopyWithZone1; break; case Trampoline.CopyWithZone2: tramp = Method.CopyWithZone2; break; #endif case Trampoline.GetGCHandle: tramp = Method.GetGCHandleTrampoline; break; case Trampoline.SetGCHandle: tramp = Method.SetGCHandleTrampoline; break; default: throw ErrorHelper.CreateError(4144, "Cannot register the method '{0}.{1}' since it does not have an associated trampoline. Please file a bug report at http://bugzilla.xamarin.com", method.DeclaringType.Type.FullName, method.Name); } return(Class.class_addMethod(reg_handle, Selector.GetHandle(method.Selector), tramp, method.Signature)); }
void Specialize(AutoIndentStringBuilder sb, ObjCMethod method, List<Exception> exceptions) { var isGeneric = method.DeclaringType.IsGeneric; switch (method.CurrentTrampoline) { case Trampoline.Retain: sb.WriteLine ("-(id) retain"); sb.WriteLine ("{"); sb.WriteLine ("return xamarin_retain_trampoline (self, _cmd);"); sb.WriteLine ("}"); sb.WriteLine (); return; case Trampoline.Release: sb.WriteLine ("-(void) release"); sb.WriteLine ("{"); sb.WriteLine ("xamarin_release_trampoline (self, _cmd);"); sb.WriteLine ("}"); sb.WriteLine (); return; case Trampoline.GetGCHandle: sb.WriteLine ("-(int) xamarinGetGCHandle"); sb.WriteLine ("{"); sb.WriteLine ("return __monoObjectGCHandle.gc_handle;"); sb.WriteLine ("}"); sb.WriteLine (); return; case Trampoline.SetGCHandle: sb.WriteLine ("-(void) xamarinSetGCHandle: (int) gc_handle"); sb.WriteLine ("{"); sb.WriteLine ("__monoObjectGCHandle.gc_handle = gc_handle;"); sb.WriteLine ("__monoObjectGCHandle.native_object = self;"); sb.WriteLine ("}"); sb.WriteLine (); return; case Trampoline.Constructor: if (isGeneric) { sb.WriteLine (GetObjCSignature (method, exceptions)); sb.WriteLine ("{"); sb.WriteLine ("xamarin_throw_product_exception (4126, \"Cannot construct an instance of the type '{0}' from Objective-C because the type is generic.\");\n", method.DeclaringType.Type.FullName.Replace ("/", "+")); sb.WriteLine ("return self;"); sb.WriteLine ("}"); return; } break; #if MONOMAC case Trampoline.CopyWithZone1: sb.AppendLine ("-(id) copyWithZone: (NSZone *) zone"); sb.AppendLine ("{"); sb.AppendLine ("id rv;"); sb.AppendLine ("int gchandle;"); sb.AppendLine (); sb.AppendLine ("gchandle = xamarin_get_gchandle_with_flags (self);"); sb.AppendLine ("if (gchandle != 0)"); sb.Indent ().AppendLine ("xamarin_set_gchandle (self, 0);").Unindent (); // Call the base class implementation sb.AppendLine ("rv = [super copyWithZone: zone];"); sb.AppendLine (); sb.AppendLine ("if (gchandle != 0)"); sb.Indent ().AppendLine ("xamarin_set_gchandle (self, gchandle);").Unindent (); sb.AppendLine (); sb.AppendLine ("return rv;"); sb.AppendLine ("}"); return; case Trampoline.CopyWithZone2: sb.AppendLine ("-(id) copyWithZone: (NSZone *) zone"); sb.AppendLine ("{"); sb.AppendLine ("return xamarin_copyWithZone_trampoline2 (self, _cmd, zone);"); sb.AppendLine ("}"); return; #endif } var rettype = string.Empty; var returntype = method.Method.ReturnType; var isStatic = method.IsStatic; var isInstanceCategory = method.IsCategoryInstance; var isCtor = false; var num_arg = method.Method.Parameters.Count; var descriptiveMethodName = method.DescriptiveMethodName; var name = GetUniqueTrampolineName ("native_to_managed_trampoline_" + descriptiveMethodName); var isVoid = returntype.FullName == "System.Void"; var arguments = new List<string> (); var merge_bodies = true; switch (method.CurrentTrampoline) { case Trampoline.None: case Trampoline.Normal: case Trampoline.Static: case Trampoline.Single: case Trampoline.Double: case Trampoline.Long: case Trampoline.StaticLong: case Trampoline.StaticDouble: case Trampoline.StaticSingle: case Trampoline.X86_DoubleABI_StaticStretTrampoline: case Trampoline.X86_DoubleABI_StretTrampoline: case Trampoline.StaticStret: case Trampoline.Stret: switch (returntype.FullName) { case "System.Int64": rettype = "long long"; break; case "System.UInt64": rettype = "unsigned long long"; break; case "System.Single": rettype = "float"; break; case "System.Double": rettype = "double"; break; default: rettype = ToObjCParameterType (returntype, descriptiveMethodName, exceptions, method.Method); break; } break; case Trampoline.Constructor: rettype = "id"; isCtor = true; break; default: return; } comment.Clear (); nslog_start.Clear (); nslog_end.Clear (); copyback.Clear (); invoke.Clear (); setup_call_stack.Clear (); body.Clear (); setup_return.Clear (); counter++; body.WriteLine ("{"); var indent = merge_bodies ? sb.Indentation : sb.Indentation + 1; body.Indentation = indent; copyback.Indentation = indent; invoke.Indentation = indent; setup_call_stack.Indentation = indent; setup_return.Indentation = indent; // A comment describing the managed signature if (trace) { nslog_start.Indentation = sb.Indentation; comment.Indentation = sb.Indentation; nslog_end.Indentation = sb.Indentation; comment.AppendFormat ("// {2} {0}.{1} (", method.Method.DeclaringType.FullName, method.Method.Name, method.Method.ReturnType.FullName); for (int i = 0; i < num_arg; i++) { var param = method.Method.Parameters [i]; if (i > 0) comment.Append (", "); comment.AppendFormat ("{0} {1}", param.ParameterType.FullName, param.Name); } comment.AppendLine (")"); comment.AppendLine ("// ArgumentSemantic: {0} IsStatic: {1} Selector: '{2}' Signature: '{3}'", method.ArgumentSemantic, method.IsStatic, method.Selector, method.Signature); } // a couple of debug printfs if (trace) { StringBuilder args = new StringBuilder (); nslog_start.AppendFormat ("NSLog (@\"{0} (this: %@, sel: %@", name); for (int i = 0; i < num_arg; i++) { var type = method.Method.Parameters [i].ParameterType; bool isRef = type.IsByReference; if (isRef) type = type.GetElementType (); var td = type.Resolve (); nslog_start.AppendFormat (", {0}: ", method.Method.Parameters [i].Name); args.Append (", "); switch (type.FullName) { case "System.Drawing.RectangleF": if (isRef) { nslog_start.Append ("%p : %@"); #if MMP args.AppendFormat ("p{0}, p{0} ? NSStringFromRect (*p{0}) : @\"NULL\"", i); #else args.AppendFormat ("p{0}, p{0} ? NSStringFromCGRect (*p{0}) : @\"NULL\"", i); #endif } else { nslog_start.Append ("%@"); #if MMP args.AppendFormat ("NSStringFromRect (p{0})", i); #else args.AppendFormat ("NSStringFromCGRect (p{0})", i); #endif } break; case "System.Drawing.PointF": if (isRef) { nslog_start.Append ("%p: %@"); #if MMP args.AppendFormat ("p{0}, p{0} ? NSStringFromPoint (*p{0}) : @\"NULL\"", i); #else args.AppendFormat ("p{0}, p{0} ? NSStringFromCGPoint (*p{0}) : @\"NULL\"", i); #endif } else { nslog_start.Append ("%@"); #if MMP args.AppendFormat ("NSStringFromPoint (p{0})", i); #else args.AppendFormat ("NSStringFromCGPoint (p{0})", i); #endif } break; default: bool unknown; var spec = GetPrintfFormatSpecifier (td, out unknown); if (unknown) { nslog_start.AppendFormat ("%{0}", spec); args.AppendFormat ("&p{0}", i); } else if (isRef) { nslog_start.AppendFormat ("%p *= %{0}", spec); args.AppendFormat ("p{0}, *p{0}", i); } else { nslog_start.AppendFormat ("%{0}", spec); args.AppendFormat ("p{0}", i); } break; } } string ret_arg = string.Empty; nslog_end.Append (nslog_start.ToString ()); if (!isVoid) { bool unknown; var spec = GetPrintfFormatSpecifier (method.Method.ReturnType.Resolve (), out unknown); if (!unknown) { nslog_end.Append (" ret: %"); nslog_end.Append (spec); ret_arg = ", res"; } } nslog_end.Append (") END\", self, NSStringFromSelector (_cmd)"); nslog_end.Append (args.ToString ()); nslog_end.Append (ret_arg); nslog_end.AppendLine (");"); nslog_start.Append (") START\", self, NSStringFromSelector (_cmd)"); nslog_start.Append (args.ToString ()); nslog_start.AppendLine (");"); } // prepare the parameters var baseMethod = GetBaseMethodInTypeHierarchy (method.Method); for (int i = 0; i < num_arg; i++) { var param = method.Method.Parameters [i]; var paramBase = baseMethod.Parameters [i]; var type = method.Parameters [i]; var objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method); var original_objctype = objctype; var isRef = type.IsByReference; var isOut = param.IsOut || paramBase.IsOut; var isArray = type is ArrayType; var isNativeEnum = false; var td = type.Resolve (); var isVariadic = i + 1 == num_arg && method.IsVariadic; if (isRef) { type = type.GetElementType (); td = type.Resolve (); original_objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method); objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method) + "*"; } else if (td.IsEnum) { type = SharedStatic.GetEnumUnderlyingType (td); isNativeEnum = IsDualBuild && SharedStatic.HasAttribute (td, ObjCRuntime, StringConstants.NativeAttribute); td = type.Resolve (); } switch (type.FullName) { case "System.Int64": case "System.UInt64": // We already show MT4145 if the underlying enum type isn't a long or ulong if (isNativeEnum) { string tp; string ntp; if (type.FullName == "System.UInt64") { tp = "unsigned long long"; ntp = "NSUInteger"; } else { tp = "long long"; ntp = "NSInteger"; } if (isRef || isOut) { setup_call_stack.AppendLine ("{1} nativeEnum{0} = 0;", i, tp); setup_call_stack.AppendLine ("arg_ptrs [{0}] = &nativeEnum{0};", i); copyback.AppendLine ("*p{0} = ({1}) nativeEnum{0};", ntp); } else { setup_call_stack.AppendLine ("{1} nativeEnum{0} = p{0};", i, tp); setup_call_stack.AppendLine ("arg_ptrs [{0}] = &nativeEnum{0};", i); } break; } goto case "System.SByte"; case "System.SByte": case "System.Byte": case "System.Char": case "System.Int16": case "System.UInt16": case "System.Int32": case "System.UInt32": case "System.Single": case "System.Double": case "System.Boolean": if (isRef || isOut) { // The isOut semantics isn't quite correct here: we pass the actual input value to managed code. // In theory we should create a temp location and then use a writeback when done instead. // This should be safe though, since managed code (at least C#) can't actually observe the value. setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0};", i); } else { setup_call_stack.AppendLine ("arg_ptrs [{0}] = &p{0};", i); } break; case "System.IntPtr": if (isVariadic) { setup_call_stack.AppendLine ("va_list a{0};", i); setup_call_stack.AppendLine ("va_start (a{0}, p{1});", i, i - 1); setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i); copyback.AppendLine ("va_end (a{0});", i); } else if (isOut) { setup_call_stack.AppendLine ("{1} a{0} = 0;", i, objctype); setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i); copyback.AppendLine ("*p{0} = a{0};", i); } else if (isRef) { setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0};", i); } else { setup_call_stack.AppendLine ("{1} a{0} = p{0};", i, original_objctype); setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i); } break; case "ObjCRuntime.Selector": case CompatNamespace + ".ObjCRuntime.Selector": if (isRef) { if (isOut) { setup_call_stack.AppendLine ("void *a{0} = NULL;", i); } else { setup_call_stack.AppendLine ("void *a{0} = *p{0} ? xamarin_get_selector (*p{0}) : NULL;", i); } setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i); copyback.AppendLine ("*p{0} = a{0};", i); } else { setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0} ? xamarin_get_selector (p{0}) : NULL;", i); } break; case "ObjCRuntime.Class": case CompatNamespace + ".ObjCRuntime.Class": if (isRef) { if (isOut) { setup_call_stack.AppendLine ("void *a{0} = NULL;", i); } else { setup_call_stack.AppendLine ("void *a{0} = *p{0} ? xamarin_get_class (*p{0}) : NULL;", i); } setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i); copyback.AppendLine ("*p{0} = a{0};", i); } else { setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0} ? xamarin_get_class (p{0}) : NULL;", i); } break; case "System.String": // This should always be an NSString and never char* if (isRef) { if (isOut) { setup_call_stack.AppendLine ("MonoString *a{0} = NULL;", i); } else { setup_call_stack.AppendLine ("MonoString *a{0} = *p{0} ? mono_string_new (mono_domain_get (), [(*p{0}) UTF8String]) : NULL;", i); } setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i); copyback.AppendLine ("char *str{0} = mono_string_to_utf8 (a{0});", i); copyback.AppendLine ("*p{0} = [[NSString alloc] initWithUTF8String:str{0}];", i); copyback.AppendLine ("[*p{0} autorelease];", i); copyback.AppendLine ("mono_free (str{0});", i); } else { setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0} ? mono_string_new (mono_domain_get (), [p{0} UTF8String]) : NULL;", i); } break; default: if (isArray) { var elementType = ((ArrayType)type).ElementType; var isNativeObject = false; setup_call_stack.AppendLine ("if (p{0}) {{", i); setup_call_stack.AppendLine ("NSArray *arr = (NSArray *) p{0};", i); if (Driver.EnableDebug) setup_call_stack.AppendLine ("xamarin_check_objc_type (p{0}, [NSArray class], _cmd, self, {0}, managed_method);", i); setup_call_stack.AppendLine ("MonoClass *e_class;"); setup_call_stack.AppendLine ("MonoArray *marr;"); setup_call_stack.AppendLine ("MonoType *p;"); setup_call_stack.AppendLine ("int j;", i); setup_call_stack.AppendLine ("p = xamarin_get_parameter_type (managed_method, {0});", i); setup_call_stack.AppendLine ("e_class = mono_class_get_element_class (mono_class_from_mono_type (p));"); setup_call_stack.AppendLine ("marr = mono_array_new (mono_domain_get (), e_class, [arr count]);", i); setup_call_stack.AppendLine ("for (j = 0; j < [arr count]; j++) {{", i); if (elementType.FullName == "System.String") { setup_call_stack.AppendLine ("NSString *sv = (NSString *) [arr objectAtIndex: j];", i); setup_call_stack.AppendLine ("mono_array_set (marr, MonoString *, j, mono_string_new (mono_domain_get (), [sv UTF8String]));", i); } else if (IsNSObject (elementType) || (elementType.Namespace == "System" && elementType.Name == "Object") || (isNativeObject = SharedStatic.IsNativeObject (elementType))) { setup_call_stack.AppendLine ("NSObject *nobj = [arr objectAtIndex: j];"); setup_call_stack.AppendLine ("MonoObject *mobj{0} = NULL;", i); setup_call_stack.AppendLine ("if (nobj) {"); if (isNativeObject) { TypeDefinition nativeObjType = elementType.Resolve (); if (nativeObjType.IsInterface) { var wrapper_type = GetProtocolAttributeWrapperType (nativeObjType); if (wrapper_type == null) throw ErrorHelper.CreateError (4125, "The registrar found an invalid type '{0}' in signature for method '{1}': " + "The interface must have a Protocol attribute specifying its wrapper type.", td.FullName, descriptiveMethodName); nativeObjType = wrapper_type.Resolve (); } // verify that the type has a ctor with two parameters if (!HasIntPtrBoolCtor (nativeObjType)) throw ErrorHelper.CreateError (4103, "The registrar found an invalid type `{0}` in signature for method `{1}`: " + "The type implements INativeObject, but does not have a constructor that takes " + "two (IntPtr, bool) arguments.", nativeObjType.FullName, descriptiveMethodName); if (nativeObjType.IsInterface) { setup_call_stack.AppendLine ("mobj{0} = xamarin_get_inative_object_static (nobj, false, \"{1}\", \"{2}\");", i, GetAssemblyQualifiedName (nativeObjType), GetAssemblyQualifiedName (elementType)); } else { // find the MonoClass for this parameter setup_call_stack.AppendLine ("MonoType *type{0};", i); setup_call_stack.AppendLine ("type{0} = xamarin_get_parameter_type (managed_method, {0});", i); setup_call_stack.AppendLine ("mobj{0} = xamarin_get_inative_object_dynamic (nobj, false, mono_type_get_object (mono_domain_get (), mono_class_get_type (e_class)));", i); } } else { setup_call_stack.AppendLine ("mobj{0} = xamarin_get_managed_object_for_ptr_fast (nobj);", i); } if (Driver.EnableDebug) { setup_call_stack.AppendLine ("xamarin_verify_parameter (mobj{0}, _cmd, self, nobj, {0}, e_class, managed_method);", i); } setup_call_stack.AppendLine ("}"); setup_call_stack.AppendLine ("mono_array_set (marr, MonoObject *, j, mobj{0});", i); } else { throw ErrorHelper.CreateError (4111, method.Method, "The registrar cannot build a signature for type `{0}' in method `{1}`.", type.FullName, descriptiveMethodName); } setup_call_stack.AppendLine ("}"); setup_call_stack.AppendLine ("arg_ptrs [{0}] = marr;", i); setup_call_stack.AppendLine ("} else {"); setup_call_stack.AppendLine ("arg_ptrs [{0}] = NULL;", i); setup_call_stack.AppendLine ("}"); } else if (IsNSObject (type)) { if (isRef) { setup_call_stack.AppendLine ("MonoObject *mobj{0} = NULL;", i); if (!isOut) { setup_call_stack.AppendLine ("NSObject *nsobj{0} = *(NSObject **) p{0};", i); setup_call_stack.AppendLine ("if (nsobj{0}) {{", i); setup_call_stack.AppendLine ("MonoType *paramtype{0} = xamarin_get_parameter_type (managed_method, {0});", i); setup_call_stack.AppendLine ("mobj{0} = xamarin_get_nsobject_with_type_for_ptr (nsobj{0}, false, paramtype{0});", i); if (Driver.EnableDebug) { setup_call_stack.AppendLine ("xamarin_verify_parameter (mobj{0}, _cmd, self, nsobj{0}, {0}, mono_class_from_mono_type (paramtype{0}), managed_method);", i); } setup_call_stack.AppendLine ("}"); } // argument semantics? setup_call_stack.AppendLine ("arg_ptrs [{0}] = (int *) &mobj{0};", i); copyback.AppendLine ("void * handle{0} = NULL;", i); copyback.AppendLine ("if (mobj{0} != NULL)", i); copyback.AppendLine ("handle{0} = xamarin_get_nsobject_handle (mobj{0});", i); copyback.AppendLine ("*p{0} = (id) handle{0};", i); } else { setup_call_stack.AppendLine ("NSObject *nsobj{0} = (NSObject *) p{0};", i); if (method.ArgumentSemantic == ArgumentSemantic.Copy) { setup_call_stack.AppendLine ("nsobj{0} = [nsobj{0} copy];", i); setup_call_stack.AppendLine ("[nsobj{0} autorelease];", i); } setup_call_stack.AppendLine ("MonoObject *mobj{0} = NULL;", i); setup_call_stack.AppendLine ("int32_t created{0} = false;", i); setup_call_stack.AppendLine ("if (nsobj{0}) {{", i); setup_call_stack.AppendLine ("MonoType *paramtype{0} = xamarin_get_parameter_type (managed_method, {0});", i); setup_call_stack.AppendLine ("mobj{0} = xamarin_get_nsobject_with_type_for_ptr_created (nsobj{0}, false, paramtype{0}, &created{0});", i); if (Driver.EnableDebug) { setup_call_stack.AppendLine ("xamarin_verify_parameter (mobj{0}, _cmd, self, nsobj{0}, {0}, mono_class_from_mono_type (paramtype{0}), managed_method);", i); } setup_call_stack.AppendLine ("}"); setup_call_stack.AppendLine ("arg_ptrs [{0}] = mobj{0};", i); if (SharedStatic.HasAttribute (paramBase, ObjCRuntime, StringConstants.TransientAttribute)) { copyback.AppendLine ("if (created{0})", i); copyback.Indentation++; copyback.AppendLine ("xamarin_dispose (mobj{0});", i); copyback.Indentation--; } } } else if (SharedStatic.IsNativeObject (td)) { TypeDefinition nativeObjType = td; if (td.IsInterface) { var wrapper_type = GetProtocolAttributeWrapperType (td); if (wrapper_type == null) throw ErrorHelper.CreateError (4125, "The registrar found an invalid type '{0}' in signature for method '{1}': " + "The interface must have a Protocol attribute specifying its wrapper type.", td.FullName, descriptiveMethodName); nativeObjType = wrapper_type.Resolve (); } // verify that the type has a ctor with two parameters if (!HasIntPtrBoolCtor (nativeObjType)) throw ErrorHelper.CreateError (4103, "The registrar found an invalid type `{0}` in signature for method `{1}`: " + "The type implements INativeObject, but does not have a constructor that takes " + "two (IntPtr, bool) arguments.", nativeObjType.FullName, descriptiveMethodName); if (!td.IsInterface) { // find the MonoClass for this parameter setup_call_stack.AppendLine ("MonoType *type{0};", i); setup_call_stack.AppendLine ("type{0} = xamarin_get_parameter_type (managed_method, {0});", i); } if (isRef) { setup_call_stack.AppendLine ("MonoObject *inobj{0};", i); if (isOut) { setup_call_stack.AppendLine ("inobj{0} = NULL;", i); } else if (td.IsInterface) { setup_call_stack.AppendLine ("inobj{0} = xamarin_get_inative_object_static (*p{0}, false, \"{1}\", \"{2}\");", i, GetAssemblyQualifiedName (nativeObjType), GetAssemblyQualifiedName (td)); } else { setup_call_stack.AppendLine ("inobj{0} = xamarin_get_inative_object_dynamic (*p{0}, false, mono_type_get_object (mono_domain_get (), type{0}));", i); } setup_call_stack.AppendLine ("arg_ptrs [{0}] = &inobj{0};", i); copyback.AppendLine ("id handle{0} = nil;", i); copyback.AppendLine ("if (inobj{0} != NULL)", i); copyback.AppendLine ("handle{0} = xamarin_get_handle_for_inativeobject (inobj{0});", i); copyback.AppendLine ("*p{0} = (id) handle{0};", i); } else { if (td.IsInterface) { setup_call_stack.AppendLine ("arg_ptrs [{0}] = xamarin_get_inative_object_static (p{0}, false, \"{1}\", \"{2}\");", i, GetAssemblyQualifiedName (nativeObjType), GetAssemblyQualifiedName (td)); } else { setup_call_stack.AppendLine ("arg_ptrs [{0}] = xamarin_get_inative_object_dynamic (p{0}, false, mono_type_get_object (mono_domain_get (), type{0}));", i); } } } else if (type.IsValueType) { if (isRef || isOut) { // The isOut semantics isn't quite correct here: we pass the actual input value to managed code. // In theory we should create a temp location and then use a writeback when done instead. // This should be safe though, since managed code (at least C#) can't actually observe the value. setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0};", i); } else { setup_call_stack.AppendLine ("arg_ptrs [{0}] = &p{0};", i); } } else if (td.BaseType.FullName == "System.MulticastDelegate") { if (isRef) { throw ErrorHelper.CreateError (4110, "The registrar cannot marshal the out parameter of type `{0}` in signature for method `{1}`.", type.FullName, descriptiveMethodName); } else { // Bug #4858 (also related: #4718) setup_call_stack.AppendLine ("if (p{0}) {{", i); setup_call_stack.AppendLine ("arg_ptrs [{0}] = (void *) xamarin_get_delegate_for_block_parameter (managed_method, {0}, p{0});", i); setup_call_stack.AppendLine ("} else {"); setup_call_stack.AppendLine ("arg_ptrs [{0}] = NULL;", i); setup_call_stack.AppendLine ("}"); } } else { throw ErrorHelper.CreateError (4105, "The registrar cannot marshal the parameter of type `{0}` in signature for method `{1}`.", type.FullName, descriptiveMethodName); } break; } } // the actual invoke if (isCtor) { invoke.AppendLine ("mthis = mono_object_new (mono_domain_get (), mono_method_get_class (managed_method));", counter); invoke.AppendLine ("uint8_t flags = NSObjectFlagsNativeRef;"); invoke.AppendLine ("xamarin_set_nsobject_handle (mthis, self);"); invoke.AppendLine ("xamarin_set_nsobject_flags (mthis, flags);"); } if (!isVoid) invoke.AppendFormat ("{0} retval = ", "MonoObject *"); invoke.AppendLine ("mono_runtime_invoke (managed_method, {0}, arg_ptrs, NULL);", isStatic ? "NULL" : "mthis"); if (isCtor) invoke.AppendLine ("xamarin_create_managed_ref (self, mthis, true);"); // prepare the return value if (!isVoid) { setup_return.AppendLine ("{0} res;", rettype); var isArray = returntype is ArrayType; var type = returntype.Resolve () ?? returntype; var retain = method.RetainReturnValue; if (returntype.IsValueType) { setup_return.AppendLine ("res = *({0} *) mono_object_unbox ((MonoObject *) retval);", rettype); } else if (isArray) { var elementType = ((ArrayType) returntype).ElementType; setup_return.AppendLine ("if (retval) {"); setup_return.AppendLine ("int length = mono_array_length ((MonoArray *) retval);"); setup_return.AppendLine ("int i;"); setup_return.AppendLine ("id *buf = (id *) malloc (sizeof (void *) * length);"); setup_return.AppendLine ("for (i = 0; i < length; i++) {"); setup_return.AppendLine ("MonoObject *value = mono_array_get ((MonoArray *) retval, MonoObject *, i);"); if (elementType.FullName == "System.String") { setup_return.AppendLine ("char *str = mono_string_to_utf8 ((MonoString *) value);"); setup_return.AppendLine ("NSString *sv = [[NSString alloc] initWithUTF8String:str];"); setup_return.AppendLine ("[sv autorelease];"); setup_return.AppendLine ("mono_free (str);"); setup_return.AppendLine ("buf [i] = sv;"); } else if (IsNSObject (elementType)) { setup_return.AppendLine ("buf [i] = xamarin_get_nsobject_handle ((MonoObject *) value);"); } else if (IsINativeObject (elementType)) { setup_return.AppendLine ("buf [i] = xamarin_get_handle_for_inativeobject ((MonoObject *) value);"); } else { throw ErrorHelper.CreateError (4111, method.Method, "The registrar cannot build a signature for type `{0}' in method `{1}`.", returntype.FullName, descriptiveMethodName); } setup_return.AppendLine ("}"); setup_return.AppendLine ("NSArray *arr = [[NSArray alloc] initWithObjects: buf count: length];"); setup_return.AppendLine ("free (buf);"); if (!retain) setup_return.AppendLine ("[arr autorelease];"); setup_return.AppendLine ("res = arr;"); setup_return.AppendLine ("} else {"); setup_return.AppendLine ("res = NULL;"); setup_return.AppendLine ("}"); setup_return.AppendLine ("xamarin_framework_peer_lock ();"); setup_return.AppendLine ("mt_dummy_use (retval);"); setup_return.AppendLine ("xamarin_framework_peer_unlock ();"); } else { setup_return.AppendLine ("if (!retval) {"); setup_return.AppendLine ("res = NULL;"); setup_return.AppendLine ("} else {"); if (IsNSObject (type)) { setup_return.AppendLine ("id retobj;"); setup_return.AppendLine ("retobj = xamarin_get_nsobject_handle (retval);"); setup_return.AppendLine ("xamarin_framework_peer_lock ();"); setup_return.AppendLine ("[retobj retain];"); setup_return.AppendLine ("xamarin_framework_peer_unlock ();"); if (!retain) setup_return.AppendLine ("[retobj autorelease];"); setup_return.AppendLine ("mt_dummy_use (retval);"); setup_return.AppendLine ("res = retobj;"); } else if (type.IsPlatformType ("ObjCRuntime", "Selector")) { setup_return.AppendLine ("res = xamarin_get_selector_handle (retval);"); } else if (type.IsPlatformType ("ObjCRuntime", "Class")) { setup_return.AppendLine ("res = xamarin_get_class_handle (retval);"); } else if (SharedStatic.IsNativeObject (type)) { setup_return.AppendLine ("{0} retobj;", rettype); setup_return.AppendLine ("retobj = xamarin_get_handle_for_inativeobject ((MonoObject *) retval);"); setup_return.AppendLine ("xamarin_framework_peer_lock ();"); setup_return.AppendLine ("[retobj retain];"); setup_return.AppendLine ("xamarin_framework_peer_unlock ();"); if (!retain) setup_return.AppendLine ("[retobj autorelease];"); setup_return.AppendLine ("mt_dummy_use (retval);"); setup_return.AppendLine ("res = retobj;"); } else if (type.FullName == "System.String") { // This should always be an NSString and never char* setup_return.AppendLine ("char *str = mono_string_to_utf8 ((MonoString *) retval);"); setup_return.AppendLine ("NSString *nsstr = [[NSString alloc] initWithUTF8String:str];"); if (!retain) setup_return.AppendLine ("[nsstr autorelease];"); setup_return.AppendLine ("mono_free (str);"); setup_return.AppendLine ("res = nsstr;"); } else if (SharedStatic.IsDelegate (type.Resolve ())) { setup_return.AppendLine ("res = xamarin_get_block_for_delegate (managed_method, retval);"); } else { throw ErrorHelper.CreateError (4104, "The registrar cannot marshal the return value for type `{0}` in signature for method `{1}`.", returntype.FullName, descriptiveMethodName); } setup_return.AppendLine ("}"); } } // Write out everything if (merge_bodies) { body.WriteLine ("MonoMethod *managed_method = *managed_method_ptr;"); } else { if (!isGeneric) body.Write ("static "); body.WriteLine ("MonoMethod *managed_method = NULL;"); } if (comment.Length > 0) body.WriteLine (comment.ToString ()); if (isInstanceCategory) body.WriteLine ("id p0 = self;"); body.WriteLine ("void *arg_ptrs [{0}];", num_arg); if (!isStatic || isInstanceCategory) body.WriteLine ("MonoObject *mthis;"); body.WriteLine ("if (mono_domain_get () == NULL)"); body.Indent (); body.WriteLine ("mono_jit_thread_attach (NULL);"); body.Unindent (); if (isCtor) { body.WriteLine ("if (xamarin_try_get_nsobject (self)) {"); body.WriteLine ("*call_super = true;"); body.WriteLine ("return self;"); body.WriteLine ("}"); } if ((!isStatic || isInstanceCategory) && !isCtor) { body.WriteLine ("mthis = NULL;"); body.WriteLine ("if (self) {"); body.WriteLine ("mthis = xamarin_get_managed_object_for_ptr_fast (self);"); body.WriteLine ("}"); } // no locking should be required here, it doesn't matter if we overwrite the field (it'll be the same value). body.WriteLine ("if (!managed_method) {"); if (num_arg > 0) { body.Write ("const char *paramptr[{0}] = {{ ", num_arg); for (int i = 0; i < num_arg; i++) { string paramtype; if (isGeneric) { paramtype = GetAssemblyQualifiedName (method.Method.Parameters [i].ParameterType); } else { paramtype = GetAssemblyQualifiedName (method.Parameters [i]); } if (merge_bodies) { body.Write ("r{0}", arguments.Count); arguments.Add (paramtype); } else { body.Write ("\"{0}\"", paramtype); } if (i < num_arg - 1) body.Write (", "); } body.WriteLine (" };"); } body.Write ("managed_method = "); if (isGeneric) body.Write ("xamarin_get_reflection_method_method (xamarin_get_generic_method_direct (mthis, "); else body.Write ("xamarin_get_reflection_method_method (xamarin_get_method_direct("); if (merge_bodies) { body.WriteLine ("r{2}, r{3}, {0}, {1}));", num_arg, num_arg > 0 ? "paramptr" : "NULL", arguments.Count, arguments.Count + 1); arguments.Add (GetAssemblyQualifiedName (method.Method.DeclaringType)); arguments.Add (method.Method.Name); } else { body.WriteLine ("\"{0}\", \"{1}\", {2}, {3}));", GetAssemblyQualifiedName (method.Method.DeclaringType), method.Method.Name, num_arg, num_arg > 0 ? "paramptr" : "NULL"); } if (merge_bodies) body.WriteLine ("*managed_method_ptr = managed_method;"); body.WriteLine ("}"); if (!isStatic && !isInstanceCategory && !isCtor) body.WriteLine ("xamarin_check_for_gced_object (mthis, _cmd, self, managed_method);"); if (trace) body.AppendLine (nslog_start); body.AppendLine (setup_call_stack); body.AppendLine (invoke); body.AppendLine (copyback); body.AppendLine (setup_return); if (trace ) body.AppendLine (nslog_end); if (isCtor) { body.WriteLine ("return self;"); } else if (isVoid) { body.WriteLine ("return;"); } else { body.WriteLine ("return res;"); } body.WriteLine ("}"); /* We merge duplicated bodies (based on the signature of the method and the entire body) */ var objc_signature = new StringBuilder ().Append (rettype).Append (":"); for (int i = 0; i < method.Method.Parameters.Count; i++) objc_signature.Append (ToObjCParameterType (method.Method.Parameters [i].ParameterType, descriptiveMethodName, exceptions, method.Method)).Append (":"); Body existing; Body b = new Body () { Code = body.ToString (), Signature = objc_signature.ToString (), }; if (merge_bodies && bodies.TryGetValue (b, out existing)) { /* We already have an identical trampoline, use it instead */ b = existing; } else { /* Need to create a new trampoline */ if (merge_bodies) bodies [b] = b; b.Name = "native_to_managed_trampoline_" + bodies.Count.ToString (); if (merge_bodies) { methods.Append ("static "); methods.Append (rettype).Append (" ").Append (b.Name).Append (" (id self, SEL _cmd, MonoMethod **managed_method_ptr"); for (int i = (isInstanceCategory ? 1 : 0); i < method.Method.Parameters.Count; i++) { methods.Append (", ").Append (ToObjCParameterType (method.Method.Parameters [i].ParameterType, descriptiveMethodName, exceptions, method.Method)); methods.Append (" ").Append ("p").Append (i.ToString ()); } for (int i = 0; i < arguments.Count; i++) methods.Append (", const char *").Append ("r").Append (i.ToString ()); if (isCtor) methods.Append (", bool* call_super"); methods.AppendLine (")"); methods.AppendLine (body); methods.AppendLine (); } } b.Count++; sb.WriteLine (); sb.WriteLine (GetObjCSignature (method, exceptions)); if (merge_bodies) { sb.WriteLine ("{"); if (!isGeneric) sb.Write ("static "); sb.WriteLine ("MonoMethod *managed_method = NULL;"); if (isCtor) { sb.WriteLine ("bool call_super = false;"); sb.Write ("id rv = "); } else if (!isVoid) { sb.Write ("return "); } sb.Write (b.Name); sb.Write (" (self, _cmd, &managed_method"); var paramCount = method.Method.Parameters.Count; if (isInstanceCategory) paramCount--; for (int i = 0; i < paramCount; i++) sb.Write (", p{0}", i); for (int i = 0; i < arguments.Count; i++) sb.Write (", \"").Write (arguments [i]).Write ("\""); if (isCtor) sb.Write (", &call_super"); sb.WriteLine (");"); if (isCtor) { sb.WriteLine ("if (call_super)"); sb.Indent (); sb.Write ("rv = [super"); var split = method.Selector.Split (':'); if (split.Length == 1) { sb.Append (" "); sb.Append (split [0]); } else { for (int i = 0; i < split.Length - 1; i++) { sb.Append (" "); sb.Append (split [i]); sb.Append (":"); sb.AppendFormat ("p{0}", i); } } sb.WriteLine ("];"); sb.Unindent (); sb.WriteLine ("return rv;"); } sb.WriteLine ("}"); } else { sb.WriteLine (body); } }
string GetObjCSignature(ObjCMethod method, List<Exception> exceptions) { if (method.CurrentTrampoline == Trampoline.Retain) return "-(id) retain"; else if (method.CurrentTrampoline == Trampoline.Release) return "-(void) release"; else if (method.CurrentTrampoline == Trampoline.GetGCHandle) return "-(int) xamarinGetGCHandle"; else if (method.CurrentTrampoline == Trampoline.SetGCHandle) return "-(void) xamarinSetGCHandle: (int) gchandle"; #if MONOMAC else if (method.CurrentTrampoline == Trampoline.CopyWithZone1 || method.CurrentTrampoline == Trampoline.CopyWithZone2) return "-(id) copyWithZone: (NSZone *)zone"; #endif var sb = new StringBuilder (); var isCtor = method.CurrentTrampoline == Trampoline.Constructor; sb.Append ((method.IsStatic && !method.IsCategoryInstance) ? '+' : '-'); sb.Append ('('); sb.Append (isCtor ? "id" : this.ToObjCParameterType (method.ReturnType, GetDescriptiveMethodName (method.Method), exceptions, method.Method)); sb.Append (')'); var split = method.Selector.Split (':'); if (split.Length == 1) { sb.Append (' '); sb.Append (split [0]); } else { for (int i = 0; i < split.Length - 1; i++) { sb.Append (' '); sb.Append (split [i]); sb.Append (':'); sb.Append ('('); sb.Append (ToObjCParameterType (method.Parameters [i], method.DescriptiveMethodName, exceptions, method.Method)); sb.Append (')'); sb.AppendFormat ("p{0}", i); } } if (method.IsVariadic) sb.Append (", ..."); return sb.ToString (); }
BaseMethodDeclarationSyntax PortMethod(CXCursor cursor) { var objcMethod = new ObjCMethod (cursor); IEnumerable<ParameterSyntax> mParams = FetchParamInfos (cursor); MethodDefinition mDef; MethodDeclarationSyntax mDecl = null; ConstructorDeclarationSyntax ctorDecl = null; var mb = new MethodBuilder (); var isBound = bindingLocator.TryFindMethod (ImplContext.SuperClassName, objcMethod.Selector, out mDef); if (isBound) { if (mDef.IsConstructor) ctorDecl = mb.BuildCtor (mDef, ImplContext.ClassName, mParams); else mDecl = mb.BuildDeclaration (mDef, mParams); } else { if (objcMethod.IsInitializer) ctorDecl = mb.BuildCtor (ImplContext.ClassName, mParams); else mDecl = BuildDefaultDeclaration (objcMethod, mParams); } IEnumerable<CXCursor> children = cursor.GetChildren (); var compoundStmt = children.First (c => c.kind == CXCursorKind.CXCursor_CompoundStmt); if (ctorDecl != null) return AddBody (compoundStmt, ctorDecl); else return AddBody (compoundStmt, mDecl); }
MethodDeclarationSyntax PortCategoryMethod(CXCursor cursor) { var objcMethod = new ObjCMethod (cursor); IEnumerable<ParameterSyntax> mParams = FetchParamInfos (cursor); ParameterSyntax thisParam = SF.Parameter (SF.Identifier ("self")) .WithType (SF.ParseTypeName (CategoryImplContext.ExtendedClassName)); mParams = (new ParameterSyntax[] { thisParam }).Concat (mParams); TypeSyntax retType = TypePorter.PortType (objcMethod.ReturnType); string methodName = MethodHelper.ConvertToMehtodName (objcMethod.Selector); var mb = new MethodBuilder (); MethodDeclarationSyntax mDecl = mb.BuildExtensionMethod (retType, methodName, mParams); IEnumerable<CXCursor> children = cursor.GetChildren (); var compoundStmt = children.First (c => c.kind == CXCursorKind.CXCursor_CompoundStmt); return AddBody (compoundStmt, mDecl); }
MethodDeclarationSyntax BuildDefaultDeclaration(ObjCMethod objcMethod, IEnumerable<ParameterSyntax> mParams) { TypeSyntax retType = TypePorter.PortType (objcMethod.ReturnType); string methodName = MethodHelper.ConvertToMehtodName (objcMethod.Selector); var mb = new MethodBuilder (); MethodDeclarationSyntax mDecl = mb.BuildDeclaration (retType, methodName, mParams); if (objcMethod.IsStatic) mDecl = mDecl.WithStaticKeyword (); return mDecl; }