public void Should_Throw_When_Unable_To_Resolve_Types_From_Simple_Invalid_Markup()
    {
        var xaml        = XDocumentXamlParser.Parse(MiniInvalidXaml);
        var compilation = CreateBasicCompilation(MiniClass);
        var compiler    = MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation));

        Assert.Throws <XamlParseException>(() => compiler.Transform(xaml));
    }
    public void Should_Resolve_Types_From_Simple_Avalonia_Markup()
    {
        var xaml        = XDocumentXamlParser.Parse(AvaloniaXaml);
        var compilation = View.CreateAvaloniaCompilation();

        MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml);

        Assert.NotNull(xaml.Root);
    }
    public void Should_Resolve_Types_From_Simple_Valid_Xaml_Markup()
    {
        var xaml        = XDocumentXamlParser.Parse(MiniValidXaml);
        var compilation = CreateBasicCompilation(MiniClass);

        MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml);

        Assert.NotNull(xaml.Root);
    }
Beispiel #4
0
    public ResolvedView ResolveView(string xaml)
    {
        _resolvedClass = null;
        _xaml          = XDocumentXamlParser.Parse(xaml, new Dictionary <string, string>
        {
            { XamlNamespaces.Blend2008, XamlNamespaces.Blend2008 }
        });

        _compiler.Transform(_xaml);
        _xaml.Root.Visit(this);
        _xaml.Root.VisitChildren(this);
        return(_resolvedClass);
    }
Beispiel #5
0
        XamlDocument Compile(IXamlTypeBuilder <IXamlILEmitter> builder, IXamlType context, string xaml)
        {
            var parsed   = XDocumentXamlParser.Parse(xaml);
            var compiler = new XamlILCompiler(
                Configuration,
                new XamlLanguageEmitMappings <IXamlILEmitter, XamlILNodeEmitResult>(),
                true)
            {
                EnableIlVerification = true
            };

            compiler.Transform(parsed);
            compiler.Compile(parsed, builder, context, "Populate", "Build",
                             "XamlNamespaceInfo",
                             "http://example.com/", null);
            return(parsed);
        }
Beispiel #6
0
        public void Parser_Should_Handle_Ignorable_Content(bool map)
        {
            var root = XDocumentXamlParser.Parse(@"
<Root xmlns='rootns' xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006' 
    mc:Ignorable='d d2' xmlns:d='test' xmlns:d2='test2'
    d:DataContext='123' d2:Lalala='321'>
    <d:DesignWidth>test</d:DesignWidth>
</Root>
 ", map ? new Dictionary <string, string>()
            {
                ["test"] = "mapped"
            } : null);
            var ni       = new NullLineInfo();
            var rootType = new XamlAstXmlTypeReference(ni, "rootns", "Root");

            if (map)
            {
                Helpers.StructDiff(root.Root, new XamlAstObjectNode(ni, rootType)
                {
                    Children =
                    {
                        new XamlAstXmlDirective(ni,     "mapped",                       "DataContext", new[] { new XamlAstTextNode(ni, "123", true), }),
                        new XamlAstTextNode(ni,         "\n    "),
                        new XamlAstObjectNode(ni,       new XamlAstXmlTypeReference(ni, "mapped",      "DesignWidth"))
                        {
                            Children =
                            {
                                new XamlAstTextNode(ni, "test")
                            }
                        },
                        new XamlAstTextNode(ni,         "\n")
                    }
                });
            }
            else
            {
                Helpers.StructDiff(root.Root, new XamlAstObjectNode(ni, rootType)
                {
                    Children =
                    {
                        new XamlAstTextNode(ni, "\n    ")
                    }
                });
            }
        }
Beispiel #7
0
        public static Func <IServiceProvider, object> Compile(string xaml)
        {
            // Enforce everything to load
            foreach (var xt in typeof(BenchCompiler).Assembly.GetTypes())
            {
                xt.GetCustomAttributes();
                xt.GetInterfaces();
                foreach (var p in xt.GetProperties())
                {
                    p.GetCustomAttributes();
                }
            }
            typeof(IXamlParentStackProviderV1).Assembly.GetCustomAttributes();


            var typeSystem    = new SreTypeSystem();
            var configuration = BenchmarksXamlXConfiguration.Configure(typeSystem);
            var parsed        = XDocumentXamlParser.Parse(xaml);

            var compiler = new XamlILCompiler(
                configuration,
                new XamlLanguageEmitMappings <IXamlILEmitter, XamlILNodeEmitResult>(),
                true);

            compiler.Transform(parsed);


            var parsedTsType = ((IXamlAstValueNode)parsed.Root).Type.GetClrType();

#if !NETCOREAPP
            var path = Path.GetDirectoryName(typeof(BenchCompiler).Assembly.GetModules()[0].FullyQualifiedName);
            var da   = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString("N")),
                                                                     AssemblyBuilderAccess.RunAndSave,
                                                                     path);
