diff --git a/.gitmodules b/.gitmodules index d83f9c8b7..0534efb84 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/Space-Wizards-Federation/SpaceWizards.Lidgren.Network [submodule "XamlX"] path = XamlX - url = https://github.com/Space-Wizards-Federation/XamlX + url = https://github.com/Space-Wizards-Federation/XamlX.git [submodule "Robust.LoaderApi"] path = Robust.LoaderApi url = https://github.com/Space-Wizards-Federation/Robust.LoaderApi.git diff --git a/Directory.Packages.props b/Directory.Packages.props index 69000ce26..9ac450737 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,6 +16,7 @@ + @@ -39,6 +40,7 @@ + @@ -79,6 +81,7 @@ + diff --git a/MSBuild/XamlIL.targets b/MSBuild/XamlIL.targets index c489b3e4b..9f0969ed1 100644 --- a/MSBuild/XamlIL.targets +++ b/MSBuild/XamlIL.targets @@ -29,7 +29,7 @@ Release $(RobustInjectorsConfiguration)_$(RuntimeIdentifier) $(RobustInjectorsConfiguration.ToLower()) - $(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\netstandard2.0\Robust.Client.Injectors.dll + $(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\net10.0\Robust.Client.Injectors.dll $(MSBuildThisFileDirectory)\..\..\artifacts\bin\Robust.Client.Injectors\$(RobustInjectorsConfiguration)\Robust.Client.Injectors.dll diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a2c17daad..d9bc21fd0 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -35,7 +35,18 @@ END TEMPLATE--> ### Breaking changes -*None yet* +- XamlX has been upgraded, and has a new style class syntax. + The syntax for multiple style classes has changed from: + ```xaml + + Hello + World + + ``` + to + ```xaml + + ``` ### New features diff --git a/Robust.Client.Injectors/Robust.Client.Injectors.csproj b/Robust.Client.Injectors/Robust.Client.Injectors.csproj index 9a4a33ce9..ce195f601 100644 --- a/Robust.Client.Injectors/Robust.Client.Injectors.csproj +++ b/Robust.Client.Injectors/Robust.Client.Injectors.csproj @@ -1,18 +1,8 @@ - - netstandard2.0 + net10.0 true 8.0 enable diff --git a/Robust.Client.NameGenerator/InvalidXamlRootTypeException.cs b/Robust.Client.NameGenerator/InvalidXamlRootTypeException.cs index e3daf4b0b..2e39641b9 100644 --- a/Robust.Client.NameGenerator/InvalidXamlRootTypeException.cs +++ b/Robust.Client.NameGenerator/InvalidXamlRootTypeException.cs @@ -6,10 +6,10 @@ namespace Robust.Client.NameGenerator public class InvalidXamlRootTypeException : Exception { public readonly INamedTypeSymbol ExpectedType; - public readonly INamedTypeSymbol ExpectedBaseType; + public readonly INamedTypeSymbol? ExpectedBaseType; public readonly INamedTypeSymbol Actual; - public InvalidXamlRootTypeException(INamedTypeSymbol actual, INamedTypeSymbol expectedType, INamedTypeSymbol expectedBaseType) + public InvalidXamlRootTypeException(INamedTypeSymbol actual, INamedTypeSymbol expectedType, INamedTypeSymbol? expectedBaseType) { Actual = actual; ExpectedType = expectedType; diff --git a/Robust.Client.NameGenerator/Robust.Client.NameGenerator.csproj b/Robust.Client.NameGenerator/Robust.Client.NameGenerator.csproj index f7fa741d8..5dea68f11 100644 --- a/Robust.Client.NameGenerator/Robust.Client.NameGenerator.csproj +++ b/Robust.Client.NameGenerator/Robust.Client.NameGenerator.csproj @@ -5,13 +5,15 @@ + + - - disable + enable + nullable diff --git a/Robust.Client.NameGenerator/RoslynTypeSystem.cs b/Robust.Client.NameGenerator/RoslynTypeSystem.cs index 1d6af521b..6f733bf76 100644 --- a/Robust.Client.NameGenerator/RoslynTypeSystem.cs +++ b/Robust.Client.NameGenerator/RoslynTypeSystem.cs @@ -1,13 +1,11 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using XamlX.TypeSystem; namespace Robust.Client.NameGenerator { /// - /// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/Infrastructure/RoslynTypeSystem.cs + /// Taken from https://github.com/AvaloniaUI/Avalonia/blob/9d28762f1439e58e9764d104223b73eef93b26b5/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs /// public class RoslynTypeSystem : IXamlTypeSystem { @@ -27,11 +25,11 @@ public RoslynTypeSystem(CSharpCompilation compilation) _assemblies.AddRange(assemblySymbols); } - public IReadOnlyList Assemblies => _assemblies; + public IEnumerable Assemblies => _assemblies; public IXamlAssembly FindAssembly(string substring) => _assemblies[0]; - public IXamlType FindType(string name) + public IXamlType? FindType(string name) { foreach (var assembly in _assemblies) { @@ -43,7 +41,7 @@ public IXamlType FindType(string name) return null; } - public IXamlType FindType(string name, string assembly) + public IXamlType? FindType(string name, string assembly) { foreach (var assemblyInstance in _assemblies) { @@ -73,7 +71,7 @@ other is RoslynAssembly roslynAssembly && .Select(data => new RoslynAttribute(data, this)) .ToList(); - public IXamlType FindType(string fullName) + public IXamlType? FindType(string fullName) { var type = _symbol.GetTypeByMetadataName(fullName); return type is null ? null : new RoslynType(type, this); @@ -95,14 +93,14 @@ public bool Equals(IXamlCustomAttribute other) => other is RoslynAttribute attribute && _data == attribute._data; - public IXamlType Type => new RoslynType(_data.AttributeClass, _assembly); + public IXamlType Type => new RoslynType(_data.AttributeClass!, _assembly); - public List Parameters => + public List Parameters => _data.ConstructorArguments .Select(argument => argument.Value) .ToList(); - public Dictionary Properties => + public Dictionary Properties => _data.NamedArguments.ToDictionary( pair => pair.Key, pair => pair.Value.Value); @@ -169,16 +167,25 @@ other is RoslynType roslynType && public bool IsArray => false; - public IXamlType ArrayElementType { get; } = null; + public IXamlType? ArrayElementType { get; } = null; - public IXamlType MakeArrayType(int dimensions) => null; + public IXamlType MakeArrayType(int dimensions) => throw new NotImplementedException(); - public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly); + public IXamlType? BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly); public bool IsValueType { get; } = false; public bool IsEnum { get; } = false; + public bool IsPublic => _symbol.DeclaredAccessibility == Accessibility.Public; + + public bool IsNestedPrivate => _symbol.DeclaredAccessibility == Accessibility.Private; + + public bool IsFunctionPointer => false; + + public IXamlType? DeclaringType => + _symbol.ContainingType is { } containingType ? new RoslynType(containingType, _assembly) : null; + public IReadOnlyList Interfaces => _symbol.AllInterfaces .Select(abstraction => new RoslynType(abstraction, _assembly)) @@ -186,7 +193,7 @@ other is RoslynType roslynType && public bool IsInterface => _symbol.IsAbstract; - public IXamlType GetEnumUnderlyingType() => null; + public IXamlType GetEnumUnderlyingType() => throw new NotImplementedException(); public IReadOnlyList GenericParameters { get; } = new List(); } @@ -216,6 +223,12 @@ other is RoslynConstructor roslynConstructor && .OfType() .Select(type => new RoslynType(type, _assembly)) .ToList(); + + public string Name => _symbol.Name; + + public IXamlType DeclaringType => new RoslynType(_symbol.ContainingType, _assembly); + + public IXamlParameterInfo GetParameterInfo(int index) => new RoslynParameter(_assembly, _symbol.Parameters[index]); } public class RoslynProperty : IXamlProperty @@ -238,17 +251,37 @@ other is RoslynProperty roslynProperty && public IXamlType PropertyType => _symbol.Type is INamedTypeSymbol namedTypeSymbol ? new RoslynType(namedTypeSymbol, _assembly) - : null; + : throw new InvalidOperationException($"Roslyn property type {_symbol.Type} is not supported."); - public IXamlMethod Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly); + public IXamlType DeclaringType => new RoslynType(_symbol.ContainingType, _assembly); - public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly); + public IXamlMethod? Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly); + + public IXamlMethod? Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly); public IReadOnlyList CustomAttributes { get; } = new List(); public IReadOnlyList IndexerParameters { get; } = new List(); } + public class RoslynParameter : IXamlParameterInfo + { + private readonly RoslynAssembly _assembly; + private readonly IParameterSymbol _symbol; + + public RoslynParameter(RoslynAssembly assembly, IParameterSymbol symbol) + { + _assembly = assembly; + _symbol = symbol; + } + + public string Name => _symbol.Name; + + public IXamlType ParameterType => new RoslynType((INamedTypeSymbol)_symbol.Type, _assembly); + + public IReadOnlyList CustomAttributes => Array.Empty(); + } + public class RoslynMethod : IXamlMethod { private readonly IMethodSymbol _symbol; @@ -268,6 +301,16 @@ other is RoslynMethod roslynMethod && public bool IsPublic => true; + public bool IsPrivate => _symbol.DeclaredAccessibility == Accessibility.Private; + + public bool IsFamily => _symbol.DeclaredAccessibility == Accessibility.Protected; + + public bool ContainsGenericParameters => _symbol.TypeParameters.Any(); + + public bool IsGenericMethod => _symbol.IsGenericMethod; + + public bool IsGenericMethodDefinition => _symbol.IsDefinition && _symbol.IsGenericMethod; + public bool IsStatic => false; public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly); @@ -278,10 +321,18 @@ other is RoslynMethod roslynMethod && .Select(type => new RoslynType(type, _assembly)) .ToList(); - public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly); + public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType! as INamedTypeSymbol, _assembly); - public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) => null; + public IReadOnlyList GenericParameters => throw new NotImplementedException(); + + public IReadOnlyList GenericArguments => _symbol.TypeArguments + .Select(ga => new RoslynType((INamedTypeSymbol)ga, _assembly)) + .ToArray(); + + public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) => throw new NotImplementedException(); public IReadOnlyList CustomAttributes { get; } = new List(); + + public IXamlParameterInfo GetParameterInfo(int index) => new RoslynParameter(_assembly, _symbol.Parameters[index]); } } diff --git a/Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs b/Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs index 828fd5438..851a02ed4 100644 --- a/Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs +++ b/Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs @@ -7,6 +7,8 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Robust.Client.UserInterface; +using Robust.Xaml; +using XamlX; using XamlX.Ast; using XamlX.Emit; using XamlX.IL; @@ -53,9 +55,8 @@ class NameVisitor : IXamlAstVisitor return visitor._names; } - private bool IsControl(IXamlType type) => type.FullName != "System.Object" && - (type.FullName == "Robust.Client.UserInterface.Control" || - IsControl(type.BaseType)); + private bool IsControl(IXamlType type) => type.FullName == "Robust.Client.UserInterface.Control" || + (type.BaseType is not null && IsControl(type.BaseType)); public IXamlAstNode Visit(IXamlAstNode node) { @@ -68,8 +69,8 @@ public IXamlAstNode Visit(IXamlAstNode node) return node; // Find Name and Access properties - XamlAstTextNode nameText = null; - XamlAstTextNode accessText = null; + XamlAstTextNode? nameText = null; + XamlAstTextNode? accessText = null; foreach (var child in objectNode.Children) { if (child is XamlAstXamlPropertyValueNode propertyValueNode && @@ -113,29 +114,102 @@ public void Pop() } } - private static string GenerateSourceCode( + private static DiagnosticSeverity MapSeverity(XamlDiagnosticSeverity severity) + { + return severity switch + { + XamlDiagnosticSeverity.None => DiagnosticSeverity.Info, + XamlDiagnosticSeverity.Warning => DiagnosticSeverity.Warning, + XamlDiagnosticSeverity.Error => DiagnosticSeverity.Error, + XamlDiagnosticSeverity.Fatal => DiagnosticSeverity.Error, + _ => throw new ArgumentOutOfRangeException(nameof(severity), severity, null) + }; + } + + /// + /// Handler for diagnostics information within a context. + /// + /// The list of diagnostics to handle + /// The source generator context to report diagnostics to + /// The file to report diagnostics for. + /// Whether any diagnostics were error diagnostics + private static bool HandleDiagnostics( + IEnumerable diagnostics, + GeneratorExecutionContext context, + string filePath) + { + var anyError = false; + foreach (var diagnostic in diagnostics) + { + context.ReportDiagnostic( + Diagnostic.Create( + new DiagnosticDescriptor( + diagnostic.Code, + diagnostic.Title, + diagnostic.Title, + "Usage", + MapSeverity(diagnostic.Severity), + true), + Location.Create( + filePath, + new TextSpan(0, 0), + new LinePositionSpan( + new LinePosition(diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0), + new LinePosition(diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0)))) + ); + anyError = anyError || diagnostic.Severity >= XamlDiagnosticSeverity.Error; + } + + return anyError; + } + + private static string? GenerateSourceCode( INamedTypeSymbol classSymbol, string xamlFile, CSharpCompilation comp, - string fileName) + string fileName, + GeneratorExecutionContext context) { var className = classSymbol.Name; var nameSpace = classSymbol.ContainingNamespace.ToDisplayString(); var parsed = XDocumentXamlParser.Parse(xamlFile); var typeSystem = new RoslynTypeSystem(comp); + var diagnostics = new List(); + var diagnosticsHandler = new XamlDiagnosticsHandler() + { + HandleDiagnostic = diagnostic => + { + diagnostics.Add(diagnostic); + return diagnostic.Severity; + }, + CodeMappings = DiagnosticsCodes.MapToXamlXErrorCode + }; var compiler = new XamlILCompiler( - new TransformerConfiguration(typeSystem, typeSystem.Assemblies[0], + new TransformerConfiguration(typeSystem, typeSystem.Assemblies.First(), new XamlLanguageTypeMappings(typeSystem) { XmlnsAttributes = {typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute")} - }), + }, + diagnosticsHandler: diagnosticsHandler), new XamlLanguageEmitMappings(), false); compiler.Transformers.Add(new TypeReferenceResolver()); compiler.Transform(parsed); + + if (HandleDiagnostics(diagnostics, context, fileName)) + { + return null; + } + var initialRoot = (XamlAstObjectNode) parsed.Root; var names = NameVisitor.GetNames(initialRoot); + if (initialRoot.Type.GetClrType().Id is not INamedTypeSymbol) + { + var kind = initialRoot.Type.GetClrType(); + throw new Exception($"CLR types referenced by XAML shouldn't be unnamed, but this one was: {kind} {kind.Id} {kind.Name}"); + } + var rootType = (INamedTypeSymbol)initialRoot.Type.GetClrType().Id; var rootTypeString = rootType.ToString(); if (classSymbol.ToString() != rootTypeString && classSymbol.BaseType?.ToString() != rootTypeString) @@ -201,8 +275,6 @@ public void Execute(GeneratorExecutionContext context) } var symbols = UnpackAnnotatedTypes(context, comp, receiver); - if (symbols == null) - return; foreach (var typeSymbol in symbols) { @@ -215,7 +287,7 @@ public void Execute(GeneratorExecutionContext context) context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor( - "RXN0001", + DiagnosticsCodes.XamlNotFound, $"Unable to discover the relevant Robust XAML file for {typeSymbol}.", "Unable to discover the relevant Robust XAML file " + $"expected at {xamlFileName}", @@ -231,7 +303,7 @@ public void Execute(GeneratorExecutionContext context) context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor( - "RXN0002", + DiagnosticsCodes.MultipleXamlFound, $"Found multiple candidate XAML files for {typeSymbol}", $"Multiple files exist with name {xamlFileName}", "Usage", @@ -247,7 +319,7 @@ public void Execute(GeneratorExecutionContext context) context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor( - "RXN0004", + DiagnosticsCodes.EmptyXamlFound, $"Unexpected empty Xaml-File was found at {xamlFileName}", "Expected Content due to a Class with the same name being annotated with [GenerateTypedNameReferences].", "Usage", @@ -260,15 +332,16 @@ public void Execute(GeneratorExecutionContext context) try { - var sourceCode = GenerateSourceCode(typeSymbol, txt, comp, xamlFileName); - context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); + var sourceCode = GenerateSourceCode(typeSymbol, txt, comp, xamlFileName, context); + if (sourceCode is not null) + context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); } catch (InvalidXamlRootTypeException invRootType) { context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor( - "RXN0005", + DiagnosticsCodes.InvalidRootType, $"XAML-File {xamlFileName} has the wrong root type!", $"{xamlFileName}: Expected root type '{invRootType.ExpectedType}' or '{invRootType.ExpectedBaseType}', but got '{invRootType.Actual}'.", "Usage", @@ -283,9 +356,9 @@ public void Execute(GeneratorExecutionContext context) context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor( - "RXN0003", + DiagnosticsCodes.UnhandledException, "Unhandled exception occured while generating typed Name references.", - $"Unhandled exception occured while generating typed Name references: {e}", + $"Unhandled exception occured while generating typed Name references: {e.ToString().Replace("\n", ",")}", "Usage", DiagnosticSeverity.Error, true), @@ -307,11 +380,11 @@ private IReadOnlyList UnpackAnnotatedTypes(in GeneratorExecuti { var model = compilation.GetSemanticModel(candidateClass.SyntaxTree); var typeSymbol = model.GetDeclaredSymbol(candidateClass); - var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr => + var relevantAttribute = typeSymbol?.GetAttributes().FirstOrDefault(attr => attr.AttributeClass != null && attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)); - if (relevantAttribute == null) + if (typeSymbol == null || relevantAttribute == null) { continue; } @@ -331,7 +404,7 @@ private IReadOnlyList UnpackAnnotatedTypes(in GeneratorExecuti context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor( - "RXN0006", + DiagnosticsCodes.MissingPartialDeclaration, missingPartialKeywordMessage, missingPartialKeywordMessage, "Usage", diff --git a/Robust.Client/UserInterface/Control.Styling.cs b/Robust.Client/UserInterface/Control.Styling.cs index 64cae4101..f1ed61886 100644 --- a/Robust.Client/UserInterface/Control.Styling.cs +++ b/Robust.Client/UserInterface/Control.Styling.cs @@ -28,7 +28,15 @@ public Stylesheet? Stylesheet } [ViewVariables] - public ICollection StyleClasses { get; } + public StyleClassCollection StyleClasses + { + get => _styleClasses; + set + { + _styleClasses = new(value); + _styleClasses._owner = this; + } + } [ViewVariables(VVAccess.ReadOnly)] public IReadOnlyCollection StylePseudoClass => _stylePseudoClass; @@ -45,7 +53,7 @@ public string? StyleIdentifier } private readonly Dictionary _styleProperties = new(); - private readonly HashSet _styleClasses = new(); + private StyleClassCollection _styleClasses; private readonly HashSet _stylePseudoClass = new(); // Styling needs to be updated. @@ -102,20 +110,17 @@ public bool HasStyleClass(string className) public void AddStyleClass(string className) { _styleClasses.Add(className); - Restyle(); } public void RemoveStyleClass(string className) { _styleClasses.Remove(className); - Restyle(); } public void SetOnlyStyleClass(string className) { _styleClasses.Clear(); _styleClasses.Add(className); - Restyle(); } internal void Restyle() @@ -276,18 +281,37 @@ public T StylePropertyDefault(string param, T defaultValue) return defaultValue; } - private sealed class StyleClassCollection : ICollection, IReadOnlyCollection + public sealed class StyleClassCollection : ICollection, IReadOnlyCollection { - private readonly Control _owner; + internal Control? _owner; + private readonly HashSet _values; + + internal StyleClassCollection(Control control) + { + _owner = control; + _values = new(); + } + + public StyleClassCollection() + { + _values = new(); + } - public StyleClassCollection(Control owner) + public StyleClassCollection(IEnumerable items) { - _owner = owner; + _values = new(items); } + public StyleClassCollection(params string[] items) + { + _values = new(items); + } + + public static StyleClassCollection Parse(string s) => new(s.Split(' ')); + public IEnumerator GetEnumerator() { - return _owner._styleClasses.GetEnumerator(); + return _values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -297,39 +321,40 @@ IEnumerator IEnumerable.GetEnumerator() public void Add(string item) { - _owner.AddStyleClass(item); + _values.Add(item); + _owner?.Restyle(); } public void Clear() { - _owner._styleClasses.Clear(); - _owner.Restyle(); + _values.Clear(); + _owner?.Restyle(); } public bool Contains(string item) { - return _owner._styleClasses.Contains(item); + return _values.Contains(item); } public void CopyTo(string[] array, int arrayIndex) { - _owner._styleClasses.CopyTo(array, arrayIndex); + _values.CopyTo(array, arrayIndex); } public bool Remove(string item) { - var ret = _owner._styleClasses.Remove(item); + var ret = _values.Remove(item); if (ret) { - _owner.Restyle(); + _owner?.Restyle(); } return ret; } - int ICollection.Count => _owner._styleClasses.Count; + int ICollection.Count => _values.Count; public bool IsReadOnly => false; - int IReadOnlyCollection.Count => _owner._styleClasses.Count; + int IReadOnlyCollection.Count => _values.Count; } } } diff --git a/Robust.Client/UserInterface/Control.cs b/Robust.Client/UserInterface/Control.cs index 546635abe..ebea1db2b 100644 --- a/Robust.Client/UserInterface/Control.cs +++ b/Robust.Client/UserInterface/Control.cs @@ -532,7 +532,7 @@ public Color ActualModulateSelf public Control() { UserInterfaceManagerInternal = IoCManager.Resolve(); - StyleClasses = new StyleClassCollection(this); + _styleClasses = new StyleClassCollection(this); Children = new OrderedChildCollection(this); Theme = UserInterfaceManagerInternal.CurrentTheme; XamlChildren = Children; diff --git a/Robust.Client/UserInterface/Controls/Button.cs b/Robust.Client/UserInterface/Controls/Button.cs index dd44b7f15..961835ce1 100644 --- a/Robust.Client/UserInterface/Controls/Button.cs +++ b/Robust.Client/UserInterface/Controls/Button.cs @@ -11,6 +11,20 @@ public class Button : ContainerButton { public Label Label { get; } + // Compatibility shim for old XAML behaviour that assumed setting + // classes would concatenate with the StyleClassButton instead of overwriting + // all of them + [ViewVariables] + public new StyleClassCollection StyleClasses + { + get => base.StyleClasses; + set + { + base.StyleClasses = value; + base.StyleClasses.Add(StyleClassButton); + } + } + public Button() { AddStyleClass(StyleClassButton); diff --git a/Robust.Client/UserInterface/Controls/MultiselectOptionButton.cs b/Robust.Client/UserInterface/Controls/MultiselectOptionButton.cs index a4d175819..cb1a24222 100644 --- a/Robust.Client/UserInterface/Controls/MultiselectOptionButton.cs +++ b/Robust.Client/UserInterface/Controls/MultiselectOptionButton.cs @@ -5,6 +5,7 @@ using Robust.Client.Graphics; using Robust.Shared.Graphics; using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Robust.Client.UserInterface.Controls @@ -59,6 +60,20 @@ public string? Label set => _label.Text = value; } + // Compatibility shim for old XAML behaviour that assumed setting + // classes would concatenate with the StyleClassButton instead of overwriting + // all of them + [ViewVariables] + new public StyleClassCollection StyleClasses + { + get => base.StyleClasses; + set + { + base.StyleClasses = value; + base.StyleClasses.Add(StyleClassButton); + } + } + public MultiselectOptionButton() { AddStyleClass(StyleClassButton); diff --git a/Robust.Client/UserInterface/Controls/OptionButton.cs b/Robust.Client/UserInterface/Controls/OptionButton.cs index bbcb49771..496e0e775 100644 --- a/Robust.Client/UserInterface/Controls/OptionButton.cs +++ b/Robust.Client/UserInterface/Controls/OptionButton.cs @@ -4,6 +4,7 @@ using Robust.Client.Graphics; using Robust.Shared.Localization; using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Robust.Client.UserInterface.Controls @@ -63,6 +64,20 @@ public bool Filterable } } + // Compatibility shim for old XAML behaviour that assumed setting + // classes would concatenate with the StyleClassButton instead of overwriting + // all of them + [ViewVariables] + new public StyleClassCollection StyleClasses + { + get => base.StyleClasses; + set + { + base.StyleClasses = value; + base.StyleClasses.Add(StyleClassButton); + } + } + public OptionButton() { OptionStyleClasses = new List(); diff --git a/Robust.Shared/ContentPack/AssemblyTypeChecker.cs b/Robust.Shared/ContentPack/AssemblyTypeChecker.cs index e87fcaa21..52e5a5fc2 100644 --- a/Robust.Shared/ContentPack/AssemblyTypeChecker.cs +++ b/Robust.Shared/ContentPack/AssemblyTypeChecker.cs @@ -292,6 +292,16 @@ private bool DoVerifyIL( var type = GetTypeFromDefinition(reader, res.Type); msg = $"{msg}, type: {type}"; } + + foreach (var argument in res.ErrorArguments) + { + if (argument.Name == "Token" && argument.Value is int token) + msg = $"{msg}, {argument.Name} 0x{token:X8}"; + else if (argument.Name == "Offset" && argument.Value is int offset) + msg = $"{msg}, {argument.Name} IL_{offset:X4}"; + else + msg = $"{msg}, {argument.Name} {argument.Value}"; + } } catch (UnsupportedMetadataException e) { diff --git a/Robust.Xaml/DiagnosticsCodes.cs b/Robust.Xaml/DiagnosticsCodes.cs new file mode 100644 index 000000000..87cd7faf2 --- /dev/null +++ b/Robust.Xaml/DiagnosticsCodes.cs @@ -0,0 +1,39 @@ +using System; +using XamlX; + +namespace Robust.Xaml; + +internal static class DiagnosticsCodes +{ + internal const string XamlNotFound = "RXN0001"; + internal const string MultipleXamlFound = "RXN0002"; + internal const string UnhandledException = "RXN0003"; + internal const string EmptyXamlFound = "RXN0004"; + internal const string InvalidRootType = "RXN0005"; + internal const string MissingPartialDeclaration = "RXN0006"; + + internal const string TransformError = "RXN0007"; + internal const string TypeSystemError = "RXN0008"; + internal const string ParseError = "RXN0009"; + internal const string EmitError = "RXN0010"; + internal const string Obsolete = "RXN0011"; + + internal static string MapToXamlXErrorCode(object exception) + { + return exception switch + { + XamlXWellKnownDiagnosticCodes wellKnownDiagnosticCodes => wellKnownDiagnosticCodes switch + { + XamlXWellKnownDiagnosticCodes.Obsolete => Obsolete, + _ => throw new ArgumentOutOfRangeException() + }, + + XamlTransformException => TransformError, + XamlTypeSystemException => TypeSystemError, + XamlLoadException => EmitError, + XamlParseException => ParseError, + + _ => "RXN_UNKNOWN", + }; + } +} diff --git a/Robust.Xaml/LowLevelCustomizations.cs b/Robust.Xaml/LowLevelCustomizations.cs index abad74fd5..e23c28f1d 100644 --- a/Robust.Xaml/LowLevelCustomizations.cs +++ b/Robust.Xaml/LowLevelCustomizations.cs @@ -51,11 +51,18 @@ public LowLevelCustomizations(CecilTypeSystem typeSystem) // resolve every type that we look for or substitute in when doing surgery // what a mess! _typeSystem = typeSystem; + if (typeSystem.TargetAssemblyDefinition == null) + { + throw new ArgumentNullException(nameof(typeSystem), + "Provided type system should have a non-null target assembly definition"); + } + _asm = typeSystem.TargetAssemblyDefinition; TypeDefinition ResolveType(string name) => - typeSystem.GetTypeReference(_typeSystem.FindType(name)).Resolve() - ?? throw new NullReferenceException($"type must exist: {name}"); + typeSystem.GetTypeReference(_typeSystem.FindType(name) ?? + throw new NullReferenceException($"type must exist: {name}")) + .Resolve(); _iocManager = ResolveType("Robust.Shared.IoC.IoCManager"); _iXamlProxyHelper = ResolveType( diff --git a/Robust.Xaml/Robust.Xaml.csproj b/Robust.Xaml/Robust.Xaml.csproj index ff779e6ab..632d73a98 100644 --- a/Robust.Xaml/Robust.Xaml.csproj +++ b/Robust.Xaml/Robust.Xaml.csproj @@ -1,14 +1,14 @@  - netstandard2.0 12.0 enable - - - + + + + @@ -16,4 +16,5 @@ + diff --git a/Robust.Xaml/RobustXamlILCompiler.cs b/Robust.Xaml/RobustXamlILCompiler.cs index ec95be233..c2dd14507 100644 --- a/Robust.Xaml/RobustXamlILCompiler.cs +++ b/Robust.Xaml/RobustXamlILCompiler.cs @@ -107,10 +107,10 @@ public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContextWithLocals f.Name == XamlCustomizations.ContextNameScopeFieldName); var namescopeRegisterFunction = context.Configuration.TypeSystem - .FindType("Robust.Client.UserInterface.XAML.NameScope").Methods + .FindType("Robust.Client.UserInterface.XAML.NameScope")!.Methods .First(m => m.Name == "Register"); - using (var targetLoc = context.GetLocalOfType(context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control"))) + using (var targetLoc = context.GetLocalOfType(context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control")!)) { codeGen @@ -144,7 +144,7 @@ public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode nod mnode.Manipulation = new XamlManipulationGroupNode(mnode, new[] { - mnode.Manipulation, + mnode.Manipulation!, new HandleRootObjectScopeNode(mnode) }); } @@ -165,7 +165,7 @@ public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContext f.Name == "NameScope"); var nameScopeType = context.Configuration.TypeSystem - .FindType("Robust.Client.UserInterface.XAML.NameScope"); + .FindType("Robust.Client.UserInterface.XAML.NameScope")!; var nameScopeCompleteMethod = nameScopeType.Methods.First(m => m.Name == "Complete"); var nameScopeAbsorbMethod = nameScopeType.Methods.First(m => m.Name == "Absorb"); using (var local = codeGen.LocalsPool.GetLocal(controlType)) diff --git a/Robust.Xaml/XamlAotCompiler.cs b/Robust.Xaml/XamlAotCompiler.cs index 0c41140b6..db939d4ce 100644 --- a/Robust.Xaml/XamlAotCompiler.cs +++ b/Robust.Xaml/XamlAotCompiler.cs @@ -49,7 +49,7 @@ public static (bool success, bool writtentofile) Compile(IBuildEngine engine, st .Where(r => !r.ToLowerInvariant().EndsWith("robust.build.tasks.dll")) .Concat(new[] { input }), input); - var asm = typeSystem.TargetAssemblyDefinition; + var asm = typeSystem.TargetAssemblyDefinition!; if (asm.MainModule.GetType("CompiledRobustXaml", "XamlIlContext") != null) { @@ -85,10 +85,40 @@ public static (bool success, bool writtentofile) Compile(IBuildEngine engine, st /// true if compilation succeeded in every case static bool CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem) { - var asm = typeSystem.TargetAssemblyDefinition; + var asm = typeSystem.TargetAssemblyDefinition!; var embrsc = new EmbeddedResources(asm); - var xaml = new XamlCustomizations(typeSystem, typeSystem.TargetAssembly); + void LogDiagnostic(XamlDiagnostic diagnostic) + { + if (diagnostic.Severity == XamlDiagnosticSeverity.Warning) + { + engine.LogWarningEvent(new BuildWarningEventArgs("Xaml", + diagnostic.Code, + diagnostic.Document ?? "", + diagnostic.LineNumber ?? 0, + diagnostic.LinePosition ?? 0, + diagnostic.LineNumber ?? 0, + diagnostic.LinePosition ?? 0, + diagnostic.Title, + "", + "Robust.Xaml")); + } + else if (diagnostic.Severity != XamlDiagnosticSeverity.None) + { + engine.LogErrorEvent(new BuildErrorEventArgs("Xaml", + diagnostic.Code, + diagnostic.Document ?? "", + diagnostic.LineNumber ?? 0, + diagnostic.LinePosition ?? 0, + diagnostic.LineNumber ?? 0, + diagnostic.LinePosition ?? 0, + diagnostic.Title, + "", + "Robust.Xaml")); + } + } + + var xaml = new XamlCustomizations(typeSystem, typeSystem.TargetAssembly!, LogDiagnostic); var lowLevel = new LowLevelCustomizations(typeSystem); var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext", @@ -129,24 +159,31 @@ bool CompileGroup(IResourceGroup group) classname = res.Name.Replace(".xaml",""); } - var classType = typeSystem.TargetAssembly.FindType(classname); + var classType = typeSystem.TargetAssembly!.FindType(classname); if (classType == null) throw new InvalidProgramException($"Unable to find type '{classname}'"); xaml.ILCompiler.Transform(parsed); var populateName = $"Populate:{res.Name}"; + var buildName = $"Build:{res.Name}"; var classTypeDefinition = typeSystem.GetTypeReference(classType).Resolve()!; var populateBuilder = typeSystem.CreateTypeBuilder(classTypeDefinition); - - xaml.ILCompiler.Compile(parsed, contextClass, - xaml.ILCompiler.DefinePopulateMethod(populateBuilder, parsed, populateName, true), - null, - null, - (closureName, closureBaseType) => - populateBuilder.DefineSubType(closureBaseType, closureName, false), - res.Uri, res + var buildBuilder = typeSystem.CreateTypeBuilder(classTypeDefinition); + var classTypeBuilder = + typeSystem.CreateTypeBuilder(classTypeDefinition, compilerGeneratedType: false); + + xaml.ILCompiler.Compile( + doc: parsed, + contextType: contextClass, + populateMethod: xaml.ILCompiler.DefinePopulateMethod(populateBuilder, parsed, populateName, XamlVisibility.Public), + populateDeclaringType: classTypeBuilder, + buildMethod: xaml.ILCompiler.DefineBuildMethod(buildBuilder, parsed, buildName, XamlVisibility.Public), + buildDeclaringType: classTypeBuilder, + namespaceInfoBuilder: null, + baseUri: res.Uri, + fileSource: res ); var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve().Methods diff --git a/Robust.Xaml/XamlCustomizations.cs b/Robust.Xaml/XamlCustomizations.cs index 870eb6cb9..d5e4dba2e 100644 --- a/Robust.Xaml/XamlCustomizations.cs +++ b/Robust.Xaml/XamlCustomizations.cs @@ -1,4 +1,7 @@ -using XamlX; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using XamlX; using XamlX.Ast; using XamlX.Emit; using XamlX.IL; @@ -30,7 +33,8 @@ internal sealed class XamlCustomizations /// (both and work) /// /// the default assembly (for unqualified names to be looked up in) - public XamlCustomizations(IXamlTypeSystem typeSystem, IXamlAssembly defaultAssembly) + /// Handler for diagnostics as reported by XAML + public XamlCustomizations(IXamlTypeSystem typeSystem, IXamlAssembly defaultAssembly, Action? handleDiagnostic) { TypeSystem = typeSystem; TypeMappings = new XamlLanguageTypeMappings(typeSystem) @@ -61,12 +65,22 @@ public XamlCustomizations(IXamlTypeSystem typeSystem, IXamlAssembly defaultAssem { ContextTypeBuilderCallback = EmitNameScopeField }; + var diagnosticsHandler = new XamlDiagnosticsHandler() + { + HandleDiagnostic = diagnostic => + { + handleDiagnostic?.Invoke(diagnostic); + return diagnostic.Severity; + }, + CodeMappings = DiagnosticsCodes.MapToXamlXErrorCode + }; TransformerConfiguration = new TransformerConfiguration( typeSystem, defaultAssembly, TypeMappings, XamlXmlnsMappings.Resolve(typeSystem, TypeMappings), - CustomValueConverter + CustomValueConverter, + diagnosticsHandler: diagnosticsHandler ); ILCompiler = new RobustXamlILCompiler(TransformerConfiguration, EmitMappings, true); } @@ -75,19 +89,15 @@ public XamlCustomizations(IXamlTypeSystem typeSystem, IXamlAssembly defaultAssem /// Create a field of type NameScope that contains a new NameScope, then /// alter the type's constructor to initialize that field. /// - /// the type to alter - /// the constructor to alter - private void EmitNameScopeField( - IXamlTypeBuilder typeBuilder, - IXamlILEmitter constructor - ) + /// The IL emitter to output to + private void EmitNameScopeField(IXamlILContextDefinition xaml) { - var nameScopeType = TypeSystem.FindType("Robust.Client.UserInterface.XAML.NameScope"); - var field = typeBuilder.DefineField(nameScopeType, + var nameScopeType = TypeSystem.FindType("Robust.Client.UserInterface.XAML.NameScope")!; + var field = xaml.TypeBuilder.DefineField(nameScopeType, ContextNameScopeFieldName, - true, + XamlVisibility.Public, false); - constructor + xaml.ConstructorBuilder.Generator .Ldarg_0() .Newobj(nameScopeType.GetConstructor()) .Stfld(field); @@ -106,6 +116,7 @@ IXamlILEmitter constructor /// /// context object that holds the TransformerConfiguration /// the node to consider rewriting + /// A list of custom attributes associated with the value /// the type of that node /// results get written to here /// @@ -113,8 +124,9 @@ IXamlILEmitter constructor private static bool CustomValueConverter( AstTransformationContext context, IXamlAstValueNode node, + IReadOnlyList? customAttributes, IXamlType type, - out IXamlAstValueNode? result) + [NotNullWhen(true)] out IXamlAstValueNode? result) { if (!(node is XamlAstTextNode textNode)) { diff --git a/Robust.Xaml/XamlJitCompiler.cs b/Robust.Xaml/XamlJitCompiler.cs index ce3eea0c8..dd772a049 100644 --- a/Robust.Xaml/XamlJitCompiler.cs +++ b/Robust.Xaml/XamlJitCompiler.cs @@ -1,10 +1,14 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Threading; +using XamlX; using XamlX.IL; using XamlX.Parsers; +using XamlX.TypeSystem; namespace Robust.Xaml; @@ -87,6 +91,111 @@ string contents } } + // Taken from https://github.com/AvaloniaUI/Avalonia/blob/01a8042094d741a8ddfcd441b4eabcb04092e988/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs#L132 + static Type EmitIgnoresAccessCheckAttributeDefinition(ModuleBuilder builder) + { + var tb = builder.DefineType("System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute", + TypeAttributes.Class | TypeAttributes.Public, typeof(Attribute)); + var field = tb.DefineField("_name", typeof(string), FieldAttributes.Private); + var propGet = tb.DefineMethod("get_AssemblyName", MethodAttributes.Public, typeof(string), + Array.Empty()); + var propGetIl = propGet.GetILGenerator(); + propGetIl.Emit(OpCodes.Ldarg_0); + propGetIl.Emit(OpCodes.Ldfld, field); + propGetIl.Emit(OpCodes.Ret); + var prop = tb.DefineProperty("AssemblyName", PropertyAttributes.None, typeof(string), Array.Empty()); + prop.SetGetMethod(propGet); + + + var ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, + new[] { typeof(string) }); + var ctorIl = ctor.GetILGenerator(); + ctorIl.Emit(OpCodes.Ldarg_0); + ctorIl.Emit(OpCodes.Ldarg_1); + ctorIl.Emit(OpCodes.Stfld, field); + ctorIl.Emit(OpCodes.Ldarg_0); + ctorIl.Emit(OpCodes.Call, typeof(Attribute) + .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .First(x => x.GetParameters().Length == 0)); + + ctorIl.Emit(OpCodes.Ret); + + tb.SetCustomAttribute(new CustomAttributeBuilder( + typeof(AttributeUsageAttribute).GetConstructor(new[] { typeof(AttributeTargets) })!, + new object[] { AttributeTargets.Assembly }, + new[] { typeof(AttributeUsageAttribute).GetProperty(nameof(AttributeUsageAttribute.AllowMultiple))! }, + new object[] { true })); + + return tb.CreateTypeInfo(); + } + + // Taken from https://github.com/AvaloniaUI/Avalonia/blob/01a8042094d741a8ddfcd441b4eabcb04092e988/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs#L185 + static HashSet FindAssembliesGrantingInternalAccess(Assembly assembly) + { + var result = new HashSet(); + var assemblyName = assembly.GetName(); + var publicKey = assemblyName.GetPublicKey(); + + // Search through all loaded assemblies to find those that grant InternalsVisibleTo to our assembly + foreach (var loadedAssembly in AppDomain.CurrentDomain.GetAssemblies()) + { + try + { + var ivtAttributes = loadedAssembly.GetCustomAttributes( + typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute), false); + + foreach (System.Runtime.CompilerServices.InternalsVisibleToAttribute ivt in ivtAttributes) + { + var ivtName = ivt.AssemblyName; + if (string.IsNullOrWhiteSpace(ivtName)) + continue; + + // Parse the InternalsVisibleTo assembly name + var ivtAssemblyName = new AssemblyName(ivtName); + + // Check if it matches our assembly name + if (string.Equals(ivtAssemblyName.Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) + { + // If public key is specified in IVT, verify it matches + var ivtPublicKey = ivtAssemblyName.GetPublicKey(); + if (ivtPublicKey != null && ivtPublicKey.Length > 0) + { + if (publicKey != null && publicKey.SequenceEqual(ivtPublicKey)) + { + result.Add(loadedAssembly); + } + } + else + { + // No public key specified in IVT, just match by name + result.Add(loadedAssembly); + } + } + } + } + catch + { + // Ignore assemblies that throw exceptions when accessing attributes + } + } + + return result; + } + + // Taken from https://github.com/AvaloniaUI/Avalonia/blob/01a8042094d741a8ddfcd441b4eabcb04092e988/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs#L185 + static void EmitIgnoresAccessCheckToAttribute(AssemblyName assemblyName, Type ignoresAccessChecksFromAttribute, AssemblyBuilder builder) + { + var name = assemblyName.Name; + if (string.IsNullOrWhiteSpace(name)) + return; + var key = assemblyName.GetPublicKey(); + if (key != null && key.Length != 0) + name += ", PublicKey=" + BitConverter.ToString(key).Replace("-", "").ToUpperInvariant(); + builder.SetCustomAttribute(new CustomAttributeBuilder( + ignoresAccessChecksFromAttribute!.GetConstructors()[0], + new object[] { name })); + } + private MethodInfo CompileOrCrash( Type t, Uri uri, @@ -95,7 +204,13 @@ string contents ) { - var xaml = new XamlCustomizations(_typeSystem, _typeSystem.FindAssembly(t.Assembly.FullName)); + void LogDiagnostic(XamlDiagnostic diagnostic) + { + if (diagnostic.Severity >= XamlDiagnosticSeverity.Error) + throw new Exception($"Error when recompiling {filePath}: {diagnostic.Title}"); + } + + var xaml = new XamlCustomizations(_typeSystem, _typeSystem.FindAssembly(t.Assembly.FullName!)!, LogDiagnostic); // attempt to parse the code var document = XDocumentXamlParser.Parse(contents); @@ -108,12 +223,21 @@ string contents AssemblyBuilderAccess.RunAndCollect ); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyNameString); + var declaration = EmitIgnoresAccessCheckAttributeDefinition(moduleBuilder); + + EmitIgnoresAccessCheckToAttribute(t.Assembly.GetName(), declaration, assemblyBuilder); + foreach (var assembly in FindAssembliesGrantingInternalAccess(t.Assembly)) + { + EmitIgnoresAccessCheckToAttribute(assembly.GetName(), declaration, assemblyBuilder); + } var contextClassRawBuilder = moduleBuilder.DefineType("ContextClass"); var populateClassRawBuilder = moduleBuilder.DefineType("PopulateClass"); + var buildClassRawBuilder = moduleBuilder.DefineType("BuildClass"); var contextClassBuilder = _typeSystem.CreateTypeBuilder(contextClassRawBuilder); var populateClassBuilder = _typeSystem.CreateTypeBuilder(populateClassRawBuilder); + var buildClassBuilder = _typeSystem.CreateTypeBuilder(buildClassRawBuilder); var contextClass = XamlILContextDefinition.GenerateContextClass( contextClassBuilder, @@ -122,23 +246,29 @@ string contents xaml.EmitMappings ); var populateName = "!Populate"; + var buildName = "!Build"; xaml.ILCompiler.Transform(document); xaml.ILCompiler.Compile( - document, - contextClass, - xaml.ILCompiler.DefinePopulateMethod( + doc: document, + contextType: contextClass, + populateMethod: xaml.ILCompiler.DefinePopulateMethod( populateClassBuilder, document, populateName, - true + XamlVisibility.Public + ), + populateDeclaringType: populateClassBuilder, + buildMethod: xaml.ILCompiler.DefineBuildMethod( + buildClassBuilder, + document, + buildName, + XamlVisibility.Public ), - null, - null, - (closureName, closureBaseType) => - contextClassBuilder.DefineSubType(closureBaseType, closureName, false), - uri.ToString(), - xaml.CreateFileSource(filePath, Encoding.UTF8.GetBytes(contents)) + buildDeclaringType: buildClassBuilder, + namespaceInfoBuilder: null, + baseUri: uri.ToString(), + fileSource: xaml.CreateFileSource(filePath, Encoding.UTF8.GetBytes(contents)) ); contextClassBuilder.CreateType(); diff --git a/XamlX b/XamlX index 5da4e1d57..009d48154 160000 --- a/XamlX +++ b/XamlX @@ -1 +1 @@ -Subproject commit 5da4e1d570a13ee270fafccd3778d29fc6ddc7f2 +Subproject commit 009d4815470cf4bf71d1adbb633a5d81dcb2bb52