contextfree / winrt-rust

Use and (eventually) make Windows Runtime APIs with Rust
Apache License 2.0
142 stars 10 forks source link

Generator is failing on Windows 10.0.17763 #64

Closed chris-morgan closed 5 years ago

chris-morgan commented 5 years ago

Build 17763 added the method Windows.Web.UI.Interop.WebViewControl.AddInitializeScript which I need, so I tried to run the generator, but it failed:

Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at Generator.Generator.GetTypeDefinition(TypeReference t) in ...\winrt-rust\Generator\Generator.cs:line 74
   at Generator.Types.TypeHelpers.GetElementTypeName(Generator gen, ITypeRequestSource source, TypeReference t, TypeUsage usage) in ...\winrt-rust\Generator\Types\TypeHelpers.cs:line 139
   at Generator.Types.TypeHelpers.GetTypeName(Generator gen, ITypeRequestSource source, TypeReference t, TypeUsage usage) in ...\winrt-rust\Generator\Types\TypeHelpers.cs:line 73
   at Generator.Types.TypeHelpers.GetInputTypeName(Generator gen, ITypeRequestSource source, TypeReference t, InputKind kind) in ...\winrt-rust\Generator\Types\TypeHelpers.cs:line 192
   at Generator.Types.MethodDetailsCache.<>c__DisplayClass7_0.<MakeInputParameters>b__0(String name, Tuple`2 t) in ...\winrt-rust\Generator\Types\MethodDef.cs:line 22
   at System.Linq.Enumerable.<ZipIterator>d__61`3.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Generator.Types.MethodDetailsCache.MakeInputParameters(Generator generator, ITypeRequestSource source) in ...\winrt-rust\Generator\Types\MethodDef.cs:line 22
   at Generator.Types.ClassMethodDef..ctor(MethodDef method, ClassDef containingClass, Boolean isFactory) in ...\winrt-rust\Generator\Types\ClassMethodDef.cs:line 64
   at Generator.Types.ClassDef.CollectDependencies() in ...\winrt-rust\Generator\Types\ClassDef.cs:line 63
   at Generator.Generator.CollectDependencies() in ...\winrt-rust\Generator\Generator.cs:line 92
   at Generator.Program.Main(String[] args) in ...\winrt-rust\Generator\Program.cs:line 19

The missing key, t.FullName, seems to be System.Guid& modopt(System.Runtime.CompilerServices.IsConst). I have investigated no further yet.

chris-morgan commented 5 years ago

The failures I observe are all connected to Windows.Foundation.GuidHelper.Equals.

I made these changes to allow it to continue in case of error:

diff --git a/Generator/Types/ClassDef.cs b/Generator/Types/ClassDef.cs
index 69a8ddc..0daec82 100644
--- a/Generator/Types/ClassDef.cs
+++ b/Generator/Types/ClassDef.cs
@@ -60,7 +60,14 @@ namespace Generator.Types
             }
             foreach (var m in staticMethods)
             {
-                methodWrappers.Add(new ClassMethodDef(m, this, false));
+                try
+                {
+                    methodWrappers.Add(new ClassMethodDef(m, this, false));
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Failed class static method, skipping: " + this + "→" + m);
+                }
             }

             // fix name clashes in method wrappers (caused by overloads from different interfaces)
diff --git a/Generator/Types/InterfaceDef.cs b/Generator/Types/InterfaceDef.cs
index 9d6cc7f..536a33e 100644
--- a/Generator/Types/InterfaceDef.cs
+++ b/Generator/Types/InterfaceDef.cs
@@ -59,8 +59,30 @@ namespace Generator.Types

             InterfaceKind = GetInterfaceKind(Generator, this, exclusiveToType);

-            rawMethodDeclarations = methods.Select(m => m.GetRawDeclaration()).ToList();
-            wrapperMethodDeclarations = methods.Select(m => m.GetWrapperDefinition()).ToList();
+            rawMethodDeclarations = new List<string>();
+            foreach (var m in methods)
+            {
+                try
+                {
+                    rawMethodDeclarations.Add(m.GetRawDeclaration());
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Failed interface raw method declaration, skipping: " + this + "→" + m);
+                }
+            }
+            wrapperMethodDeclarations = new List<string>();
+            foreach (var m in methods)
+            {
+                try
+                {
+                    wrapperMethodDeclarations.Add(m.GetWrapperDefinition());
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Failed interface wrapper method declaration, skipping: " + this + "→" + m);
+                }
+            }
         }

         public override void Emit()

This causes it to seem to run successfully, with the following console output which confirms the problem to be localised to one method (and its variants):

Failed class static method, skipping: Windows.Foundation.GuidHelper→Equals
Failed interface raw method declaration, skipping: Windows.Foundation.IGuidHelperStatics→Equals
Failed interface wrapper method declaration, skipping: Windows.Foundation.IGuidHelperStatics→Equals
Boddlnagg commented 5 years ago

This is interesting! This method has two parameters that are apparently typed as const references. They have this additional modifier System.Runtime.CompilerServices.IsConst, which I've never seen before.

MSDN says: (https://docs.microsoft.com/de-de/dotnet/api/system.runtime.compilerservices.isconst)

The Microsoft C++ compiler emits this modifier into metadata for any parameter, return type, or object declaration declared as const in the source code.

The ECMA Partition II documentation for CLI says:

The CLI itself shall treat required and optional modifiers in the same manner. Two signatures that differ only by the addition of a custom modifier (required or optional) shall not be considered to match. Custom modifiers have no other effect on the operation of the VES. [Rationale: The distinction between required and optional modifiers is important to tools other than the CLI that deal with the metadata, typically compilers and program analysers. A required modifier indicates that there is a special semantics to the modified item that should not be ignored, while an optional modifier can simply be ignored. For example, the const qualifier in the C programming language can be modelled with an optional modifier since the caller of a method that has a const-qualified parameter need not treat it in any special way. On the other hand, a parameter that shall be copy-constructed in C++ shall be marked with a required custom attribute since it is the caller who makes the copy. end rationale]

So this means that in this case I can emit a *const Guid instead of a *mut Guid (or simply ignore it, because there's not really a difference between *const and *mut).

Update:

Actually, there never have been reference input parameters before: *mut Guid previously only existed as an output parameter, but now we have *const Guid as input, which maps to &Guid in the wrapper. Every other method before that took a value type parameter did receive it by value.

Also, ILSpy shows this signature as bool Equals([In] ref Guid target, [In] ref Guid value);.

I have a fix for the generator ready, just need to test if the generated code actually is correct and works.