#else
            var da = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString("N")), AssemblyBuilderAccess.Run);
#endif

            var dm = da.DefineDynamicModule("testasm.dll");
            var t  = dm.DefineType(Guid.NewGuid().ToString("N"), TypeAttributes.Public);

            var ctb            = dm.DefineType(t.Name + "Context", TypeAttributes.Public);
            var contextTypeDef = compiler.CreateContextType(((SreTypeSystem)typeSystem).CreateTypeBuilder(ctb));

            var parserTypeBuilder = ((SreTypeSystem)typeSystem).CreateTypeBuilder(t);
            compiler.Compile(parsed, parserTypeBuilder, contextTypeDef, "Populate", "Build",
                             "XamlNamespaceInfo", "https://github.com/kekekeks/Xaml", null);

            var created = t.CreateType();

#if !NETCOREAPP
            dm.CreateGlobalFunctions();
            // Useful for debugging the actual MSIL, don't remove
            lock (s_asmLock)
                da.Save("testasm.dll");
#endif

            var isp = Expression.Parameter(typeof(IServiceProvider));
            return(Expression.Lambda <Func <IServiceProvider, object> >(
                       Expression.Convert(Expression.Call(
                                              created.GetMethod("Build"), isp), typeof(object)), isp).Compile());
        }
        static bool?CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem,
                                string projectDirectory, bool verifyIl,
                                MessageImportance logImportance
                                , bool debuggerLaunch = false)
        {
            if (debuggerLaunch)
            {
                // According this https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debugger.launch?view=net-6.0#remarks
                // documentation, on not windows platform Debugger.Launch() always return true without running a debugger.
                if (System.Diagnostics.Debugger.Launch())
                {
                    // Set timeout at 1 minut.
                    var time    = new System.Diagnostics.Stopwatch();
                    var timeout = TimeSpan.FromMinutes(1);
                    time.Start();

                    // wait for the debugger to be attacked or timeout.
                    while (!System.Diagnostics.Debugger.IsAttached && time.Elapsed < timeout)
                    {
                        engine.LogMessage($"[PID:{System.Diagnostics.Process.GetCurrentProcess().Id}] Wating attach debugger. Elapsed {time.Elapsed}...", MessageImportance.High);
                        System.Threading.Thread.Sleep(100);
                    }

                    time.Stop();
                    if (time.Elapsed >= timeout)
                    {
                        engine.LogMessage("Wating attach debugger timeout.", MessageImportance.Normal);
                    }
                }
                else
                {
                    engine.LogMessage("Debugging cancelled.", MessageImportance.Normal);
                }
            }
            var asm    = typeSystem.TargetAssemblyDefinition;
            var emres  = new EmbeddedResources(asm);
            var avares = new AvaloniaResources(asm, projectDirectory);

            if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0)
            {
                // Nothing to do
                return(null);
            }

            var clrPropertiesDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlHelpers",
                                                      TypeAttributes.Class, asm.MainModule.TypeSystem.Object);

            asm.MainModule.Types.Add(clrPropertiesDef);
            var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
                                                            TypeAttributes.Class, asm.MainModule.TypeSystem.Object);

            asm.MainModule.Types.Add(indexerAccessorClosure);

            var(xamlLanguage, emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem);
            var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
                                                                         typeSystem.TargetAssembly,
                                                                         xamlLanguage,
                                                                         XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage),
                                                                         AvaloniaXamlIlLanguage.CustomValueConverter,
                                                                         new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
                                                                         new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)),
                                                                         new DeterministicIdGenerator());


            var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext",
                                                TypeAttributes.Class, asm.MainModule.TypeSystem.Object);

            asm.MainModule.Types.Add(contextDef);

            var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
                                                                            xamlLanguage, emitConfig);

            var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass)
            {
                EnableIlVerification = verifyIl
            };

            var editorBrowsableAttribute = typeSystem
                                           .GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute"))
                                           .Resolve();
            var editorBrowsableCtor =
                asm.MainModule.ImportReference(editorBrowsableAttribute.GetConstructors()
                                               .First(c => c.Parameters.Count == 1));

            var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
            var createRootServiceProviderMethod = asm.MainModule.ImportReference(
                typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods
                .First(x => x.Name == "CreateRootServiceProviderV2"));

            var loaderDispatcherDef = new TypeDefinition("CompiledAvaloniaXaml", "!XamlLoader",
                                                         TypeAttributes.Class, asm.MainModule.TypeSystem.Object);


            loaderDispatcherDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
            {
                ConstructorArguments = { new CustomAttributeArgument(editorBrowsableCtor.Parameters[0].ParameterType, 1) }
            });


            var loaderDispatcherMethod = new MethodDefinition("TryLoad",
                                                              MethodAttributes.Static | MethodAttributes.Public,
                                                              asm.MainModule.TypeSystem.Object)
            {
                Parameters = { new ParameterDefinition(asm.MainModule.TypeSystem.String) }
            };

            loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
            asm.MainModule.Types.Add(loaderDispatcherDef);

            var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
                                                                  m =>
                                                                  m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
                                                                  m.ReturnType.FullName == "System.Boolean" &&
                                                                  m.Parameters[0].ParameterType.FullName == "System.String" &&
                                                                  m.Parameters[1].ParameterType.FullName == "System.String"));

            bool CompileGroup(IResourceGroup group)
            {
                var typeDef = new TypeDefinition("CompiledAvaloniaXaml", "!" + group.Name,
                                                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);

                typeDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
                {
                    ConstructorArguments = { new CustomAttributeArgument(editorBrowsableCtor.Parameters[0].ParameterType, 1) }
                });
                asm.MainModule.Types.Add(typeDef);
                var builder = typeSystem.CreateTypeBuilder(typeDef);

                foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x => x.FilePath.ToLowerInvariant()))
                {
                    try
                    {
                        engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance);

                        // StreamReader is needed here to handle BOM
                        var xaml   = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
                        var parsed = XDocumentXamlParser.Parse(xaml);

                        var initialRoot = (XamlAstObjectNode)parsed.Root;


                        var precompileDirective = initialRoot.Children.OfType <XamlAstXmlDirective>()
                                                  .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Precompile");
                        if (precompileDirective != null)
                        {
                            var precompileText = (precompileDirective.Values[0] as XamlAstTextNode)?.Text.Trim()
                                                 .ToLowerInvariant();
                            if (precompileText == "false")
                            {
                                continue;
                            }
                            if (precompileText != "true")
                            {
                                throw new XamlParseException("Invalid value for x:Precompile", precompileDirective);
                            }
                        }

                        var classDirective = initialRoot.Children.OfType <XamlAstXmlDirective>()
                                             .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
                        IXamlType classType = null;
                        if (classDirective != null)
                        {
                            if (classDirective.Values.Count != 1 || !(classDirective.Values[0] is XamlAstTextNode tn))
                            {
                                throw new XamlParseException("x:Class should have a string value", classDirective);
                            }
                            classType = typeSystem.TargetAssembly.FindType(tn.Text);
                            if (classType == null)
                            {
                                throw new XamlParseException($"Unable to find type `{tn.Text}`", classDirective);
                            }
                            compiler.OverrideRootType(parsed,
                                                      new XamlAstClrTypeReference(classDirective, classType, false));
                            initialRoot.Children.Remove(classDirective);
                        }


                        compiler.Transform(parsed);
                        var populateName = classType == null ? "Populate:" + res.Name : "!XamlIlPopulate";
                        var buildName    = classType == null ? "Build:" + res.Name : null;

                        var classTypeDefinition =
                            classType == null ? null : typeSystem.GetTypeReference(classType).Resolve();


                        var populateBuilder = classTypeDefinition == null ?
                                              builder :
                                              typeSystem.CreateTypeBuilder(classTypeDefinition);
                        compiler.Compile(parsed, contextClass,
                                         compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
                                                                       classTypeDefinition == null),
                                         buildName == null ? null : compiler.DefineBuildMethod(builder, parsed, buildName, true),
                                         builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name,
                                                               true),
                                         (closureName, closureBaseType) =>
                                         populateBuilder.DefineSubType(closureBaseType, closureName, false),
                                         res.Uri, res
                                         );


                        if (classTypeDefinition != null)
                        {
                            var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve()
                                                         .Methods.First(m => m.Name == populateName);

                            var designLoaderFieldType = typeSystem
                                                        .GetType("System.Action`1")
                                                        .MakeGenericType(typeSystem.GetType("System.Object"));

                            var designLoaderFieldTypeReference = (GenericInstanceType)typeSystem.GetTypeReference(designLoaderFieldType);
                            designLoaderFieldTypeReference.GenericArguments[0] =
                                asm.MainModule.ImportReference(designLoaderFieldTypeReference.GenericArguments[0]);
                            designLoaderFieldTypeReference = (GenericInstanceType)
                                                             asm.MainModule.ImportReference(designLoaderFieldTypeReference);

                            var designLoaderLoad =
                                typeSystem.GetMethodReference(
                                    designLoaderFieldType.Methods.First(m => m.Name == "Invoke"));
                            designLoaderLoad =
                                asm.MainModule.ImportReference(designLoaderLoad);
                            designLoaderLoad.DeclaringType = designLoaderFieldTypeReference;

                            var designLoaderField = new FieldDefinition("!XamlIlPopulateOverride",
                                                                        FieldAttributes.Static | FieldAttributes.Private, designLoaderFieldTypeReference);
                            classTypeDefinition.Fields.Add(designLoaderField);

                            const string TrampolineName = "!XamlIlPopulateTrampoline";
                            var          trampoline     = new MethodDefinition(TrampolineName,
                                                                               MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
                            trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
                            classTypeDefinition.Methods.Add(trampoline);

                            var regularStart = Instruction.Create(OpCodes.Call, createRootServiceProviderMethod);

                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));

                            trampoline.Body.Instructions.Add(regularStart);
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                            CopyDebugDocument(trampoline, compiledPopulateMethod);

                            var foundXamlLoader = false;
                            // Find AvaloniaXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
                            foreach (var method in classTypeDefinition.Methods
                                     .Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
                            {
                                var i = method.Body.Instructions;
                                for (var c = 1; c < i.Count; c++)
                                {
                                    if (i[c].OpCode == OpCodes.Call)
                                    {
                                        var op = i[c].Operand as MethodReference;

                                        // TODO: Throw an error
                                        // This usually happens when same XAML resource was added twice for some weird reason
                                        // We currently support it for dual-named default theme resource
                                        if (op != null &&
                                            op.Name == TrampolineName)
                                        {
                                            foundXamlLoader = true;
                                            break;
                                        }
                                        if (op != null &&
                                            op.Name == "Load" &&
                                            op.Parameters.Count == 1 &&
                                            op.Parameters[0].ParameterType.FullName == "System.Object" &&
                                            op.DeclaringType.FullName == "Avalonia.Markup.Xaml.AvaloniaXamlLoader")
                                        {
                                            if (MatchThisCall(i, c - 1))
                                            {
                                                i[c].Operand    = trampoline;
                                                foundXamlLoader = true;
                                            }
                                        }
                                    }
                                }
                            }

                            if (!foundXamlLoader)
                            {
                                var ctors = classTypeDefinition.GetConstructors()
                                            .Where(c => !c.IsStatic).ToList();
                                // We can inject xaml loader into default constructor
                                if (ctors.Count == 1 && ctors[0].Body.Instructions.Count(o => o.OpCode != OpCodes.Nop) == 3)
                                {
                                    var i      = ctors[0].Body.Instructions;
                                    var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
                                    i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
                                    i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
                                }
                                else
                                {
                                    throw new InvalidProgramException(
                                              $"No call to AvaloniaXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
                                }
                            }
                        }

                        if (buildName != null || classTypeDefinition != null)
                        {
                            var compiledBuildMethod = buildName == null ?
                                                      null :
                                                      typeSystem.GetTypeReference(builder).Resolve()
                                                      .Methods.First(m => m.Name == buildName);
                            var parameterlessConstructor = compiledBuildMethod != null ?
                                                           null :
                                                           classTypeDefinition.GetConstructors().FirstOrDefault(c =>
                                                                                                                c.IsPublic && !c.IsStatic && !c.HasParameters);

                            if (compiledBuildMethod != null || parameterlessConstructor != null)
                            {
                                var i   = loaderDispatcherMethod.Body.Instructions;
                                var nop = Instruction.Create(OpCodes.Nop);
                                i.Add(Instruction.Create(OpCodes.Ldarg_0));
                                i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
                                i.Add(Instruction.Create(OpCodes.Call, stringEquals));
                                i.Add(Instruction.Create(OpCodes.Brfalse, nop));
                                if (parameterlessConstructor != null)
                                {
                                    i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor));
                                }
                                else
                                {
                                    i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod));
                                    i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
                                }

                                i.Add(Instruction.Create(OpCodes.Ret));
                                i.Add(nop);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        int lineNumber = 0, linePosition = 0;
                        if (e is XamlParseException xe)
                        {
                            lineNumber   = xe.LineNumber;
                            linePosition = xe.LinePosition;
                        }
                        engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", res.FilePath,
                                                                     lineNumber, linePosition, lineNumber, linePosition,
                                                                     e.Message, "", "Avalonia"));
                        return(false);
                    }
                    res.Remove();
                }


                // Technically that's a hack, but it fixes corert incompatibility caused by deterministic builds
                int dupeCounter = 1;

                foreach (var grp in typeDef.NestedTypes.GroupBy(x => x.Name))
                {
                    if (grp.Count() > 1)
                    {
                        foreach (var dupe in grp)
                        {
                            dupe.Name += "_dup" + dupeCounter++;
                        }
                    }
                }


                return(true);
            }

            if (emres.Resources.Count(CheckXamlName) != 0)
            {
                if (!CompileGroup(emres))
                {
                    return(false);
                }
            }
            if (avares.Resources.Count(CheckXamlName) != 0)
            {
                if (!CompileGroup(avares))
                {
                    return(false);
                }
                avares.Save();
            }

            loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
            loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
            return(true);
        }
