using XamlX; using XamlX.Ast; using XamlX.Emit; using XamlX.IL; using XamlX.Transform; using XamlX.TypeSystem; namespace Robust.Xaml; /// /// Shared XAML config info that both the AOT and JIT compiler can use. /// /// /// This is a bunch of code primarily written by PJB that originally appeared in XamlAotCompiler.cs. /// internal sealed class XamlCustomizations { public const string ContextNameScopeFieldName = "RobustNameScope"; public readonly IXamlTypeSystem TypeSystem; public readonly XamlLanguageTypeMappings TypeMappings; public readonly XamlLanguageEmitMappings EmitMappings; public readonly TransformerConfiguration TransformerConfiguration; public readonly RobustXamlILCompiler ILCompiler; /// /// Create and hold a bunch of resources related to SS14's particular dialect of XAML. /// /// /// the type system for XamlX to use /// (both and work) /// /// the default assembly (for unqualified names to be looked up in) public XamlCustomizations(IXamlTypeSystem typeSystem, IXamlAssembly defaultAssembly) { TypeSystem = typeSystem; TypeMappings = new XamlLanguageTypeMappings(typeSystem) { XmlnsAttributes = { typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"), }, ContentAttributes = { typeSystem.GetType("Avalonia.Metadata.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"), }; EmitMappings = new XamlLanguageEmitMappings { ContextTypeBuilderCallback = EmitNameScopeField }; TransformerConfiguration = new TransformerConfiguration( typeSystem, defaultAssembly, TypeMappings, XamlXmlnsMappings.Resolve(typeSystem, TypeMappings), CustomValueConverter ); ILCompiler = new RobustXamlILCompiler(TransformerConfiguration, EmitMappings, true); } /// /// 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 ) { var nameScopeType = TypeSystem.FindType("Robust.Client.UserInterface.XAML.NameScope"); var field = typeBuilder.DefineField(nameScopeType, ContextNameScopeFieldName, true, false); constructor .Ldarg_0() .Newobj(nameScopeType.GetConstructor()) .Stfld(field); } /// /// Convert a to some other kind of node, /// if the purpose of the node appears to be to represent one of various /// builtin types. /// /// /// (See, for instance, .) /// /// The arguments here come from an interface built into XamlX. /// /// context object that holds the TransformerConfiguration /// the node to consider rewriting /// the type of that node /// results get written to here /// /// if the literal for a type is poorly spelled for that type private static bool CustomValueConverter( AstTransformationContext context, IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode? result) { if (!(node is XamlAstTextNode textNode)) { result = null; return false; } var text = textNode.Text; var types = context.GetRobustTypes(); if (type.Equals(types.Vector2)) { var foo = MathParsing.ParseVector2(text); if (foo == null) { throw new XamlLoadException($"Unable to parse \"{text}\" as a Vector2", node); } var (x, y) = foo.Value; result = new RXamlSingleVecLikeConstAstNode( node, types.Vector2, types.Vector2ConstructorFull, types.Single, new[] { x, y }); return true; } if (type.Equals(types.Thickness)) { var foo = MathParsing.ParseThickness(text); if (foo == null) { throw new XamlLoadException($"Unable to parse \"{text}\" as a Thickness", node); } var val = foo; float[] full; if (val.Length == 1) { var u = val[0]; full = new[] { u, u, u, u }; } else if (val.Length == 2) { var h = val[0]; var v = val[1]; full = new[] { h, v, h, v }; } else // 4 { full = val; } result = new RXamlSingleVecLikeConstAstNode( node, types.Thickness, types.ThicknessConstructorFull, types.Single, full); return true; } if (type.Equals(types.Color)) { // TODO: Interpret these colors at XAML compile time instead of at runtime. result = new RXamlColorAstNode(node, types, text); return true; } result = null; return false; } /// /// Wrap the and /// from a Xaml file or from a XamlMetadataAttribute. /// /// /// This interface is the primary input format that XamlX expects. /// /// the resource file path /// the contents /// IFileSource public IFileSource CreateFileSource(string filePath, byte[] contents) { return new InternalFileSource(filePath, contents); } /// /// A trivial implementation of . /// private class InternalFileSource(string filePath, byte[] contents) : IFileSource { public string FilePath { get; } = filePath; public byte[] FileContents { get; } = contents; } }