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;
}
}