Beispiel #9
0
        static bool?CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem)
        {
            var asm    = typeSystem.TargetAssemblyDefinition;
            var embrsc = new EmbeddedResources(asm);

            if (embrsc.Resources.Count(CheckXamlName) == 0)
            {
                // Nothing to do
                return(null);
            }

            var xamlLanguage = new XamlLanguageTypeMappings(typeSystem)
            {
                XmlnsAttributes =
                {
                    typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"),
                },
                ContentAttributes =
                {
                    typeSystem.GetType("Robust.Client.UserInterface.XAML.ContentAttribute")
                },
                UsableDuringInitializationAttributes =
                {
                    typeSystem.GetType("Robust.Client.UserInterface.XAML.UsableDuringInitializationAttribute")
                },
                DeferredContentPropertyAttributes =
                {
                    typeSystem.GetType("Robust.Client.UserInterface.XAML.DeferredContentAttribute")
                },
                RootObjectProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestRootObjectProvider"),
                UriContextProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestUriContext"),
                ProvideValueTarget = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestProvideValueTarget"),
            };
            var emitConfig = new XamlLanguageEmitMappings <IXamlILEmitter, XamlILNodeEmitResult>
            {
                ContextTypeBuilderCallback = (b, c) => EmitNameScopeField(xamlLanguage, typeSystem, b, c)
            };

            var transformerconfig = new TransformerConfiguration(
                typeSystem,
                typeSystem.TargetAssembly,
                xamlLanguage,
                XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage), CustomValueConverter);

            var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext",
                                                TypeAttributes.Class, asm.MainModule.TypeSystem.Object);

            asm.MainModule.Types.Add(contextDef);
            var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
                                                                            xamlLanguage, emitConfig);

            var compiler =
                new RobustXamlILCompiler(transformerconfig, emitConfig, true);

            var loaderDispatcherDef = new TypeDefinition("CompiledRobustXaml", "!XamlLoader",
                                                         TypeAttributes.Class, asm.MainModule.TypeSystem.Object);

            var loaderDispatcherMethod = new MethodDefinition("TryLoad",
                                                              MethodAttributes.Static | MethodAttributes.Public,
                                                              asm.MainModule.TypeSystem.Object)
            {
                Parameters = { new ParameterDefinition(asm.MainModule.TypeSystem.String) }
            };

            loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
            asm.MainModule.Types.Add(loaderDispatcherDef);

            var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
                                                                  m =>
                                                                  m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
                                                                  m.ReturnType.FullName == "System.Boolean" &&
                                                                  m.Parameters[0].ParameterType.FullName == "System.String" &&
                                                                  m.Parameters[1].ParameterType.FullName == "System.String"));

            bool CompileGroup(IResourceGroup group)
            {
                var typeDef = new TypeDefinition("CompiledRobustXaml", "!" + group.Name, TypeAttributes.Class,
                                                 asm.MainModule.TypeSystem.Object);

                //typeDef.CustomAttributes.Add(new CustomAttribute(ed));
                asm.MainModule.Types.Add(typeDef);
                var builder = typeSystem.CreateTypeBuilder(typeDef);

                foreach (var res in group.Resources.Where(CheckXamlName))
                {
                    try
                    {
                        engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", MessageImportance.Low);

                        var xaml   = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
                        var parsed = XDocumentXamlParser.Parse(xaml);

                        var initialRoot = (XamlAstObjectNode)parsed.Root;

                        var classDirective = initialRoot.Children.OfType <XamlAstXmlDirective>()
                                             .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
                        string classname;
                        if (classDirective != null && classDirective.Values[0] is XamlAstTextNode tn)
                        {
                            classname = tn.Text;
                        }
                        else
                        {
                            classname = res.Name.Replace(".xaml", "");
                        }

                        var classType = typeSystem.TargetAssembly.FindType(classname);
                        if (classType == null)
                        {
                            throw new Exception($"Unable to find type '{classname}'");
                        }

                        compiler.Transform(parsed);

                        var populateName = $"Populate:{res.Name}";
                        var buildName    = $"Build:{res.Name}";

                        var classTypeDefinition = typeSystem.GetTypeReference(classType).Resolve();

                        var populateBuilder = typeSystem.CreateTypeBuilder(classTypeDefinition);

                        compiler.Compile(parsed, contextClass,
                                         compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
                                                                       classTypeDefinition == null),
                                         compiler.DefineBuildMethod(builder, parsed, buildName, true),
                                         null,
                                         (closureName, closureBaseType) =>
                                         populateBuilder.DefineSubType(closureBaseType, closureName, false),
                                         res.Uri, res
                                         );

                        //add compiled populate method
                        var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve().Methods
                                                     .First(m => m.Name == populateName);

                        const string TrampolineName = "!XamlIlPopulateTrampoline";
                        var          trampoline     = new MethodDefinition(TrampolineName,
                                                                           MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
                        trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
                        classTypeDefinition.Methods.Add(trampoline);

                        trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
                        trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                        trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
                        trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));

                        var foundXamlLoader = false;
                        // Find RobustXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
                        foreach (var method in classTypeDefinition.Methods
                                 .Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
                        {
                            var i = method.Body.Instructions;
                            for (var c = 1; c < i.Count; c++)
                            {
                                if (i[c].OpCode == OpCodes.Call)
                                {
                                    var op = i[c].Operand as MethodReference;

                                    if (op != null &&
                                        op.Name == TrampolineName)
                                    {
                                        foundXamlLoader = true;
                                        break;
                                    }

                                    if (op != null &&
                                        op.Name == "Load" &&
                                        op.Parameters.Count == 1 &&
                                        op.Parameters[0].ParameterType.FullName == "System.Object" &&
                                        op.DeclaringType.FullName == "Robust.Client.UserInterface.XAML.RobustXamlLoader")
                                    {
                                        if (MatchThisCall(i, c - 1))
                                        {
                                            i[c].Operand    = trampoline;
                                            foundXamlLoader = true;
                                        }
                                    }
                                }
                            }
                        }

                        if (!foundXamlLoader)
                        {
                            var ctors = classTypeDefinition.GetConstructors()
                                        .Where(c => !c.IsStatic).ToList();
                            // We can inject xaml loader into default constructor
                            if (ctors.Count == 1 && ctors[0].Body.Instructions.Count(o => o.OpCode != OpCodes.Nop) == 3)
                            {
                                var i      = ctors[0].Body.Instructions;
                                var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
                                i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
                                i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
                            }
                            else
                            {
                                throw new InvalidProgramException(
                                          $"No call to RobustXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
                            }
                        }

                        //add compiled build method
                        var compiledBuildMethod = typeSystem.GetTypeReference(builder).Resolve().Methods
                                                  .First(m => m.Name == buildName);
                        var parameterlessCtor = classTypeDefinition.GetConstructors()
                                                .FirstOrDefault(c => c.IsPublic && !c.IsStatic && !c.HasParameters);

                        if (compiledBuildMethod != null && parameterlessCtor != null)
                        {
                            var i   = loaderDispatcherMethod.Body.Instructions;
                            var nop = Instruction.Create(OpCodes.Nop);
                            i.Add(Instruction.Create(OpCodes.Ldarg_0));
                            i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
                            i.Add(Instruction.Create(OpCodes.Call, stringEquals));
                            i.Add(Instruction.Create(OpCodes.Brfalse, nop));
                            if (parameterlessCtor != null)
                            {
                                i.Add(Instruction.Create(OpCodes.Newobj, parameterlessCtor));
                            }
                            else
                            {
                                i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
                            }

                            i.Add(Instruction.Create(OpCodes.Ret));
                            i.Add(nop);
                        }
                    }
                    catch (Exception e)
                    {
                        engine.LogErrorEvent(new BuildErrorEventArgs("XAMLIL", "", res.FilePath, 0, 0, 0, 0,
                                                                     $"{res.FilePath}: {e.Message}", "", "CompileRobustXaml"));
                    }
                }
                return(true);
            }

            if (embrsc.Resources.Count(CheckXamlName) != 0)
            {
                if (!CompileGroup(embrsc))
                {
                    return(false);
                }
            }

            loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
            loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
            return(true);
        }
