mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
202 lines
9.8 KiB
C#
202 lines
9.8 KiB
C#
using System.Linq;
|
|
using XamlX.Ast;
|
|
using XamlX.Emit;
|
|
using XamlX.IL;
|
|
using XamlX.Transform;
|
|
using XamlX.TypeSystem;
|
|
|
|
namespace Robust.Build.Tasks
|
|
{
|
|
/// <summary>
|
|
/// Emitters & Transformers based on:
|
|
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
|
|
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
|
|
/// </summary>
|
|
public class RobustXamlILCompiler : XamlILCompiler
|
|
{
|
|
public RobustXamlILCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings, bool fillWithDefaults) : base(configuration, emitMappings, fillWithDefaults)
|
|
{
|
|
Transformers.Add(new AddNameScopeRegistration());
|
|
Transformers.Add(new RobustMarkRootObjectScopeNode());
|
|
|
|
Emitters.Add(new AddNameScopeRegistration.Emitter());
|
|
Emitters.Add(new RobustMarkRootObjectScopeNode.Emitter());
|
|
}
|
|
|
|
class AddNameScopeRegistration : IXamlAstTransformer
|
|
{
|
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
|
|
{
|
|
if (node is XamlPropertyAssignmentNode pa)
|
|
{
|
|
if (pa.Property.Name == "Name"
|
|
&& pa.Property.DeclaringType.FullName == "Robust.Client.UserInterface.Control")
|
|
{
|
|
if (context.ParentNodes().FirstOrDefault() is XamlManipulationGroupNode mg
|
|
&& mg.Children.OfType<RobustNameScopeRegistrationXamlIlNode>().Any())
|
|
return node;
|
|
|
|
IXamlAstValueNode value = null;
|
|
for (var c = 0; c < pa.Values.Count; c++)
|
|
if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
|
|
{
|
|
value = pa.Values[c];
|
|
if (!(value is XamlAstTextNode))
|
|
{
|
|
var local = new XamlAstCompilerLocalNode(value);
|
|
// Wrap original in local initialization
|
|
pa.Values[c] = new XamlAstLocalInitializationNodeEmitter(value, value, local);
|
|
// Use local
|
|
value = local;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (value != null)
|
|
{
|
|
var objectType = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().FirstOrDefault()?.Type.GetClrType();
|
|
return new XamlManipulationGroupNode(pa)
|
|
{
|
|
Children =
|
|
{
|
|
pa,
|
|
new RobustNameScopeRegistrationXamlIlNode(value, objectType)
|
|
}
|
|
};
|
|
}
|
|
}
|
|
/*else if (pa.Property.CustomAttributes.Select(attr => attr.Type).Intersect(context.Configuration.TypeMappings.DeferredContentPropertyAttributes).Any())
|
|
{
|
|
pa.Values[pa.Values.Count - 1] =
|
|
new NestedScopeMetadataNode(pa.Values[pa.Values.Count - 1]);
|
|
}*/
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
class RobustNameScopeRegistrationXamlIlNode : XamlAstNode, IXamlAstManipulationNode
|
|
{
|
|
public IXamlAstValueNode Name { get; set; }
|
|
public IXamlType TargetType { get; }
|
|
|
|
public RobustNameScopeRegistrationXamlIlNode(IXamlAstValueNode name, IXamlType targetType) : base(name)
|
|
{
|
|
TargetType = targetType;
|
|
Name = name;
|
|
}
|
|
|
|
public override void VisitChildren(IXamlAstVisitor visitor)
|
|
=> Name = (IXamlAstValueNode)Name.Visit(visitor);
|
|
}
|
|
|
|
internal class Emitter : IXamlAstLocalsNodeEmitter<IXamlILEmitter, XamlILNodeEmitResult>
|
|
{
|
|
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
|
|
{
|
|
if (node is RobustNameScopeRegistrationXamlIlNode registration)
|
|
{
|
|
|
|
var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
|
|
f.Name == XamlCompiler.ContextNameScopeFieldName);
|
|
var namescopeRegisterFunction = context.Configuration.TypeSystem
|
|
.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")))
|
|
{
|
|
|
|
codeGen
|
|
// var target = {pop}
|
|
.Stloc(targetLoc.Local)
|
|
// _context.NameScope.Register(Name, target)
|
|
.Ldloc(context.ContextLocal)
|
|
.Ldfld(scopeField);
|
|
|
|
context.Emit(registration.Name, codeGen, registration.Name.Type.GetClrType());
|
|
|
|
codeGen
|
|
.Ldloc(targetLoc.Local)
|
|
.EmitCall(namescopeRegisterFunction, true);
|
|
}
|
|
|
|
return XamlILNodeEmitResult.Void(1);
|
|
}
|
|
return default;
|
|
}
|
|
}
|
|
}
|
|
|
|
class RobustMarkRootObjectScopeNode : IXamlAstTransformer
|
|
{
|
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
|
|
{
|
|
if (!context.ParentNodes().Any()
|
|
&& node is XamlValueWithManipulationNode mnode)
|
|
{
|
|
mnode.Manipulation = new XamlManipulationGroupNode(mnode,
|
|
new[]
|
|
{
|
|
mnode.Manipulation,
|
|
new HandleRootObjectScopeNode(mnode)
|
|
});
|
|
}
|
|
return node;
|
|
}
|
|
class HandleRootObjectScopeNode : XamlAstNode, IXamlAstManipulationNode
|
|
{
|
|
public HandleRootObjectScopeNode(IXamlLineInfo lineInfo) : base(lineInfo)
|
|
{
|
|
}
|
|
}
|
|
internal class Emitter : IXamlILAstNodeEmitter
|
|
{
|
|
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
|
|
{
|
|
if (!(node is HandleRootObjectScopeNode))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var controlType = context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control");
|
|
|
|
var next = codeGen.DefineLabel();
|
|
var dontAbsorb = codeGen.DefineLabel();
|
|
var end = codeGen.DefineLabel();
|
|
var contextScopeField = context.RuntimeContext.ContextType.Fields.First(f =>
|
|
f.Name == XamlCompiler.ContextNameScopeFieldName);
|
|
var controlNameScopeField = controlType.Fields.First(f => f.Name == "NameScope");
|
|
var nameScopeType = context.Configuration.TypeSystem
|
|
.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))
|
|
{
|
|
codeGen
|
|
.Isinst(controlType)
|
|
.Dup()
|
|
.Stloc(local.Local) //store control in local field
|
|
.Brfalse(next) //if control is null, move to next (this should never happen but whatev, avalonia does it)
|
|
.Ldloc(context.ContextLocal)
|
|
.Ldfld(contextScopeField)
|
|
.Ldloc(local.Local) //load control from local field
|
|
.Ldfld(controlNameScopeField) //load namescope field from control
|
|
.EmitCall(nameScopeAbsorbMethod, true)
|
|
.Ldloc(local.Local) //load control
|
|
.Ldloc(context.ContextLocal) //load contextObject
|
|
.Ldfld(contextScopeField) //load namescope field from context obj
|
|
.Stfld(controlNameScopeField) //store namescope field in control
|
|
.MarkLabel(next)
|
|
.Ldloc(context.ContextLocal)
|
|
.Ldfld(contextScopeField)
|
|
.EmitCall(nameScopeCompleteMethod, true); //set the namescope as complete
|
|
}
|
|
|
|
return XamlILNodeEmitResult.Void(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|