예제 #1
0
파일: RustCodeGen.cs 프로젝트: yvt/ngspades
            public RustInterfaceMethodInfo(RustCodeGen gen, Marshaller.ComMethodInfo cmi)
            {
                RustCodeGen    = gen;
                ComMethodInfo  = cmi;
                ParameterInfos = cmi.ParameterInfos.Select((pi) => new RustParameterInfo(gen, pi)).ToArray();

                string name = cmi.MethodInfo.Name;

                if (name.StartsWith("get_") || name.StartsWith("set_"))
                {
                    NativeName = name.Substring(0, 4) + SnakeCaseConverter.Join(DotNetPascalCaseConverter.Split(name.Substring(4))).ToLowerInvariant();
                }
                else
                {
                    NativeName = SnakeCaseConverter.Join(DotNetPascalCaseConverter.Split(name)).ToLowerInvariant();
                }

                // Retrieve the documentation entry
                if (gen.options.RustdocEntrySource != null)
                {
                    if (name.StartsWith("get_") || name.StartsWith("set_"))
                    {
                        var prop = cmi.MethodInfo.DeclaringType.GetProperty(name.Substring(4));
                        if (prop != null)
                        {
                            RustdocEntry = gen.options.RustdocEntrySource.GetEntryForProperty(prop,
                                                                                              name.StartsWith("set_"));
                        }
                    }
                    else
                    {
                        RustdocEntry = gen.options.RustdocEntrySource.GetEntryForMethod(cmi.MethodInfo);
                    }
                }

                if (cmi.ReturnsHresult)
                {
                    NativeReturnTypeName = $"{gen.options.NgscomCratePath}::HResult";
                }
                else
                {
                    NativeReturnTypeName = RustCodeGen.TranslateNativeType(cmi.NativeReturnType, false, false);
                }
            }
