public static void AddInterfaceListenerEventsAndProperties(TypeWriter tw, InterfaceGen iface, ClassGen target, string name, string connector_fmt, string add, string remove, CodeGenerationOptions opt) { if (!iface.IsValid) { return; } foreach (var method in iface.Methods) { var nameSpec = iface.Methods.Count > 1 ? method.EventName ?? method.AdjustedName : string.Empty; var nameUnique = string.IsNullOrEmpty(nameSpec) ? name : nameSpec; if (nameUnique.StartsWith("On")) { nameUnique = nameUnique.Substring(2); } if (target.ContainsName(nameUnique)) { nameUnique += "Event"; } AddInterfaceListenerEventOrProperty(tw, iface, method, target, nameUnique, connector_fmt, add, remove, opt); } }
public static void AddInterfaceListenerEventOrProperty(TypeWriter tw, InterfaceGen iface, Method method, ClassGen target, string name, string connector_fmt, string add, string remove, CodeGenerationOptions opt) { if (method.EventName == string.Empty) { return; } var nameSpec = iface.Methods.Count > 1 ? method.AdjustedName : string.Empty; var idx = iface.FullName.LastIndexOf("."); var start = iface.Name.StartsWith("IOn") ? 3 : 1; var full_delegate_name = iface.FullName.Substring(0, idx + 1) + iface.Name.Substring(start, iface.Name.Length - start - 8) + nameSpec; if (method.IsSimpleEventHandler) { full_delegate_name = "EventHandler"; } else if (method.RetVal.IsVoid || method.IsEventHandlerWithHandledProperty) { full_delegate_name = "EventHandler<" + iface.FullName.Substring(0, idx + 1) + iface.GetArgsName(method) + ">"; } else { full_delegate_name += "Handler"; } if (method.RetVal.IsVoid || method.IsEventHandlerWithHandledProperty) { if (opt.GetSafeIdentifier(name) != name) { Report.LogCodedWarning(0, Report.WarningInvalidEventName2, method, iface.FullName, name); return; } else { var mt = target.Methods.Where(method => string.Compare(method.Name, connector_fmt, StringComparison.OrdinalIgnoreCase) == 0 && method.IsListenerConnector).FirstOrDefault(); var hasHandlerArgument = mt != null && mt.IsListenerConnector && mt.Parameters.Count == 2 && mt.Parameters [1].Type == "Android.OS.Handler"; tw.Events.Add(new InterfaceListenerEvent(iface, name, nameSpec, full_delegate_name, connector_fmt, add, remove, hasHandlerArgument, opt)); } } else { if (opt.GetSafeIdentifier(name) != name) { Report.LogCodedWarning(0, Report.WarningInvalidEventPropertyName, method, iface.FullName, name); return; } tw.Properties.Add(new InterfaceListenerPropertyImplementor(iface, name, opt)); tw.Properties.Add(new InterfaceListenerProperty(iface, name, nameSpec, method.AdjustedName, full_delegate_name, opt)); } }
public static void Run(CodeGeneratorOptions options) { if (options == null) { throw new ArgumentNullException("options"); } string assemblyQN = options.AssemblyQualifiedName; string api_level = options.ApiLevel; int product_version = options.ProductVersion; bool preserve_enums = options.PreserveEnums; string csdir = options.ManagedCallableWrapperSourceOutputDirectory ?? "cs"; string javadir = "java"; string enumdir = options.EnumOutputDirectory ?? "enum"; string enum_metadata = options.EnumMetadataOutputFile ?? "enummetadata"; var references = options.AssemblyReferences; string enum_fields_map = options.EnumFieldsMapFile; string enum_flags = options.EnumFlagsFile; string enum_methods_map = options.EnumMethodsMapFile; var fixups = options.FixupFiles; string api_versions_xml = options.ApiVersionsXmlFile; var annotations_zips = options.AnnotationsZipFiles; string filename = options.ApiDescriptionFile; string mapping_file = options.MappingReportFile; bool only_xml_adjuster = options.OnlyRunApiXmlAdjuster; string api_xml_adjuster_output = options.ApiXmlAdjusterOutput; var apiSource = ""; var opt = new CodeGenerationOptions() { CodeGenerationTarget = options.CodeGenerationTarget, UseGlobal = options.GlobalTypeNames, IgnoreNonPublicType = true, UseShortFileNames = options.UseShortFileNames, ProductVersion = options.ProductVersion }; // Load reference libraries var resolver = new DirectoryAssemblyResolver(Console.WriteLine, loadDebugSymbols: false); foreach (var lib in options.LibraryPaths) { resolver.SearchDirectories.Add(lib); } foreach (var reference in references) { resolver.SearchDirectories.Add(Path.GetDirectoryName(reference)); } foreach (var reference in references) { try { Report.Verbose(0, "resolving assembly {0}.", reference); var assembly = resolver.Load(reference); foreach (var md in assembly.Modules) { foreach (var td in md.Types) { // FIXME: at some stage we want to import generic types. // For now generator fails to load generic types that have conflicting type e.g. // AdapterView`1 and AdapterView cannot co-exist. // It is mostly because generator primarily targets jar (no real generics land). if (td.HasGenericParameters && md.GetType(td.FullName.Substring(0, td.FullName.IndexOf('`'))) != null) { continue; } ProcessReferencedType(td, opt); } } } catch (Exception ex) { Report.Warning(0, Report.WarningCodeGenerator + 0, ex, "failed to parse assembly {0}: {1}", reference, ex.Message); } } // For class-parse API description, transform it to jar2xml style. string apiXmlFile = filename; string apiSourceAttr = null; using (var xr = XmlReader.Create(filename)) { xr.MoveToContent(); apiSourceAttr = xr.GetAttribute("api-source"); } if (apiSourceAttr == "class-parse") { apiXmlFile = api_xml_adjuster_output ?? Path.Combine(Path.GetDirectoryName(filename), Path.GetFileName(filename) + ".adjusted"); new Adjuster().Process(filename, SymbolTable.AllRegisteredSymbols().OfType <GenBase> ().ToArray(), apiXmlFile); } if (only_xml_adjuster) { return; } // load XML API definition with fixups. Dictionary <string, EnumMappings.EnumDescription> enums = null; EnumMappings enummap = null; if (enum_fields_map != null || enum_methods_map != null) { enummap = new EnumMappings(enumdir, enum_metadata, api_level, preserve_enums); enums = enummap.Process(enum_fields_map, enum_flags, enum_methods_map); fixups.Add(enum_metadata); } Parser p = new Parser(); List <GenBase> gens = p.Parse(apiXmlFile, fixups, api_level, product_version); if (gens == null) { return; } apiSource = p.ApiSource; opt.Gens = gens; // disable interface default methods here, especially before validation. foreach (var gen in gens) { gen.StripNonBindables(); } Validate(gens, opt); if (api_versions_xml != null) { ApiVersionsSupport.AssignApiLevels(gens, api_versions_xml, api_level); } foreach (GenBase gen in gens) { gen.FillProperties(); } foreach (var gen in gens) { gen.UpdateEnums(opt); } foreach (GenBase gen in gens) { gen.FixupMethodOverrides(); } foreach (GenBase gen in gens) { gen.FixupExplicitImplementation(); } GenerateAnnotationAttributes(gens, annotations_zips); //SymbolTable.Dump (); GenerationInfo gen_info = new GenerationInfo(csdir, javadir, assemblyQN); opt.AssemblyName = gen_info.Assembly; if (mapping_file != null) { GenerateMappingReportFile(gens, mapping_file); } new NamespaceMapping(gens).Generate(opt, gen_info); foreach (IGeneratable gen in gens) { if (gen.IsGeneratable) { gen.Generate(opt, gen_info); } } ClassGen.GenerateTypeRegistrations(opt, gen_info); ClassGen.GenerateEnumList(gen_info); // Create the .cs files for the enums var enumFiles = enums == null ? null : enummap.WriteEnumerations(enumdir, enums, FlattenNestedTypes(gens).ToArray(), opt.UseShortFileNames); gen_info.GenerateLibraryProjectFile(options, enumFiles); }
public static void AddInterfaceListenerEventsAndProperties(TypeWriter tw, InterfaceGen iface, ClassGen target, CodeGenerationOptions opt) { var methods = target.Methods.Concat(target.Properties.Where(p => p.Setter != null).Select(p => p.Setter)); var props = new HashSet <string> (); var refs = new HashSet <string> (); var eventMethods = methods.Where(m => m.IsListenerConnector && m.EventName != string.Empty && m.ListenerType == iface).OrderBy(m => m.Parameters.Count).GroupBy(m => m.Name).Select(g => g.First()).Distinct(); foreach (var method in eventMethods) { var name = method.CalculateEventName(target.ContainsName); if (string.IsNullOrEmpty(name)) { Report.LogCodedWarning(0, Report.WarningEmptyEventName, method, iface.FullName, method.Name); continue; } if (opt.GetSafeIdentifier(name) != name) { Report.LogCodedWarning(0, Report.WarningInvalidEventName, method, iface.FullName, method.Name); continue; } var prop = target.Properties.FirstOrDefault(p => p.Setter == method); if (prop != null) { var setter = "__Set" + prop.Name; props.Add(prop.Name); refs.Add(setter); AddInterfaceListenerEventsAndProperties(tw, iface, target, name, setter, string.Format("__v => {0} = __v", prop.Name), string.Format("__v => {0} = null", prop.Name), opt); } else { refs.Add(method.Name); string rm = null; string remove; if (method.Name.StartsWith("Set")) { remove = string.Format("__v => {0} (null)", method.Name); } else if (method.Name.StartsWith("Add") && (rm = "Remove" + method.Name.Substring("Add".Length)) != null && methods.Where(m => m.Name == rm).Any()) { remove = string.Format("__v => {0} (__v)", rm); } else { remove = string.Format("__v => {{throw new NotSupportedException (\"Cannot unregister from {0}.{1}\");}}", iface.FullName, method.Name); } AddInterfaceListenerEventsAndProperties(tw, iface, target, name, method.Name, method.Name, remove, opt); } } foreach (var r in refs) { tw.Fields.Add(new WeakImplementorField(r, opt)); } tw.Methods.Add(new CreateImplementorMethod(iface, opt)); }
static void Run(CodeGeneratorOptions options, DirectoryAssemblyResolver resolver) { string assemblyQN = options.AssemblyQualifiedName; string api_level = options.ApiLevel; int product_version = options.ProductVersion; bool preserve_enums = options.PreserveEnums; string csdir = options.ManagedCallableWrapperSourceOutputDirectory ?? "cs"; string javadir = "java"; string enumdir = options.EnumOutputDirectory ?? "enum"; string enum_metadata = options.EnumMetadataOutputFile ?? "enummetadata"; var references = options.AssemblyReferences; string enum_fields_map = options.EnumFieldsMapFile; string enum_flags = options.EnumFlagsFile; string enum_methods_map = options.EnumMethodsMapFile; var fixups = options.FixupFiles; var annotations_zips = options.AnnotationsZipFiles; string filename = options.ApiDescriptionFile; string mapping_file = options.MappingReportFile; bool only_xml_adjuster = options.OnlyRunApiXmlAdjuster; string api_xml_adjuster_output = options.ApiXmlAdjusterOutput; var apiSource = ""; var opt = new CodeGenerationOptions() { CodeGenerationTarget = options.CodeGenerationTarget, UseGlobal = options.GlobalTypeNames, IgnoreNonPublicType = true, UseShortFileNames = options.UseShortFileNames, ProductVersion = options.ProductVersion, SupportInterfaceConstants = options.SupportInterfaceConstants, SupportDefaultInterfaceMethods = options.SupportDefaultInterfaceMethods, SupportNestedInterfaceTypes = options.SupportNestedInterfaceTypes, }; var resolverCache = new TypeDefinitionCache(); // Load reference libraries foreach (var lib in options.LibraryPaths) { resolver.SearchDirectories.Add(lib); } foreach (var reference in references) { resolver.SearchDirectories.Add(Path.GetDirectoryName(reference)); } // Figure out if this is class-parse string apiXmlFile = filename; string apiSourceAttr = null; using (var xr = XmlReader.Create(filename)) { xr.MoveToContent(); apiSourceAttr = xr.GetAttribute("api-source"); } // We don't use shallow referenced types with class-parse because the Adjuster process // enumerates every ctor/method/property/field to build its model, so we will need // every type to be fully populated. opt.UseShallowReferencedTypes = apiSourceAttr != "class-parse"; foreach (var reference in references.Distinct()) { try { Report.Verbose(0, "resolving assembly {0}.", reference); var assembly = resolver.Load(reference); foreach (var md in assembly.Modules) { foreach (var td in md.Types) { // FIXME: at some stage we want to import generic types. // For now generator fails to load generic types that have conflicting type e.g. // AdapterView`1 and AdapterView cannot co-exist. // It is mostly because generator primarily targets jar (no real generics land). var nonGenericOverload = td.HasGenericParameters ? md.GetType(td.FullName.Substring(0, td.FullName.IndexOf('`'))) : null; if (BindSameType(td, nonGenericOverload, resolverCache)) { continue; } ProcessReferencedType(td, opt); } } } catch (Exception ex) { Report.Warning(0, Report.WarningCodeGenerator + 0, ex, "failed to parse assembly {0}: {1}", reference, ex.Message); } } // For class-parse API description, transform it to jar2xml style. if (apiSourceAttr == "class-parse") { apiXmlFile = api_xml_adjuster_output ?? Path.Combine(Path.GetDirectoryName(filename), Path.GetFileName(filename) + ".adjusted"); new Adjuster().Process(filename, opt, opt.SymbolTable.AllRegisteredSymbols(opt).OfType <GenBase> ().ToArray(), apiXmlFile, Report.Verbosity ?? 0); } if (only_xml_adjuster) { return; } // load XML API definition with fixups. Dictionary <string, EnumMappings.EnumDescription> enums = null; EnumMappings enummap = null; if (enum_fields_map != null || enum_methods_map != null) { enummap = new EnumMappings(enumdir, enum_metadata, api_level, preserve_enums); enums = enummap.Process(enum_fields_map, enum_flags, enum_methods_map); fixups.Add(enum_metadata); } Parser p = new Parser(opt); List <GenBase> gens = p.Parse(apiXmlFile, fixups, api_level, product_version); if (gens == null) { return; } apiSource = p.ApiSource; // disable interface default methods here, especially before validation. gens = gens.Where(g => !g.IsObfuscated && g.Visibility != "private").ToList(); foreach (var gen in gens) { gen.StripNonBindables(opt); if (gen.IsGeneratable) { AddTypeToTable(opt, gen); } } // Apply fixups KotlinFixups.Fixup(gens); Validate(gens, opt, new CodeGeneratorContext()); foreach (var api_versions_xml in options.ApiVersionsXmlFiles) { ApiVersionsSupport.AssignApiLevels(gens, api_versions_xml); } foreach (GenBase gen in gens) { gen.FillProperties(); } var cache = new AncestorDescendantCache(gens); foreach (var gen in gens) { gen.UpdateEnums(opt, cache); } foreach (GenBase gen in gens) { gen.FixupMethodOverrides(opt); } foreach (GenBase gen in gens) { gen.FixupExplicitImplementation(); } SealedProtectedFixups.Fixup(gens); GenerateAnnotationAttributes(gens, annotations_zips); //SymbolTable.Dump (); GenerationInfo gen_info = new GenerationInfo(csdir, javadir, assemblyQN); opt.AssemblyName = gen_info.Assembly; if (mapping_file != null) { GenerateMappingReportFile(gens, mapping_file); } new NamespaceMapping(gens).Generate(opt, gen_info); foreach (IGeneratable gen in gens) { if (gen.IsGeneratable) { gen.Generate(opt, gen_info); } } ClassGen.GenerateTypeRegistrations(opt, gen_info); ClassGen.GenerateEnumList(gen_info); // Create the .cs files for the enums var enumFiles = enums == null ? null : enummap.WriteEnumerations(enumdir, enums, FlattenNestedTypes(gens).ToArray(), opt.UseShortFileNames); gen_info.GenerateLibraryProjectFile(options, enumFiles); }
public BoundClass(ClassGen klass, CodeGenerationOptions opt, CodeGeneratorContext context, GenerationInfo generationInfo) { context.ContextTypes.Push(klass); context.ContextGeneratedMethods = new List <Method> (); generationInfo.TypeRegistrations.Add(new KeyValuePair <string, string> (klass.RawJniName, klass.AssemblyQualifiedName)); var is_enum = klass.base_symbol != null && klass.base_symbol.FullName == "Java.Lang.Enum"; if (is_enum) { generationInfo.Enums.Add(klass.RawJniName.Replace('/', '.') + ":" + klass.Namespace + ":" + klass.JavaSimpleName); } this.opt = opt; Name = klass.Name; SetVisibility(klass.Visibility); IsShadow = klass.NeedsNew; IsAbstract = klass.IsAbstract; IsSealed = klass.IsFinal; IsPartial = true; UsePriorityOrder = true; AddImplementedInterfaces(klass); Comments.Add($"// Metadata.xml XPath class reference: path=\"{klass.MetadataXPathReference}\""); if (klass.IsDeprecated) { Attributes.Add(new ObsoleteAttr(klass.DeprecatedComment) { WriteAttributeSuffix = true }); } Attributes.Add(new RegisterAttr(klass.RawJniName, null, null, true, klass.AdditionalAttributeString()) { UseGlobal = true, UseShortForm = true }); if (klass.TypeParameters != null && klass.TypeParameters.Any()) { Attributes.Add(new CustomAttr(klass.TypeParameters.ToGeneratedAttributeString())); } // Figure out our base class string obj_type = null; if (klass.base_symbol != null) { obj_type = klass.base_symbol is GenericSymbol gs && gs.IsConcrete ? gs.GetGenericType(null) : opt.GetOutputName(klass.base_symbol.FullName); } if (klass.InheritsObject && obj_type != null) { Inherits = obj_type; } // Handle fields var seen = new HashSet <string> (); SourceWriterExtensions.AddFields(this, klass, klass.Fields, seen, opt, context); var ic = new InterfaceConstsClass(klass, seen, opt, context); if (ic.ShouldGenerate) { NestedTypes.Add(ic); } // Sibling classes if (!klass.AssemblyQualifiedName.Contains('/')) { foreach (InterfaceExtensionInfo nestedIface in klass.GetNestedInterfaceTypes()) { if (nestedIface.Type.Methods.Any(m => m.CanHaveStringOverload) || nestedIface.Type.Methods.Any(m => m.Asyncify)) { sibling_types.Add(new InterfaceExtensionsClass(nestedIface.Type, nestedIface.DeclaringType, opt)); } } } if (klass.IsAbstract) { sibling_types.Add(new ClassInvokerClass(klass, opt)); } AddNestedTypes(klass, opt, context, generationInfo); AddBindingInfrastructure(klass); AddConstructors(klass, opt, context); AddProperties(klass, opt); AddMethods(klass, opt, context); AddAbstractMembers(klass, opt, context); AddExplicitGenericInterfaceMembers(klass, opt); AddCharSequenceEnumerator(klass); context.ContextGeneratedMethods.Clear(); context.ContextTypes.Pop(); }
public ClassInvokerClass(ClassGen klass, CodeGenerationOptions opt) { Name = $"{klass.Name}Invoker"; IsInternal = true; IsPartial = true; UsePriorityOrder = true; Inherits = klass.Name; foreach (var igen in klass.GetAllDerivedInterfaces().Where(i => i.IsGeneric)) { Implements.Add(opt.GetOutputName(igen.FullName)); } if (opt.CodeGenerationTarget == CodeGenerationTarget.JavaInterop1) { Attributes.Add(new JniTypeSignatureAttr(klass.RawJniName, false)); } else { Attributes.Add(new RegisterAttr(klass.RawJniName, noAcw: true, additionalProperties: klass.AdditionalAttributeString()) { UseGlobal = true }); } SourceWriterExtensions.AddSupportedOSPlatform(Attributes, klass, opt); ConstructorWriter ctor = opt.CodeGenerationTarget == CodeGenerationTarget.JavaInterop1 ? new ConstructorWriter { Name = Name, IsPublic = true, BaseCall = "base (ref reference, options)", Parameters = { new MethodParameterWriter("reference", new TypeReferenceWriter("ref JniObjectReference")), new MethodParameterWriter("options", new TypeReferenceWriter("JniObjectReferenceOptions")), }, } : new ConstructorWriter { Name = Name, IsPublic = true, BaseCall = "base (handle, transfer)", Parameters = { new MethodParameterWriter("handle", TypeReferenceWriter.IntPtr), new MethodParameterWriter("transfer", new TypeReferenceWriter("JniHandleOwnership")), }, } ; Constructors.Add(ctor); // ClassInvokerHandle Fields.Add(new PeerMembersField(opt, klass.RawJniName, $"{klass.Name}Invoker", false)); Properties.Add(new JniPeerMembersGetter()); if (opt.CodeGenerationTarget != CodeGenerationTarget.JavaInterop1) { Properties.Add(new ThresholdTypeGetter()); } AddMemberInvokers(klass, opt, new HashSet <string> ()); }