Exemple #1
0
        public static string GetArrayCreator(string parameterName, Type type)
        {
            string arrayCreator = $"MonoArray* __{parameterName}arr = mono_array_new (__mono_context.domain, {{0}}, __{parameterName}length);";

            switch (Type.GetTypeCode(type))
            {
            case TypeCode.String:
                return(string.Format(arrayCreator, "mono_get_string_class ()"));

            case TypeCode.Boolean:
                return(string.Format(arrayCreator, "mono_get_boolean_class ()"));

            case TypeCode.Char:
                return(string.Format(arrayCreator, "mono_get_char_class ()"));

            case TypeCode.SByte:
                return(string.Format(arrayCreator, "mono_get_sbyte_class ()"));

            case TypeCode.Int16:
                return(string.Format(arrayCreator, "mono_get_int16_class ()"));

            case TypeCode.Int32:
                return(string.Format(arrayCreator, "mono_get_int32_class ()"));

            case TypeCode.Int64:
                return(string.Format(arrayCreator, "mono_get_int64_class ()"));

            case TypeCode.Byte:
                return(string.Format(arrayCreator, "mono_get_byte_class ()"));

            case TypeCode.UInt16:
                return(string.Format(arrayCreator, "mono_get_uint16_class ()"));

            case TypeCode.UInt32:
                return(string.Format(arrayCreator, "mono_get_uint32_class ()"));

            case TypeCode.UInt64:
                return(string.Format(arrayCreator, "mono_get_uint64_class ()"));

            case TypeCode.Single:
                return(string.Format(arrayCreator, "mono_get_single_class ()"));

            case TypeCode.Double:
                return(string.Format(arrayCreator, "mono_get_double_class ()"));

            case TypeCode.Object:
                return(string.Format(arrayCreator, $"{NameGenerator.GetObjCName (type)}_class"));

            case TypeCode.Decimal:
                return(string.Format(arrayCreator, "mono_embeddinator_get_decimal_class ()"));

            default:
                throw new NotImplementedException($"Converting type {type.FullName} to mono class");
            }
        }
        // TODO override with attribute ? e.g. [ObjC.Selector ("foo")]
        string ImplementMethod(MethodInfo info, string name, bool isExtension = false, PropertyInfo pi = null, bool useTypeNames = false)
        {
            var type = info.DeclaringType;
            var managed_type_name = NameGenerator.GetObjCName(type);

            string objcsig;
            string monosig;
            var    managed_name   = info.Name;
            var    parametersInfo = info.GetParameters();

            GetSignatures(name, managed_name, (MemberInfo)pi ?? info, parametersInfo, useTypeNames, isExtension, out objcsig, out monosig);

            var builder = new MethodHelper(headers, implementation)
            {
                AssemblySafeName = type.Assembly.GetName().Name.Sanitize(),
                IsStatic         = info.IsStatic,
                IsExtension      = isExtension,
                ReturnType       = GetReturnType(type, info.ReturnType),
                ManagedTypeName  = type.FullName,
                MetadataToken    = info.MetadataToken,
                MonoSignature    = monosig,
                ObjCSignature    = objcsig,
                ObjCTypeName     = managed_type_name,
                IsValueType      = type.IsValueType,
                IsVirtual        = info.IsVirtual,
            };

            if (pi == null)
            {
                builder.WriteHeaders();
            }

            builder.BeginImplementation();
            builder.WriteMethodLookup();

            string postInvoke = String.Empty;
            var    args       = "nil";

            if (parametersInfo.Length > 0)
            {
                Generate(parametersInfo, isExtension, out postInvoke);
                args = "__args";
            }

            builder.WriteInvoke(args);

            // ref and out parameters might need to be converted back
            implementation.Write(postInvoke);
            ReturnValue(info.ReturnType);
            builder.EndImplementation();
            return(objcsig);
        }
        void GenerateDefaultValuesWrapper(string name, MethodBase mb, ParameterInfo[] parameters, int start)
        {
            MethodInfo    mi = mb as MethodInfo;
            string        objcsig;
            string        monosig;
            var           parametersInfo = parameters;
            var           plist          = new List <ParameterInfo> ();
            StringBuilder arguments      = new StringBuilder();

            headers.WriteLine("/** This is an helper method that inlines the following default values:");
            foreach (var p in parameters)
            {
                string pName = NameGenerator.GetExtendedParameterName(p, parameters);
                if (arguments.Length == 0)
                {
                    arguments.Append(p.Name.PascalCase()).Append(':');
                }
                else
                {
                    arguments.Append(' ').Append(p.Name.CamelCase()).Append(':');
                }
                if (p.Position >= start && p.HasDefaultValue)
                {
                    var raw = FormatRawValue(p.ParameterType, p.RawDefaultValue);
                    headers.WriteLine($" *     ({NameGenerator.GetTypeName (p.ParameterType)}) {pName} = {raw};");
                    arguments.Append(raw);
                }
                else
                {
                    arguments.Append(pName);
                    plist.Add(p);
                }
            }
            headers.WriteLine(" *");
            headers.WriteLine($" *  @see {name}");
            headers.WriteLine(" */");

            if (mi == null)
            {
                name = start == 0 ? "init" : "initWith";
            }
            else
            {
                name = mb.Name.CamelCase();
            }

            GetSignatures(name, mb.Name, mb, plist.ToArray(), false, false, out objcsig, out monosig);
            var type    = mb.DeclaringType;
            var builder = new MethodHelper(headers, implementation)
            {
                IsStatic      = mb.IsStatic,
                ReturnType    = mi == null ? "nullable instancetype" : GetReturnType(type, mi.ReturnType),
                ObjCSignature = objcsig,
            };

            builder.WriteHeaders();
            headers.WriteLine();

            builder.BeginImplementation();
            if (mi == null || !mi.ReturnType.Is("System", "Void"))
            {
                implementation.Write("return [");
            }
            if (mb.IsStatic)
            {
                implementation.Write(NameGenerator.GetObjCName(mi.DeclaringType));
                implementation.Write(' ');
            }
            else
            {
                implementation.Write("self ");
            }
            if (mi == null)
            {
                name = "initWith";
            }
            implementation.WriteLine($"{name}{arguments}];");
            builder.EndImplementation();
        }
        protected override void Generate(ProcessedAssembly a)
        {
            var originalName = a.Name;
            var name         = a.SafeName;

            implementation.WriteLine($"static void __lookup_assembly_{name} ()");
            implementation.WriteLine("{");
            implementation.Indent++;
            implementation.WriteLine($"if (__{name}_image)");
            implementation.Indent++;
            implementation.WriteLine("return;");
            implementation.Indent--;
            implementation.WriteLine("__initialize_mono ();");
            implementation.WriteLine($"__{name}_image = mono_embeddinator_load_assembly (&__mono_context, \"{originalName}.dll\");");
            implementation.WriteLine($"assert (__{name}_image && \"Could not load the assembly '{originalName}.dll'.\");");
            var categories = extensions_methods.Keys;

            if (categories.Count > 0)
            {
                implementation.WriteLine("// we cannot use `+initialize` inside categories as they would replace the original type code");
                implementation.WriteLine("// since there should not be tons of them we're pre-loading them when loading the assembly");
                foreach (var definedType in extensions_methods.Keys)
                {
                    var managed_name = NameGenerator.GetObjCName(definedType);
                    implementation.WriteLineUnindented("#if TOKENLOOKUP");
                    implementation.WriteLine($"{managed_name}_class = mono_class_get (__{name}_image, 0x{definedType.MetadataToken:X8});");
                    implementation.WriteLineUnindented("#else");
                    implementation.WriteLine($"{managed_name}_class = mono_class_from_name (__{name}_image, \"{definedType.Namespace}\", \"{definedType.Name}\");");
                    implementation.WriteLineUnindented("#endif");
                }
            }
            implementation.Indent--;
            implementation.WriteLine("}");
            implementation.WriteLine();

            var assembly = a.Assembly;

            foreach (var t in enums.Where((ProcessedType arg) => arg.Type.Assembly == assembly))
            {
                GenerateEnum(t);
            }

            foreach (var t in protocols.Where((ProcessedType arg) => arg.Type.Assembly == assembly))
            {
                GenerateProtocol(t);
            }

            foreach (var t in types.Where((ProcessedType arg) => arg.Type.Assembly == assembly))
            {
                Generate(t);
            }

            foreach (var extension in extensions_methods)
            {
                var defining_type = extension.Key;
                if (defining_type.Assembly != assembly)
                {
                    continue;
                }
                foreach (var category in extension.Value)
                {
                    GenerateCategory(defining_type, category.Key, category.Value);
                }
            }
        }
        protected void Generate(ProcessedFieldInfo field)
        {
            FieldInfo fi        = field.Field;
            bool      read_only = fi.IsInitOnly || fi.IsLiteral;

            headers.Write("@property (nonatomic");
            if (fi.IsStatic)
            {
                headers.Write(", class");
            }
            if (read_only)
            {
                headers.Write(", readonly");
            }
            var ft    = fi.FieldType;
            var bound = types.Contains(ft);

            if (bound && ft.IsValueType)
            {
                headers.Write(", nonnull");
            }

            var field_type = NameGenerator.GetTypeName(ft);

            if (bound)
            {
                field_type += " *";
            }

            var name = fi.Name.CamelCase();

            var spacing = field_type [field_type.Length - 1] == '*' ? string.Empty : " ";

            headers.WriteLine($") {field_type}{spacing}{name};");

            // it's similar, but different from implementing a method

            var type = fi.DeclaringType;
            var managed_type_name = NameGenerator.GetObjCName(type);
            var return_type       = GetReturnType(type, fi.FieldType);

            implementation.Write(fi.IsStatic ? '+' : '-');
            implementation.WriteLine($" ({return_type}) {name}");
            implementation.WriteLine("{");
            implementation.Indent++;
            implementation.WriteLine("static MonoClassField* __field = nil;");
            implementation.WriteLine("if (!__field) {");
            implementation.Indent++;
            implementation.WriteLineUnindented("#if TOKENLOOKUP");
            implementation.WriteLine($"__field = mono_class_get_field ({managed_type_name}_class, 0x{fi.MetadataToken:X8});");
            implementation.WriteLineUnindented("#else");
            implementation.WriteLine($"const char __field_name [] = \"{fi.Name}\";");
            implementation.WriteLine($"__field = mono_class_get_field_from_name ({managed_type_name}_class, __field_name);");
            implementation.WriteLineUnindented("#endif");
            implementation.Indent--;
            implementation.WriteLine("}");
            var instance = "nil";

            if (!fi.IsStatic)
            {
                implementation.WriteLine($"MonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
                instance = "__instance";
            }
            implementation.WriteLine($"MonoObject* __result = mono_field_get_value_object (__mono_context.domain, __field, {instance});");
            if (types.Contains(ft))
            {
                implementation.WriteLine("if (!__result)");
                implementation.Indent++;
                implementation.WriteLine("return nil;");
                implementation.Indent--;
            }
            ReturnValue(fi.FieldType);
            implementation.Indent--;
            implementation.WriteLine("}");
            implementation.WriteLine();

            if (read_only)
            {
                return;
            }
            implementation.Write(fi.IsStatic ? '+' : '-');
            implementation.WriteLine($" (void) set{fi.Name}:({field_type})value");
            implementation.WriteLine("{");
            implementation.Indent++;
            implementation.WriteLine("static MonoClassField* __field = nil;");
            implementation.WriteLine("if (!__field) {");
            implementation.Indent++;
            implementation.WriteLineUnindented("#if TOKENLOOKUP");
            implementation.WriteLine($"__field = mono_class_get_field ({managed_type_name}_class, 0x{fi.MetadataToken:X8});");
            implementation.WriteLineUnindented("#else");
            implementation.WriteLine($"const char __field_name [] = \"{fi.Name}\";");
            implementation.WriteLine($"__field = mono_class_get_field_from_name ({managed_type_name}_class, __field_name);");
            implementation.WriteLineUnindented("#endif");
            implementation.Indent--;
            implementation.WriteLine("}");
            StringBuilder sb = null;

            implementation.WriteLine($"void* __value;");
            GenerateArgument("value", "__value", fi.FieldType, ref sb);
            if (fi.IsStatic)
            {
                implementation.WriteLine($"MonoVTable *__vtable = mono_class_vtable (__mono_context.domain, {managed_type_name}_class);");
                implementation.WriteLine("mono_field_static_set_value (__vtable, __field, __value);");
            }
            else
            {
                implementation.WriteLine($"MonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
                implementation.WriteLine("mono_field_set_value (__instance, __field, __value);");
            }
            implementation.Indent--;
            implementation.WriteLine("}");
            implementation.WriteLine();
        }
        protected override void Generate(ProcessedType type)
        {
            Type t           = type.Type;
            var  aname       = t.Assembly.GetName().Name.Sanitize();
            var  static_type = t.IsSealed && t.IsAbstract;

            var managed_name = NameGenerator.GetObjCName(t);

            List <string> conformed_protocols = new List <string> ();

            foreach (var i in t.GetInterfaces())
            {
                if (protocols.Contains(i))
                {
                    conformed_protocols.Add(NameGenerator.GetObjCName(i));
                }
            }

            var tbuilder = new ClassHelper(headers, implementation)
            {
                AssemblyQualifiedName = t.AssemblyQualifiedName,
                AssemblyName          = aname,
                BaseTypeName          = NameGenerator.GetTypeName(t.BaseType),
                Name            = NameGenerator.GetTypeName(t),
                Namespace       = t.Namespace,
                ManagedName     = t.Name,
                Protocols       = conformed_protocols,
                IsBaseTypeBound = types.Contains(t.BaseType),
                IsStatic        = t.IsSealed && t.IsAbstract,
                MetadataToken   = t.MetadataToken,
            };

            tbuilder.BeginHeaders();
            tbuilder.BeginImplementation();

            var default_init = false;
            List <ProcessedConstructor> constructors;

            if (ctors.TryGetValue(t, out constructors))
            {
                // First get the unavailable init ctor selectors in parent class
                var unavailableCtors = GetUnavailableParentCtors(t, constructors);
                if (unavailableCtors.Count() > 0)
                {
                    // TODO: Print a #pragma mark once we have a well defined header structure http://nshipster.com/pragma/
                    foreach (var uctor in unavailableCtors)
                    {
                        var    ctorparams = uctor.Constructor.GetParameters();
                        string name       = "init";
                        string signature  = ".ctor()";
                        if (ctorparams.Length > 0)
                        {
                            GetSignatures("initWith", uctor.Constructor.Name, uctor.Constructor, ctorparams, uctor.FallBackToTypeName, false, out name, out signature);
                        }
                        headers.WriteLine("/** This initializer is not available as it was not re-exposed from the base type");
                        headers.WriteLine(" *  For more details consult https://github.com/mono/Embeddinator-4000/blob/master/docs/ObjC.md#constructors-vs-initializers");
                        headers.WriteLine(" */");
                        headers.WriteLine($"- (nullable instancetype){name} NS_UNAVAILABLE;");
                        headers.WriteLine();
                    }
                }

                foreach (var ctor in constructors)
                {
                    var pcount = ctor.Constructor.ParameterCount;
                    default_init |= pcount == 0;

                    var    parameters = ctor.Constructor.GetParameters();
                    string name       = "init";
                    string signature  = ".ctor()";
                    if (parameters.Length > 0)
                    {
                        GetSignatures("initWith", ctor.Constructor.Name, ctor.Constructor, parameters, ctor.FallBackToTypeName, false, out name, out signature);
                    }

                    var builder = new MethodHelper(headers, implementation)
                    {
                        AssemblySafeName = aname,
                        ReturnType       = "nullable instancetype",
                        ManagedTypeName  = t.FullName,
                        MetadataToken    = ctor.Constructor.MetadataToken,
                        MonoSignature    = signature,
                        ObjCSignature    = name,
                        ObjCTypeName     = managed_name,
                        IsConstructor    = true,
                        IsValueType      = t.IsValueType,
                        IgnoreException  = true,
                    };

                    builder.WriteHeaders();

                    builder.BeginImplementation();
                    builder.WriteMethodLookup();

                    // TODO: this logic will need to be update for managed NSObject types (e.g. from XI / XM) not to call [super init]
                    implementation.WriteLine("if (!_object) {");
                    implementation.Indent++;
                    implementation.WriteLine($"MonoObject* __instance = mono_object_new (__mono_context.domain, {managed_name}_class);");

                    string postInvoke = String.Empty;
                    var    args       = "nil";
                    if (pcount > 0)
                    {
                        Generate(parameters, false, out postInvoke);
                        args = "__args";
                    }
                    builder.WriteInvoke(args);
                    implementation.Write(postInvoke);
                    implementation.WriteLine("_object = mono_embeddinator_create_object (__instance);");
                    implementation.Indent--;
                    implementation.WriteLine("}");
                    if (types.Contains(t.BaseType))
                    {
                        implementation.WriteLine("return self = [super initForSuper];");
                    }
                    else
                    {
                        implementation.WriteLine("return self = [super init];");
                    }
                    builder.EndImplementation();

                    headers.WriteLine();

                    if (members_with_default_values.Contains(ctor.Constructor))
                    {
                        default_init |= GenerateDefaultValuesWrappers(name, ctor.Constructor);
                    }
                }
            }

            // generate an `init` for a value type (even if none was defined, the default one is usable)
            if (!default_init && t.IsValueType)
            {
                var builder = new MethodHelper(headers, implementation)
                {
                    AssemblySafeName = aname,
                    ReturnType       = "nullable instancetype",
                    ManagedTypeName  = t.FullName,
                    MonoSignature    = ".ctor()",
                    ObjCSignature    = "init",
                    ObjCTypeName     = managed_name,
                    IsConstructor    = true,
                    IsValueType      = t.IsValueType,
                    IgnoreException  = true,
                };

                builder.WriteHeaders();
                builder.BeginImplementation();
                // no call to `WriteMethodLookup` since there is not such method if we reached this case

                implementation.WriteLine("if (!_object) {");
                implementation.Indent++;
                implementation.WriteLine($"MonoObject* __instance = mono_object_new (__mono_context.domain, {managed_name}_class);");
                // no call to `WriteInvoke` since there is not such method if we reached this case
                implementation.WriteLine("_object = mono_embeddinator_create_object (__instance);");
                implementation.Indent--;
                implementation.WriteLine("}");
                if (types.Contains(t.BaseType))
                {
                    implementation.WriteLine("return self = [super initForSuper];");
                }
                else
                {
                    implementation.WriteLine("return self = [super init];");
                }
                builder.EndImplementation();

                headers.WriteLine();
                default_init = true;
            }

            if (!default_init || static_type)
            {
                tbuilder.DefineNoDefaultInit();
            }

            List <ProcessedProperty> props;

            if (properties.TryGetValue(t, out props))
            {
                headers.WriteLine();
                foreach (var pi in props)
                {
                    Generate(pi);
                }
            }

            List <ProcessedFieldInfo> f;

            if (fields.TryGetValue(t, out f))
            {
                headers.WriteLine();
                foreach (var fi in f)
                {
                    Generate(fi);
                }
            }

            List <ProcessedProperty> s;

            if (subscriptProperties.TryGetValue(t, out s))
            {
                headers.WriteLine();
                foreach (var si in s)
                {
                    GenerateSubscript(si);
                }
            }

            List <ProcessedMethod> meths;

            if (methods.TryGetValue(t, out meths))
            {
                headers.WriteLine();
                foreach (var mi in meths)
                {
                    Generate(mi);
                }
            }

            MethodInfo m;

            if (icomparable.TryGetValue(t, out m))
            {
                var pt      = m.GetParameters() [0].ParameterType;
                var builder = new ComparableHelper(headers, implementation)
                {
                    ObjCSignature    = $"compare:({managed_name} * _Nullable)other",
                    AssemblySafeName = aname,
                    MetadataToken    = m.MetadataToken,
                    ObjCTypeName     = managed_name,
                    ManagedTypeName  = t.FullName,
                    MonoSignature    = $"CompareTo({NameGenerator.GetMonoName (pt)})",
                };
                builder.WriteHeaders();
                builder.WriteImplementation();
            }

            if (equals.TryGetValue(t, out m))
            {
                var builder = new EqualsHelper(headers, implementation)
                {
                    AssemblySafeName = aname,
                    MetadataToken    = m.MetadataToken,
                    ObjCTypeName     = managed_name,
                    ManagedTypeName  = t.FullName,
                };

                builder.WriteHeaders();
                builder.WriteImplementation();
            }

            if (hashes.TryGetValue(t, out m))
            {
                var builder = new HashHelper(headers, implementation)
                {
                    AssemblySafeName = aname,
                    MetadataToken    = m.MetadataToken,
                    ObjCTypeName     = managed_name,
                    ManagedTypeName  = t.FullName,
                };

                builder.WriteHeaders();
                builder.WriteImplementation();
            }

            tbuilder.EndHeaders();
            tbuilder.EndImplementation();
        }