// for debugging static void WriteAllNamespaces(TypeAggregator types) { foreach (var type in types.AllTypes) { Console.WriteLine(type.Namespace); } }
static SLImportModules ImportsForPlatform(PlatformName platform, TypeDefinition [] types) { var imports = new SLImportModules(); var modulesUsed = new HashSet <string> (); foreach (var type in types) { var moduleName = type.Namespace; var name = "ThisShouldNeverGetFiltered"; if (TypeAggregator.FilterModuleAndName(platform, type.Namespace, ref name)) { TypeAggregator.RemapModuleAndName(platform, ref moduleName, ref name, TypeType.None); if (String.IsNullOrEmpty(moduleName)) { continue; } if (modulesUsed.Contains(moduleName)) { continue; } modulesUsed.Add(moduleName); imports.Add(new SLImport(moduleName)); } } return(imports); }
static SLAttribute AvailableAttributeForType(PlatformName platform, TypeDefinition type) { var map = TypeAggregator.AvailableMapForPlatform(platform); string available; if (map != null && map.TryGetValue(type.FullName, out available)) { return(new SLAttribute("available", true, new SLIdentifier(available))); } if (type.HasCustomAttributes) { foreach (var customAttribute in type.CustomAttributes) { if (customAttribute.AttributeType.Name == "IntroducedAttribute") { var platformValue = PlatformValueForPlatform(platform); if (!customAttribute.HasConstructorArguments) { continue; } if (customAttribute.ConstructorArguments [0].Value.ToString() != platformValue) { continue; } var attr = AvailableAttributeFromIntroducedAttributeType(platform, customAttribute); if (attr != null) { return(attr); } } else { var attributeName = AttributeNameForPlatform(platform); if (attributeName == null) { continue; } if (customAttribute.AttributeType.Name != attributeName) { continue; } var attr = AvailableAttributeFromAttributeType(platform, customAttribute); if (attr != null) { return(attr); } } } } return(null); }
/// <summary> /// Determines if a type should be included. Types are included if they are in namespaces, they do not have /// generic parameters, and they pass TypeAggregator's filter. /// </summary> /// <param name="type">The type to check</param> /// <param name="namespaces">The namespaces to include</param> /// <param name="platform">The platform being targeted</param> /// <returns>true if type should be included, false otherwise</returns> static bool IncludeType(TypeDefinition type, List <string> namespaces, PlatformName platform) { if (!namespaces.Contains(type.Namespace)) { return(false); } if (type.HasGenericParameters) { return(false); } var name = type.Name; if (TypeAggregator.FilterModuleAndName(platform, type.Namespace, ref name)) { return(true); } return(false); }
/// <summary> /// Makes body of map, where each line is of form: /// "Swift.Int": Int.self /// </summary> /// <param name="types">List of type definitions to put in map body</param> /// <param name="platform">Platform for compilation</param> /// <returns>String containing map body, one mapping per line.</returns> static string MapBodyForTypes(List <TypeDefinition> types, PlatformName platform) { StringBuilder result = new StringBuilder(); foreach (var type in types) { var cSharpName = type.Name; var swiftName = type.Name; var moduleName = type.Namespace; TypeAggregator.RemapModuleAndName(platform, ref moduleName, ref swiftName, TypeType.None); result.AppendLine($" \"{moduleName}.{cSharpName}\": {swiftName}.self,"); } if (result.Length == 0) { return(""); } return(result.ToString().Substring(0, result.Length - 1)); }
static SLFile GenerateStubsFromTypes(TypeAggregator types, PlatformName platform, List <string> namespaces) { var imports = ImportsForPlatform(platform, types.AllTypes); SLConditionalCompilation.If(PlatformCondition(platform)).AttachBefore(imports); new SLComment(kRobotText, true).AttachBefore(imports); var slfile = new SLFile(imports); slfile.Functions.AddRange(MetaWrapperForFunc(platform, types.PublicEnums, TypeType.Enum, namespaces)); slfile.Functions.AddRange(MetaWrapperForFunc(platform, types.PublicStructs, TypeType.Struct, namespaces)); // can't do ordinal because function names can have Bockovers slfile.Functions.Sort((func1, func2) => String.Compare(func1.Name.Name, func2.Name.Name, StringComparison.InvariantCulture)); slfile.Trailer.Add(SLConditionalCompilation.Endif); // can't do ordinal because module names can have Bockovers imports.Sort((import1, import2) => String.Compare(import1.Module, import2.Module, StringComparison.InvariantCulture)); return(slfile); }
static IEnumerable <SLFunc> MetaWrapperForFunc(PlatformName platform, IEnumerable <TypeDefinition> types, TypeType entityType, List <string> namespaces) { foreach (var type in types) { if (namespaces.Count > 0 && !namespaces.Contains(type.Namespace)) { continue; } if (type.HasGenericParameters) { continue; } var moduleName = type.Namespace; var name = type.Name; if (TypeAggregator.FilterModuleAndName(platform, moduleName, ref name)) { yield return(MetadataFuncForType(platform, type, entityType)); } } }
static SLFunc MetadataFuncForType(PlatformName platform, TypeDefinition type, TypeType entityType) { var name = type.Name; var moduleName = type.Namespace; // typename based on C# type name, not remapped name var funcID = new SLIdentifier(FuncIDForTypeDefinition(type)); TypeAggregator.RemapModuleAndName(platform, ref moduleName, ref name, entityType); var value = new SLIdentifier($"{name}.self"); var returnLine = SLReturn.ReturnLine(value); var body = new SLCodeBlock(null); body.Add(returnLine); var func = new SLFunc(Visibility.Public, new SLSimpleType("Any.Type"), funcID, null, body); new SLComment(type.FullName, true).AttachBefore(func); var attr = AvailableAttributeForType(platform, type); if (attr != null) { attr.AttachBefore(func); } return(func); }
/// <summary> /// Creates a hash table for all types, and a function to access them. /// </summary> /// <param name="types">Aggregated types, a subset of which are included</param> /// <param name="platform">Platform targeted (typically iOS or Mac)</param> /// <param name="namespaces">Namespaces to include</param> /// <returns>SLFile which, when written, has hash table and function as described in summary</returns> static SLFile GenerateSwiftHashTableFromTypes(TypeAggregator types, PlatformName platform, List <string> namespaces) { var imports = ImportsForPlatform(platform, types.AllTypes); SLConditionalCompilation.If(PlatformCondition(platform)).AttachBefore(imports); new SLComment(kRobotText, true).AttachBefore(imports); var slfile = new SLFile(imports); // collect and filter types var selectedTypes = new List <TypeDefinition> (); selectedTypes.AddRange(types.PublicEnums); selectedTypes.AddRange(types.PublicStructs); selectedTypes = selectedTypes.FindAll(type => IncludeType(type, namespaces, platform)); selectedTypes.Sort((type1, type2) => String.CompareOrdinal(type1.FullName, type2.FullName)); // sort types into respective availability categories var availableCategories = new Dictionary <string, List <TypeDefinition> > (); foreach (var type in selectedTypes) { var typeAttributeObj = AvailableAttributeForType(platform, type); var typeAttribute = typeAttributeObj == null ? "" : typeAttributeObj.StringRep; List <TypeDefinition> otherTypes; if (availableCategories.TryGetValue(typeAttribute, out otherTypes)) { otherTypes.Add(type); } else { otherTypes = new List <TypeDefinition> { type }; availableCategories.Add(typeAttribute, otherTypes); } } // debug //foreach (var pair in availableCategories) { // Console.WriteLine ($"Available attribute \"{pair.Key}\": {pair.Value.Aggregate("", (str, type) => str + ", " + type.Name).Substring(2)} (length: {pair.Value.Count})"); //} // add map for each availability status var maps = new StringBuilder(); var mergeFns = new StringBuilder(); mergeFns.AppendLine("private func addToTypes() {"); foreach (var category in availableCategories) { var availableInfo = category.Key; var modifier = "public"; var prefix = ""; var header = ""; if (availableInfo != "") { modifier = "private"; prefix = AvailableDictPrefix(availableInfo); header = $"@{availableInfo}"; } if (header != "") { maps.AppendLine(header); } maps.AppendLine($"{modifier} var {prefix}types: [String:Any.Type] = ["); maps.AppendLine(MapBodyForTypes(category.Value, platform)); maps.AppendLine("]"); maps.AppendLine(); if (availableInfo != "") { mergeFns.AppendLine($" if #{availableInfo} {{"); mergeFns.AppendLine($" types.merge({AvailableDictPrefix (availableInfo)}types) {{ (_, newer) in newer }}"); mergeFns.AppendLine(" }"); } } if (!availableCategories.ContainsKey("")) { maps.AppendLine("public var types = [String:Any.Type]()"); } mergeFns.AppendLine("}"); slfile.Declarations.Add(new SLLine(new SLIdentifier(maps.ToString()), false)); slfile.Declarations.Add(new SLLine(new SLIdentifier(mergeFns.ToString()), false)); // add function to fetch type var handleTranslation = @" private var merged = false public func getSwiftType(str: UnsafeMutablePointer<String>, result: UnsafeMutablePointer<Any.Type>) -> Bool { if !merged { merged = true addToTypes() } if let mt = types[str.pointee] { result.initialize(to: mt) return true } return false }"; slfile.Declarations.Add(new SLLine(new SLIdentifier(handleTranslation), false)); slfile.Trailer.Add(SLConditionalCompilation.Endif); // can't do ordinal because module names can have Bockovers imports.Sort((import1, import2) => String.Compare(import1.Module, import2.Module, StringComparison.InvariantCulture)); return(slfile); }
static CSFile GeneratePInvokesFromTypes(TypeAggregator types, PlatformName platform, string framework) { var fileName = Path.GetFileNameWithoutExtension(framework); // /path/XamGlue.framework -> XamGlue var dylibFile = Path.Combine(framework, fileName); var funcs = TLFunctionsForFile(dylibFile, platform); var ns = new CSNamespace("SwiftRuntimeLibrary.SwiftMarshal"); var use = new CSUsingPackages(); use.And(new CSUsing("System.Runtime.InteropServices")) .And(new CSUsing("System")) .And(new CSUsing("System.Collections.Generic")) .And(new CSUsing("SwiftRuntimeLibrary")); var csFile = new CSFile(use, new CSNamespace [] { ns }); var csClass = new CSClass(CSVisibility.Internal, $"{fileName}Metadata"); new CSComment(kRobotText).AttachBefore(use); CSConditionalCompilation.If(PlatformToCSCondition(platform)).AttachBefore(use); CSConditionalCompilation.Endif.AttachAfter(ns); ns.Block.Add(csClass); var typeOntoPinvoke = new List <KeyValuePair <CSBaseExpression, CSBaseExpression> > (); var typesToProcess = new List <TypeDefinition> (); typesToProcess.AddRange(types.PublicEnums); typesToProcess.AddRange(types.PublicStructs); // pre-sort by function name typesToProcess.Sort((type1, type2) => String.CompareOrdinal(FuncIDForTypeDefinition(type1), FuncIDForTypeDefinition(type2))); foreach (var type in typesToProcess) { if (type.HasGenericParameters) { continue; } var moduleName = type.Namespace; var name = type.Name; if (TypeAggregator.FilterModuleAndName(platform, moduleName, ref name)) { var pinvoke = PInvokeForType(type, funcs); if (pinvoke != null) { csClass.Methods.Add(pinvoke); use.AddIfNotPresent(type.Namespace); var typeOf = new CSSimpleType(type.FullName).Typeof(); var funcName = pinvoke.Name; typeOntoPinvoke.Add(new KeyValuePair <CSBaseExpression, CSBaseExpression> (typeOf, funcName)); } } } var initializers = typeOntoPinvoke.Select(typeAndFunc => new CSInitializer(new CSBaseExpression [] { typeAndFunc.Key, typeAndFunc.Value }, false)); var bindingExpr = new CSInitializedType(new CSFunctionCall("Dictionary<Type, Func<SwiftMetatype>>", true), new CSInitializer(initializers, true)); var bindingDecl = new CSFieldDeclaration(new CSSimpleType("Dictionary<Type, Func<SwiftMetatype>>"), "ObjCBindingSwiftMetatypes", bindingExpr, CSVisibility.Internal, true); csClass.Fields.Add(new CSLine(bindingDecl)); use.Sort((package1, package2) => String.CompareOrdinal(package1.Package, package2.Package)); return(csFile); }
public static int Main(string [] args) { TypeOMaticOptions options = new TypeOMaticOptions(); var extra = options.ParseCommandLine(args); if (options.PrintHelp) { options.PrintUsage(Console.Out); return(0); } if (extra.Count > 0) { // Warn about extra params that are ignored. Console.WriteLine($"WARNING: The following extra parameters will be ignored: '{ String.Join (",", extra) }'"); } if (string.IsNullOrEmpty(options.SwiftLibPath)) { Console.WriteLine("Unable to find the custom swift compiler libraries. Try using --swift-lib-path."); return(1); } if (options.Platform == PlatformName.None) { Console.WriteLine($"Unknown platform {options.Platform}"); options.PrintUsage(Console.Out); return(1); } var targetDirectory = Path.Combine(options.SwiftLibPath, PlaformToDirectoryName(options.Platform)); if (!Directory.Exists(targetDirectory)) { Console.WriteLine($"Unable to find library directory {targetDirectory}"); return(1); } if (options.ToGenerate == null || !String.Equals(options.ToGenerate, "swift") && !String.Equals(options.ToGenerate, "csharp")) { Console.WriteLine("You need to specify what type of code you want to generate, either 'swift' or 'csharp'."); return(1); } if (options.Namespaces.Count == 0) { Console.WriteLine("You need to select input namespaces."); return(1); } if (String.Equals(options.ToGenerate, "csharp") && options.Framework == null) { Console.WriteLine("You need to select a XamGlue.framework to generate C#"); return(1); } if (options.Framework != null && !Directory.Exists(options.Framework)) { Console.WriteLine($"Unable to find XamGlue framework at {options.Framework}."); return(1); } var aggregatedTypes = new TypeAggregator(options.Platform); aggregatedTypes.Aggregate(); var writer = new CodeWriter(options.OutputWriter); if (String.Equals(options.ToGenerate, "swift")) { var slFile = GenerateSwiftHashTableFromTypes(aggregatedTypes, options.Platform, options.Namespaces); slFile.WriteAll(writer); } else { var csFile = GenerateCSharpHashTableFromTypes(aggregatedTypes, options.Platform, options.Namespaces, options.Framework); csFile.WriteAll(writer); } writer.TextWriter.Flush(); if (options.OutputWriter != Console.Out) { options.OutputWriter.Close(); options.OutputWriter.Dispose(); } return(0); }
/// <summary> /// Creates a list of all types, and a hash table which is initialized on first lookup from C# type /// to Swift types. /// </summary> /// <param name="types">Aggregated types, a subset of which are included</param> /// <param name="platform">Platform targeted (typically iOS or Mac)</param> /// <param name="namespaces">Namespaces to include</param> /// <param name="framework">Name of framework used (typically XamGlue)</param> /// <returns>CSFile which, when written, has function looking up in hash table as described in summary</returns> static CSFile GenerateCSharpHashTableFromTypes(TypeAggregator types, PlatformName platform, List <string> namespaces, string framework) { var fileName = Path.GetFileNameWithoutExtension(framework); var ns = new CSNamespace("SwiftRuntimeLibrary.SwiftMarshal"); var use = new CSUsingPackages(); use.And(new CSUsing("System.Runtime.InteropServices")) .And(new CSUsing("System")) .And(new CSUsing("System.Collections.Generic")) .And(new CSUsing("SwiftRuntimeLibrary")); var csFile = new CSFile(use, new CSNamespace [] { ns }); var csClass = new CSClass(CSVisibility.Internal, $"{fileName}Metadata"); new CSComment(kRobotText).AttachBefore(use); CSConditionalCompilation.If(PlatformToCSCondition(platform)).AttachBefore(use); CSConditionalCompilation.Endif.AttachAfter(ns); ns.Block.Add(csClass); // collect all possible types, filter and sort var selectedTypes = new List <TypeDefinition> (); selectedTypes.AddRange(types.PublicEnums); selectedTypes.AddRange(types.PublicStructs); selectedTypes = selectedTypes.FindAll(type => IncludeType(type, namespaces, platform)); selectedTypes.Sort((type1, type2) => String.CompareOrdinal(type1.FullName, type2.FullName)); // add used namespaces to import list, sort list foreach (var type in selectedTypes) { use.AddIfNotPresent(type.Namespace); } use.Sort((package1, package2) => String.CompareOrdinal(package1.Package, package2.Package)); // create list of types to translate var typesForList = selectedTypes.Select(type => new CSSimpleType(type.FullName).Typeof()); var listInitializeExpr = new CSInitializedType( new CSFunctionCall("List<Type>", true), new CSInitializer(typesForList, true) ); var listBindingDecl = new CSFieldDeclaration(new CSSimpleType("List<Type>"), "csImportTypes", listInitializeExpr, CSVisibility.Internal, true); csClass.Fields.Add(new CSLine(listBindingDecl)); // create pinvoke for function var dylibFile = Path.Combine(framework, fileName); var funcs = TLFunctionsForFile(dylibFile, platform); var handleTranslationCode = new CSIdentifier(@" public static unsafe bool GetSwiftType (Type t, out SwiftMetatype md) { using (var swiftStr = new SwiftString (t.FullName)) { return SwiftCore.GetEnumMetadataByName (swiftStr.SwiftData, out md); } } internal static Dictionary<Type, SwiftMetatype> csImportMeta = null; internal static bool TryGetImportedMetadata (Type cSharpType, out SwiftMetatype md) { if (csImportMeta == null) { csImportMeta = new Dictionary<Type, SwiftMetatype> (csImportTypes.Count); foreach (var t in csImportTypes) { SwiftMetatype meta; GetSwiftType(t, out meta); csImportMeta [t] = meta; } } return csImportMeta.TryGetValue (cSharpType, out md); }" ); csClass.Fields.Add(new CSLine(handleTranslationCode, false)); return(csFile); }