예제 #2
0
파일: RustCodeGen.cs 프로젝트: yvt/ngspades
        void GenerateInterface(Type type)
        {
            var info     = new Marshaller.InterfaceInfo(type);
            var typeInfo = type.GetTypeInfo();

            var name = type.Name;

            if (!name.StartsWith("I"))
            {
                throw new NotSupportedException($"The name of interface {type.FullName} doesn't start with the character 'I'.");
            }
            var nameDecomposed = DotNetPascalCaseConverter.Split(name.Substring(1));

            nameDecomposed[0] = "I" + nameDecomposed[0];
            var upperSnakeCaseName = SnakeCaseConverter.Join(nameDecomposed).ToUpperInvariant();
            var lowerSnakeCaseName = SnakeCaseConverter.Join(nameDecomposed).ToLowerInvariant();

            var iidIdt = "IID_" + upperSnakeCaseName;
            var iid    = info.ComGuid.ToByteArray();

            stringBuilder.AppendLine($"com_iid!({iidIdt} = [{ByteArrayToIntegerConstant(iid, 0, 4, false)}, " +
                                     $"{ByteArrayToIntegerConstant(iid, 4, 2, false)}, {ByteArrayToIntegerConstant(iid, 6, 2, false)}, " +
                                     $"[{ByteArrayToIntegerConstant(iid, 8, 8, true)}]]);");

            var           baseTypes           = new List <Type>();
            var           indirectlyBaseTypes = new HashSet <Type>();
            Action <Type> scanIndirectBase    = null;

            scanIndirectBase = (theType) => {
                indirectlyBaseTypes.Add(theType);
                foreach (var bt in theType.GetTypeInfo().ImplementedInterfaces)
                {
                    scanIndirectBase(bt);
                }
            };
            foreach (var bt in typeInfo.ImplementedInterfaces)
            {
                baseTypes.Add(bt);

                var ti2 = bt.GetTypeInfo();
                if (!ti2.IsInterface)
                {
                    throw new InvalidOperationException();
                }

                foreach (var bt2 in ti2.ImplementedInterfaces)
                {
                    scanIndirectBase(bt2);
                }
            }

            var realBaseTypes = baseTypes.Where((t) => !indirectlyBaseTypes.Contains(t)).ToArray();

            if (realBaseTypes.Length > 1)
            {
                throw new NotSupportedException($"Cannot emit interop code for the interface {type.FullName} because " +
                                                "Interfaces with multiple base interface are not supported by NgsCOM.");
            }

            foreach (var t in realBaseTypes)
            {
                EnqueueTypeGeneration(t);
            }

            var baseDeclarations = new List <string>();

            foreach (var t in realBaseTypes)
            {
                var nativeName = GetOutputIdentifier(t);
                baseDeclarations.Add($"({nativeName}, {nativeName}Trait)");
            }
            foreach (var t in indirectlyBaseTypes)
            {
                var nativeName = GetOutputIdentifier(t);
                baseDeclarations.Add(nativeName);
            }

            stringBuilder.AppendLine("com_interface! {");

            var methods = info.ComMethodInfos
                          .Where((cmi) => cmi.MethodInfo.DeclaringType == info.Type)
                          .Select((cmi) => new RustInterfaceMethodInfo(this, cmi)).ToArray();

            // Emit the documentation comment
            {
                var doc = options.RustdocEntrySource?.GetEntryForType(type);
                if (doc.HasValue)
                {
                    GenerateDocComment(doc, "\t");
                }
                else
                {
                    stringBuilder.AppendLine($"\t/// `{type.FullName}`");
                }
                stringBuilder.AppendLine("\t///");
                stringBuilder.AppendLine("\t/// # COM interop");
                stringBuilder.AppendLine($"\t/// This type was generated automatically from `{type.FullName}`.");

                // Emit the template code here
                stringBuilder.AppendLine("\t///");
                stringBuilder.AppendLine($"\t/// Use the following template code to create a COM class");
                stringBuilder.AppendLine($"\t/// that implements `{name}`:");
                stringBuilder.AppendLine("\t///");
                stringBuilder.AppendLine("\t/// ```ignore");
                stringBuilder.AppendLine("\t/// com_impl! {");
                stringBuilder.AppendLine("\t///     class MyClassName {");
                stringBuilder.AppendLine($"\t///         {lowerSnakeCaseName}: {name};");
                stringBuilder.AppendLine("\t///         @data: MyClassNameData;");
                stringBuilder.AppendLine("\t///     }");
                stringBuilder.AppendLine("\t/// }");
                stringBuilder.AppendLine("\t///");
                stringBuilder.AppendLine($"\t/// impl {name}Trait for MyClassName {{");
                stringBuilder.AppendLine("\t///");
                foreach (var rimi in methods)
                {
                    stringBuilder.AppendLine($"\t///     {rimi.GetSignature(false)} {{");
                    if (rimi.ComMethodInfo.ReturnsHresult)
                    {
                        stringBuilder.AppendLine("\t///         \thresults::E_NOTIMPL");
                    }
                    else
                    {
                        stringBuilder.AppendLine("\t///         \tunimplemented!()");
                    }
                    stringBuilder.AppendLine("\t///     }");
                    stringBuilder.AppendLine("\t///");
                }
                stringBuilder.AppendLine("\t/// }");
                stringBuilder.AppendLine("\t/// ```");
                stringBuilder.AppendLine("\t///");
            }

            stringBuilder.AppendLine($"\tinterface ({name}, {name}Trait): {string.Join(", ", baseDeclarations)} {{");
            stringBuilder.AppendLine($"\t\tiid: {iidIdt},");
            stringBuilder.AppendLine($"\t\tvtable: {name}Vtbl,");
            stringBuilder.AppendLine();

            foreach (var rimi in methods)
            {
                GenerateDocComment(rimi.RustdocEntry, "\t\t");

                stringBuilder.AppendLine($"\t\t{rimi.GetSignature(true)};");
            }

            stringBuilder.AppendLine("\t}");
            stringBuilder.AppendLine("}");
            stringBuilder.AppendLine();
        }