public void Run() { if (this.PluginInfo == null) { throw new ArgumentNullException(nameof(PluginInfo)); } if (this.PlatformApis == null || this.PlatformApis.Count == 0 || this.PlatformApis.Values.Any(a => a == null || a.Assemblies == null || a.Assemblies.Count == 0)) { throw new ArgumentNullException(nameof(PlatformApis)); } if (this.PlatformApis.Values.Any(a => a.Assemblies.Count > 1)) { throw new NotSupportedException("Merging does not support multiple assemblies."); } this.platformAssemblies = this.PlatformApis.ToDictionary(pa => pa.Key, pa => pa.Value.Assemblies.Single()); this.platformTypesIndex = IndexTypes(this.platformAssemblies); this.platformMembersIndex = IndexMembers(this.platformTypesIndex); this.ValidateAssemblies(); if (this.validationWarnings.Count > 0) { Log.Warning(""); foreach (string message in this.validationWarnings) { Log.Warning("{0}", message); } } if (this.validationErrors.Count > 0) { Log.Error(""); foreach (string message in this.validationErrors) { Log.Error("{0}", message); } Log.Error(""); throw new InvalidOperationException("Cannot merge APIs due to validation errors."); } Assembly mergedAssembly = this.MergeAssemblies(this.platformAssemblies.Values); this.MergedApi = new ClrApi { Platform = String.Join(",", this.PlatformApis.Keys), Assemblies = new List <Assembly>(new[] { mergedAssembly }), }; // Event classes are automatically marshalled by value. foreach (Class eventClass in mergedAssembly.Types.OfType <Class>().Where( c => c.ExtendsType == "System.EventArgs")) { PluginInfo.AssemblyClassInfo classInfo = this.PluginInfo.Assembly.Classes .FirstOrDefault(c => c.Name == eventClass.Name || c.Name == eventClass.FullName); if (classInfo != null) { classInfo.MarshalByValue = "true"; } else { this.PluginInfo.Assembly.Classes.Add(new PluginInfo.AssemblyClassInfo { Name = eventClass.Name, MarshalByValue = "true", }); } } }
void WriteTypeScriptBindingsForType( Type type, IEnumerable <Type> importTypes, CodeWriter code) { string simpleTypeName; if (!type.IsNested) { if (!(type is Enum)) { foreach (Type importType in importTypes) { code.Code($"import {importType.Name} = require(\"./{importType.Name}\");"); } if (!(type is Interface)) { code.Code(); code.Code($"import {{ bridge, NativeObject, NativeReference{(this.ES6 ? "" : ", Promise")} }} " + $"from \"{this.BridgeModuleName}\";"); } else if (!this.ES6) { code.Code(); code.Code($"import {{ Promise }} from \"{this.BridgeModuleName}\";"); } } code.Code(); simpleTypeName = type.Name; } else { // TODO: Support multi-level nesting simpleTypeName = type.Name.Replace('+', '.'); } simpleTypeName = simpleTypeName.Substring(simpleTypeName.LastIndexOf('.') + 1); PluginInfo.AssemblyClassInfo classInfo = this.PluginInfo.Assembly.Classes .FirstOrDefault(c => c.Name == type.Name || c.Name == type.FullName); bool marshalByValue = (classInfo != null && classInfo.MarshalByValue == "true"); if (type is Class || type is Struct) { string extendsTypeName = (marshalByValue ? "NativeObject" : "NativeReference"); code.Code($"{(type.IsNested ? "export " : "")}class {simpleTypeName} extends {extendsTypeName} {{"); code.Code($"\tstatic type: string = \"{type.FullName}\";"); } else if (type is Interface) { code.Code($"{(type.IsNested ? "export " : "")}interface {simpleTypeName} {{"); } else if (type is Enum) { code.Code($"{(type.IsNested ? "export " : "")}enum {simpleTypeName} {{"); } else { throw new NotSupportedException("Type type not supported: " + type.GetType().Name); } CodeWriter members = code.Indent(); Func <Property, bool> isStructProperty = p => p.GetMethod != null && !p.IsStatic; if (type is Enum) { foreach (EnumValue field in type.Members.Cast <EnumValue>().OrderBy(f => (int)f.Value)) { this.WriteTypeScriptBindingForField(type, field, members); } } else if (marshalByValue) { // Give the C3P JS marshaller hints about how to convert certain marshal-by-value fields. IEnumerable <Property> properties = type.Members.OfType <Property>().Where(isStructProperty); string[] guidFields = properties.Where(p => p.PropertyType == "System.Guid") .Select(p => p.Name).ToArray(); if (guidFields.Length > 0) { members.Code("static typeConversions: any = { " + String.Join(", ", guidFields.Select(f => $"\"{Uncapitalize(f)}\": \"uuid\"")) + " };"); } bool isFirstField = true; foreach (Property property in properties) { if (isFirstField) { members.Code(); isFirstField = false; } this.WriteTypeScriptBindingForField(type, property, members); } } if (marshalByValue) { members.Code(); members.Code( "constructor() {", $"\tsuper({simpleTypeName}.type);", "}"); } else if (type is Class) { string implicitContextArgument = null; foreach (Constructor constructor in type.Members.OfType <Constructor>()) { members.Code(); this.WriteTypeScriptBindingForConstructor(type, constructor, members); if (implicitContextArgument == null) { implicitContextArgument = GetImplicitContextArgument(constructor); } } string argsWithContext = (implicitContextArgument == null ? "args" : "[" + implicitContextArgument + "].concat(args)"); members.Code(); members.Code("constructor(handle: Promise<number>);"); members.Code(); members.Code( $"constructor(...args: any[]) {{", $"\tsuper(", $"\t\t{simpleTypeName}.type,", $"\t\t(args.length === 1 && args[0] instanceof Promise ? args[0] :", $"\t\t\tbridge.createInstance({type.Name}.type, {argsWithContext})));", $"}}"); members.Code(); members.Code("dispose(): Promise<void> {"); members.Code("\tvar releaseNativeInstance: () => Promise<void> = "); members.Code("\t\tbridge.releaseInstance.bind(undefined, this.type, this.handle);"); members.Code("\treturn super.dispose().then(function () { return releaseNativeInstance(); });"); members.Code("}"); } foreach (Property property in type.Members.OfType <Property>() .Where(p => !marshalByValue || !isStructProperty(p))) { members.Code(); this.WriteTypeScriptBindingsForProperty(type, property, members); } foreach (Event eventMember in type.Members.OfType <Event>()) { members.Code(); this.WriteTypeScriptBindingForEvent(type, eventMember, members); } foreach (Method method in type.Members.OfType <Method>()) { members.Code(); this.WriteTypeScriptBindingForMethod(type, method, members); } code.Code("}"); if (!(type is Enum) && !(type is Interface)) { code.Code($"bridge.registerType({simpleTypeName}.type, <any>{simpleTypeName});"); } if (type.Members.OfType <Type>().Count() != 0) { code.Code(); code.Code($"module {type.Name} {{"); foreach (Type nestedType in type.Members.OfType <Type>()) { code.Code(); this.WriteTypeScriptBindingsForType(nestedType, null, code.Indent()); } code.Code("}"); } if (!type.IsNested) { code.Code(); code.Code($"export = {type.Name};"); } }