Пример #1
0
        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",
                    });
                }
            }
        }
Пример #2
0
        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};");
            }
        }