Beispiel #10
0
        public void Parser_Should_Be_Able_To_Parse_A_Simple_Tree()
        {
            var root = XDocumentXamlParser.Parse(
                @"
<Root xmlns='rootns' xmlns:t='testns' xmlns:d='directive' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
    <Child Ext='{Extension 123, 321, Prop=test, Prop2=test2, Prop3={Extension}, Prop4=test3}'
        Other.Prop='{}Not extension'
        Prop1='123' 
        Root.AttachedProp='AttachedValue'
        t:Namespaced.AttachedProp='AttachedValue'
        d:Directive='DirectiveValue'
        d:DirectiveExt='{Extension 123}'>
        <t:SubChild Prop='321' Root.AttachedProp='AttachedValue'/>
        <Child.DottedProp>DottedValue</Child.DottedProp>
        <Root.AttachedDottedProp>AttachedValue</Root.AttachedDottedProp>
        <Child.NodeListProp>
            <SubChild/>
            <SubChild/>
        </Child.NodeListProp>
    </Child>
    <GenericType x:TypeArguments='Child,t:NamespacedGeneric(Child,GenericType ( Child, t:Namespaced) )'/>

</Root>");
            var ni             = new NullLineInfo();
            var rootType       = new XamlAstXmlTypeReference(ni, "rootns", "Root");
            var childType      = new XamlAstXmlTypeReference(ni, "rootns", "Child");
            var subChildType   = new XamlAstXmlTypeReference(ni, "rootns", "SubChild");
            var nsSubChildType = new XamlAstXmlTypeReference(ni, "testns", "SubChild");
            var namespacedType = new XamlAstXmlTypeReference(ni, "testns", "Namespaced");
            var extensionType  = new XamlAstXmlTypeReference(ni, "rootns", "Extension")
            {
                IsMarkupExtension = true
            };

            var other = new XamlDocument
            {
                NamespaceAliases = new Dictionary <string, string>
                {
                    [""]  = "rootns",
                    ["t"] = "testns",
                    ["d"] = "directive",
                    ["x"] = "http://schemas.microsoft.com/winfx/2006/xaml"
                },
                Root = new XamlAstObjectNode(ni, rootType)
                {
                    Children =
                    {
                        // <Child
                        new XamlAstObjectNode(ni,                                                                                                                             childType)
                        {
                            Children =
                            {
                                // Ext='{Extension 123, 321, Prop=test, Prop2=test2}'
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             childType,                                                                                                                                            "Ext",                                                                                                                                       childType),
                                                                 new XamlAstObjectNode(ni,                                                                                                                                                                  extensionType)
                                {
                                    Arguments =
                                    {
                                        new XamlAstTextNode(ni, "123"),
                                        new XamlAstTextNode(ni, "321"),
                                    },
                                    Children =
                                    {
                                        new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                                                                                                                                                                             new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                                                                                                                                                                                          extensionType,                                                                                                                                                                                                                                                                                             "Prop",                                                                                                                                      extensionType),
                                                                         new XamlAstTextNode(ni,                                                                                                                                                                                                                                                                                                                         "test")),
                                        new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                                                                                                                                                                             new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                                                                                                                                                                                          extensionType,                                                                                                                                                                                                                                                                                             "Prop2",                                                                                                                                     extensionType),
                                                                         new XamlAstTextNode(ni,                                                                                                                                                                                                                                                                                                                         "test2")),
                                        new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                                                                                                                                                                             new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                                                                                                                                                                                          extensionType,                                                                                                                                                                                                                                                                                             "Prop3",                                                                                                                                     extensionType),
                                                                         new XamlAstObjectNode(ni,                                                                                                                                                                                                                                                                                                                       extensionType)),
                                        new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                                                                                                                                                                             new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                                                                                                                                                                                          extensionType,                                                                                                                                                                                                                                                                                             "Prop4",                                                                                                                                     extensionType),
                                                                         new XamlAstTextNode(ni,                                                                                                                                                                                                                                                                                                                         "test3")),
                                    }
                                }),
                                //Other.Prop='{}Not extension'
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             new XamlAstXmlTypeReference(ni,                                                                                                                       "rootns",                                                                                                                                    "Other"),                               "Prop", childType),
                                                                 new XamlAstTextNode(ni,                                                                                                                                                                    "Not extension")),
                                //  Prop1='123'
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             childType,                                                                                                                                            "Prop1",                                                                                                                                     childType),
                                                                 new XamlAstTextNode(ni,                                                                                                                                                                    "123")),
                                // Root.AttachedProp='AttachedValue'
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             rootType,                                                                                                                                             "AttachedProp",                                                                                                                              childType),
                                                                 new XamlAstTextNode(ni,                                                                                                                                                                    "AttachedValue")),
                                //t:Namespaced.AttachedProp='AttachedValue'
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             namespacedType,                                                                                                                                       "AttachedProp",                                                                                                                              childType),
                                                                 new XamlAstTextNode(ni,                                                                                                                                                                    "AttachedValue")),
                                //d:Directive='DirectiveValue'>
                                new XamlAstXmlDirective(ni,                                                                                                                                                                                                 "directive",                                                                                                                                 "Directive",                            new[]
                                {
                                    new XamlAstTextNode(ni,                                                                                                                                                                                                 "DirectiveValue")
                                }),
                                //d:DirectiveExt='{Extension 123}'>
                                new XamlAstXmlDirective(ni,                                                                                                                                                                                                 "directive",                                                                                                                                 "DirectiveExt",                         new[]
                                {
                                    new XamlAstObjectNode(ni,                                                                                                                                                                                               extensionType)
                                    {
                                        Arguments =
                                        {
                                            new XamlAstTextNode(ni, "123"),
                                        }
                                    }
                                }),
                                // <t:SubChild Prop='321' Root.AttachedProp='AttachedValue'/>
                                new XamlAstObjectNode(ni,                                                                                                                                                                                                   nsSubChildType)
                                {
                                    Children =
                                    {
                                        new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                                                                                                                                                                              new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                                                                                                                                                                                           nsSubChildType,                                                                                                                                                                                                                                                                                             "Prop",                                                                                                                                      nsSubChildType),
                                                                         new XamlAstTextNode(ni,                                                                                                                                                                                                                                                                                                                          "321")),
                                        // Root.AttachedProp='AttachedValue'
                                        new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                                                                                                                                                                              new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                                                                                                                                                                                           rootType,                                                                                                                                                                                                                                                                                                   "AttachedProp",                                                                                                                              nsSubChildType),
                                                                         new XamlAstTextNode(ni,                                                                                                                                                                                                                                                                                                                          "AttachedValue")),
                                    }
                                },
                                //<Child.DottedProp>DottedValue</Child.DottedProp>
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             childType,                                                                                                                                            "DottedProp",                                                                                                                                childType),
                                                                 new[]
                                {
                                    new XamlAstTextNode(ni,                                                                                                                                                                                                 "DottedValue")
                                }),
                                // <Root.AttachedDottedProp>AttachedValue</Root.AttachedDottedProp>
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             rootType,                                                                                                                                             "AttachedDottedProp",                                                                                                                        childType),
                                                                 new[]
                                {
                                    new XamlAstTextNode(ni,                                                                                                                                                                                                 "AttachedValue")
                                }),
                                //<Child.NodeListProp>
                                new XamlAstXamlPropertyValueNode(ni,                                                                                                                                                                                        new XamlAstNamePropertyReference(ni,
                                                                                                                                                                                                                                                                                             childType,                                                                                                                                            "NodeListProp",                                                                                                                              childType),
                                                                 new[]
                                {
                                    // <SubChild/>
                                    new XamlAstObjectNode(ni,                                                                                                                                                                                               subChildType),
                                    // <SubChild/>
                                    new XamlAstObjectNode(ni,                                                                                                                                                                                               subChildType),
                                })
                            }
                        },
                        //<GenericType x:TypeArguments='Child,t:NamespacedGeneric(Child,GenericType(Child, t:Namespaced))'/>
                        new XamlAstObjectNode(ni,                                                                                                                             new XamlAstXmlTypeReference(ni,     "rootns",             "GenericType",
                                                                                                                                                                                                          new[]
                        {
                            childType,
                            new XamlAstXmlTypeReference(ni,                                                                                                                   "testns",                           "NamespacedGeneric",  new[]
                            {
                                childType,
                                new XamlAstXmlTypeReference(ni,                                                                                                               "rootns",                           "GenericType",        new[]
                                {
                                    childType,
                                    namespacedType
                                }),
                            }),
                        }))
                    }
                }
            };

            Helpers.StructDiff(root, other);
        }