diff --git a/Directory.Packages.props b/Directory.Packages.props
index e740d3a3e38..72462143247 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,6 +16,7 @@
+
@@ -39,6 +40,7 @@
+
@@ -80,6 +82,7 @@
+
diff --git a/MSBuild/XamlIL.targets b/MSBuild/XamlIL.targets
index c489b3e4bc8..9f0969ed145 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 22012ded28b..6c520195a0e 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -35,7 +35,21 @@ 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
+
+ ```
+
+- Setting `StyleClasses` in XAML now overrides all of the style classes with the provided list, instead of concatenating the given style class with any existing style classes.
+ The stock Button controls have modified setters to provide compatibility with older stylesheets, but user-defined controls may need to watch out for this.
### New features
diff --git a/Robust.Client.Injectors/Robust.Client.Injectors.csproj b/Robust.Client.Injectors/Robust.Client.Injectors.csproj
index 9a4a33ce9ad..ce195f601fe 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 e3daf4b0bf3..2e39641b98e 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 f7fa741d847..5dea68f1140 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 1d6af521b35..6f733bf76b7 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 828fd5438ce..851a02ed4da 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 64cae41011a..f1ed61886c6 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 546635abece..ebea1db2b6b 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 dd44b7f1513..961835ce15a 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 a4d17581958..cb1a2422239 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 bbcb49771d9..496e0e77534 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 e87fcaa21f8..52e5a5fc242 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 00000000000..ab13cc6a1a7
--- /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 abad74fd5b7..e23c28f1dc9 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 ff779e6ab06..632d73a9891 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 ec95be23392..c2dd145076b 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 0c41140b6e6..db939d4cedd 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 870eb6cb9f9..d5e4dba2eac 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 ce3eea0c872..dd772a04997 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 5da4e1d570a..009d4815470 160000
--- a/XamlX
+++ b/XamlX
@@ -1 +1 @@
-Subproject commit 5da4e1d570a13ee270fafccd3778d29fc6ddc7f2
+Subproject commit 009d4815470cf4bf71d1adbb633a5d81dcb2bb52