mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
Add canonical instance encoding for constants
This commit is contained in:
@@ -14,6 +14,7 @@ public class Disassembler
|
|||||||
private readonly MarkupLoadResult _loadResult;
|
private readonly MarkupLoadResult _loadResult;
|
||||||
private readonly Dictionary<string, string> _importedUris;
|
private readonly Dictionary<string, string> _importedUris;
|
||||||
private readonly Dictionary<uint, List<Label>> _offsetLabelMap = new();
|
private readonly Dictionary<uint, List<Label>> _offsetLabelMap = new();
|
||||||
|
private static readonly TypeSchema _stringTypeSchema = UIXTypes.MapIDToType(UIXTypeID.String);
|
||||||
|
|
||||||
private Disassembler(MarkupLoadResult loadResult)
|
private Disassembler(MarkupLoadResult loadResult)
|
||||||
{
|
{
|
||||||
@@ -224,70 +225,10 @@ public class Disassembler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stringTypeSchema = UIXTypes.MapIDToType(UIXTypeID.String);
|
|
||||||
|
|
||||||
foreach (var (c, typeSchema, constantValue) in constants)
|
foreach (var (c, typeSchema, constantValue) in constants)
|
||||||
{
|
{
|
||||||
var constantName = $"const{c:D}";
|
var constantName = $"const{c:D}";
|
||||||
QualifiedTypeName qualifiedTypeName = GetQualifiedName(typeSchema);
|
yield return EncodeConstant(constantValue, constantName, typeSchema);
|
||||||
|
|
||||||
if (constantValue is IStringEncodable encodable)
|
|
||||||
{
|
|
||||||
var encodedValue = encodable.EncodeString();
|
|
||||||
yield return new StringEncodedConstantDirective(constantName, qualifiedTypeName, encodedValue);
|
|
||||||
}
|
|
||||||
else if (constantValue is Layout.ILayout constantLayout && Layout.PredefinedLayouts.TryConvertToString(constantLayout, out var constantLayoutString))
|
|
||||||
{
|
|
||||||
qualifiedTypeName = GetQualifiedName(UIXTypes.MapIDToType(UIXTypeID.Layout));
|
|
||||||
yield return new StringEncodedConstantDirective(constantName, qualifiedTypeName, constantLayoutString);
|
|
||||||
}
|
|
||||||
else if (typeSchema.SupportsTypeConversion(stringTypeSchema))
|
|
||||||
{
|
|
||||||
var encodedValue = constantValue.ToString();
|
|
||||||
yield return new StringEncodedConstantDirective(constantName, qualifiedTypeName, encodedValue);
|
|
||||||
}
|
|
||||||
else if (typeSchema.SupportsBinaryEncoding)
|
|
||||||
{
|
|
||||||
ByteCodeWriter writer = new();
|
|
||||||
typeSchema.EncodeBinary(writer, constantValue);
|
|
||||||
|
|
||||||
var reader = writer.CreateReader();
|
|
||||||
byte[] encodedBytes = new byte[reader.Size];
|
|
||||||
Marshal.Copy(reader.GetAddress(0), encodedBytes, 0, (int)reader.Size);
|
|
||||||
|
|
||||||
yield return new BinaryEncodedConstantDirective(constantName, qualifiedTypeName, encodedBytes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XName constElemName = qualifiedTypeName.NamespacePrefix is null
|
|
||||||
? qualifiedTypeName.TypeName
|
|
||||||
: XName.Get(qualifiedTypeName.TypeName, qualifiedTypeName.NamespacePrefix);
|
|
||||||
XElement constElem = new(constElemName);
|
|
||||||
|
|
||||||
var defaultConstantValue = typeSchema.ConstructDefault();
|
|
||||||
|
|
||||||
foreach (var prop in typeSchema.Properties)
|
|
||||||
{
|
|
||||||
// No need to serialize properties that can't be set
|
|
||||||
if (!prop.CanWrite)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var defaultPropValue = prop.GetValue(defaultConstantValue);
|
|
||||||
var propValue = prop.GetValue(constantValue);
|
|
||||||
|
|
||||||
var encodedPropValue = EncodeSimpleConstant(propValue);
|
|
||||||
var encodedDefaultPropValue = EncodeSimpleConstant(defaultPropValue);
|
|
||||||
|
|
||||||
// No need to serialize properties that are at their default value
|
|
||||||
if (encodedPropValue == encodedDefaultPropValue)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
constElem.SetAttributeValue(prop.Name, encodedPropValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
var constructor = constElem.ToString(SaveOptions.DisableFormatting);
|
|
||||||
yield return new ConstantDirective(constantName, qualifiedTypeName, constructor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,6 +317,78 @@ public class Disassembler
|
|||||||
labels.Add(new(labelName));
|
labels.Add(new(labelName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ConstantDirective EncodeConstant(object constantValue, string constantName, TypeSchema typeSchema)
|
||||||
|
{
|
||||||
|
var qualifiedTypeName = GetQualifiedName(typeSchema);
|
||||||
|
|
||||||
|
// String encodable
|
||||||
|
if (constantValue is IStringEncodable encodable)
|
||||||
|
{
|
||||||
|
var encodedValue = encodable.EncodeString();
|
||||||
|
return new StringEncodedConstantDirective(constantName, qualifiedTypeName, encodedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom handling for ILayout
|
||||||
|
if (constantValue is Layout.ILayout constantLayout && Layout.PredefinedLayouts.TryConvertToString(constantLayout, out var constantLayoutString))
|
||||||
|
{
|
||||||
|
qualifiedTypeName = GetQualifiedName(UIXTypes.MapIDToType(UIXTypeID.Layout));
|
||||||
|
return new CanonicalInstanceConstantDirective(constantName, qualifiedTypeName, constantLayoutString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeSchema.SupportsTypeConversion(_stringTypeSchema))
|
||||||
|
{
|
||||||
|
var encodedValue = constantValue.ToString();
|
||||||
|
return new StringEncodedConstantDirective(constantName, qualifiedTypeName, encodedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary encodable
|
||||||
|
if (typeSchema.SupportsBinaryEncoding)
|
||||||
|
{
|
||||||
|
ByteCodeWriter writer = new();
|
||||||
|
typeSchema.EncodeBinary(writer, constantValue);
|
||||||
|
|
||||||
|
var reader = writer.CreateReader();
|
||||||
|
byte[] encodedBytes = new byte[reader.Size];
|
||||||
|
Marshal.Copy(reader.GetAddress(0), encodedBytes, 0, (int)reader.Size);
|
||||||
|
reader.Dispose(null);
|
||||||
|
|
||||||
|
return new BinaryEncodedConstantDirective(constantName, qualifiedTypeName, encodedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
throw new NotSupportedException($"Unable to encode constant value '{constantValue}' of type '{qualifiedTypeName}'");
|
||||||
|
#else
|
||||||
|
XName constElemName = qualifiedTypeName.NamespacePrefix is null
|
||||||
|
? qualifiedTypeName.TypeName
|
||||||
|
: XName.Get(qualifiedTypeName.TypeName, qualifiedTypeName.NamespacePrefix);
|
||||||
|
XElement constElem = new(constElemName);
|
||||||
|
|
||||||
|
var defaultConstantValue = typeSchema.ConstructDefault();
|
||||||
|
|
||||||
|
foreach (var prop in typeSchema.Properties)
|
||||||
|
{
|
||||||
|
// No need to serialize properties that can't be set
|
||||||
|
if (!prop.CanWrite)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var defaultPropValue = prop.GetValue(defaultConstantValue);
|
||||||
|
var propValue = prop.GetValue(constantValue);
|
||||||
|
|
||||||
|
var encodedPropValue = EncodeSimpleConstant(propValue);
|
||||||
|
var encodedDefaultPropValue = EncodeSimpleConstant(defaultPropValue);
|
||||||
|
|
||||||
|
// No need to serialize properties that are at their default value
|
||||||
|
if (encodedPropValue == encodedDefaultPropValue)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
constElem.SetAttributeValue(prop.Name, encodedPropValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructor = constElem.ToString(SaveOptions.DisableFormatting);
|
||||||
|
return new ConstantDirective(constantName, qualifiedTypeName, constructor);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
private static string EncodeSimpleConstant(object value)
|
private static string EncodeSimpleConstant(object value)
|
||||||
{
|
{
|
||||||
return value is IStringEncodable encodable
|
return value is IStringEncodable encodable
|
||||||
|
|||||||
@@ -87,9 +87,23 @@ partial class Lexer
|
|||||||
if (!typeNameResult.WasSuccessful)
|
if (!typeNameResult.WasSuccessful)
|
||||||
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]);
|
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]);
|
||||||
|
|
||||||
var binaryEncodingMarkerResult = Parse.String(".bin")(input);
|
var encodingMarkerResult = Parse.Char('.')(input);
|
||||||
input = binaryEncodingMarkerResult.Remainder;
|
input = encodingMarkerResult.Remainder;
|
||||||
var binaryEncoded = binaryEncodingMarkerResult.WasSuccessful;
|
if (!encodingMarkerResult.WasSuccessful)
|
||||||
|
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '.', followed by the persist mode."]);
|
||||||
|
|
||||||
|
var persistModeResult = Parse.CharExcept('(').AtLeastOnce().Text().Token()(input);
|
||||||
|
input = persistModeResult.Remainder;
|
||||||
|
if (!persistModeResult.WasSuccessful)
|
||||||
|
return Result.Failure<IDirective>(input, "Invalid constant directive", ["No persist mode was specified."]);
|
||||||
|
|
||||||
|
Markup.MarkupConstantPersistMode? persistMode = persistModeResult.Value.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"bin" => Markup.MarkupConstantPersistMode.Binary,
|
||||||
|
"str" => Markup.MarkupConstantPersistMode.FromString,
|
||||||
|
"can" => Markup.MarkupConstantPersistMode.Canonical,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
var openBracketResult = Parse.Char('(').Token()(input);
|
var openBracketResult = Parse.Char('(').Token()(input);
|
||||||
input = openBracketResult.Remainder;
|
input = openBracketResult.Remainder;
|
||||||
@@ -109,7 +123,7 @@ partial class Lexer
|
|||||||
var constantName = constNameResult.Value;
|
var constantName = constNameResult.Value;
|
||||||
var typeName = typeNameResult.Value;
|
var typeName = typeNameResult.Value;
|
||||||
|
|
||||||
if (!binaryEncoded)
|
if (persistMode == Markup.MarkupConstantPersistMode.FromString)
|
||||||
{
|
{
|
||||||
directive = new StringEncodedConstantDirective(constantName, typeName, contentResult.Value)
|
directive = new StringEncodedConstantDirective(constantName, typeName, contentResult.Value)
|
||||||
{
|
{
|
||||||
@@ -117,7 +131,15 @@ partial class Lexer
|
|||||||
Column = column,
|
Column = column,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else if (persistMode == Markup.MarkupConstantPersistMode.Canonical)
|
||||||
|
{
|
||||||
|
directive = new CanonicalInstanceConstantDirective(constantName, typeName, contentResult.Value)
|
||||||
|
{
|
||||||
|
Line = line,
|
||||||
|
Column = column,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (persistMode == Markup.MarkupConstantPersistMode.Binary)
|
||||||
{
|
{
|
||||||
byte[] constantBytes;
|
byte[] constantBytes;
|
||||||
var byteParts = contentResult.Value.Split(',');
|
var byteParts = contentResult.Value.Split(',');
|
||||||
@@ -156,6 +178,10 @@ partial class Lexer
|
|||||||
Column = column,
|
Column = column,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Result.Failure<IDirective>(input, "Invalid constant directive", [$"'{persistModeResult.Value}' is not a valid persist mode."]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using Microsoft.Iris.Markup;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.Iris.Asm.Models;
|
||||||
|
|
||||||
|
public record ConstantDirective : Directive
|
||||||
|
{
|
||||||
|
public ConstantDirective(string name, QualifiedTypeName typeName, string constructor) : base("constant")
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
TypeName = typeName;
|
||||||
|
Constructor = constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public QualifiedTypeName TypeName { get; }
|
||||||
|
public string Constructor { get; }
|
||||||
|
public MarkupConstantPersistMode PersistMode { get; init; }
|
||||||
|
|
||||||
|
public override string ToString() => $"{base.ToString()} {Name} = {Constructor}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public record StringEncodedConstantDirective : ConstantDirective
|
||||||
|
{
|
||||||
|
public StringEncodedConstantDirective(string name, QualifiedTypeName typeName, string content)
|
||||||
|
: base(name, typeName, $"{typeName}.str({content})")
|
||||||
|
{
|
||||||
|
Content = content;
|
||||||
|
PersistMode = MarkupConstantPersistMode.FromString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Content { get; }
|
||||||
|
|
||||||
|
public override string ToString() => base.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record BinaryEncodedConstantDirective : ConstantDirective
|
||||||
|
{
|
||||||
|
public BinaryEncodedConstantDirective(string name, QualifiedTypeName typeName, byte[] content)
|
||||||
|
: base(name, typeName, GetConstructor(typeName, content))
|
||||||
|
{
|
||||||
|
Content = content;
|
||||||
|
PersistMode = MarkupConstantPersistMode.Binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Content { get; }
|
||||||
|
|
||||||
|
public override string ToString() => base.ToString();
|
||||||
|
|
||||||
|
private static string GetConstructor(QualifiedTypeName typeName, byte[] content)
|
||||||
|
{
|
||||||
|
string contentStr;
|
||||||
|
if (content.Length == 1)
|
||||||
|
contentStr = $"0x{content[0]:X2}";
|
||||||
|
else if (content.Length == 2)
|
||||||
|
contentStr = $"0x{BitConverter.ToUInt16(content, 0):X4}";
|
||||||
|
else if (content.Length == 4)
|
||||||
|
contentStr = $"0x{BitConverter.ToUInt32(content, 0):X8}";
|
||||||
|
else
|
||||||
|
contentStr = string.Join(", ", content.Select(b => $"0x{b:X2}"));
|
||||||
|
|
||||||
|
return $"{typeName}.bin({contentStr})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record CanonicalInstanceConstantDirective : ConstantDirective
|
||||||
|
{
|
||||||
|
public CanonicalInstanceConstantDirective(string name, QualifiedTypeName typeName, string canonicalName)
|
||||||
|
: base(name, typeName, $"{typeName}.can({canonicalName})")
|
||||||
|
{
|
||||||
|
CanonicalName = canonicalName;
|
||||||
|
PersistMode = MarkupConstantPersistMode.Canonical;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CanonicalName { get; }
|
||||||
|
|
||||||
|
public override string ToString() => base.ToString();
|
||||||
|
}
|
||||||
@@ -15,63 +15,6 @@ public record SectionDirective : Directive
|
|||||||
public override string ToString() => $"{base.ToString()} {Name}";
|
public override string ToString() => $"{base.ToString()} {Name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ConstantDirective : Directive
|
|
||||||
{
|
|
||||||
public ConstantDirective(string name, QualifiedTypeName typeName, string constructor) : base("constant")
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
TypeName = typeName;
|
|
||||||
Constructor = constructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
public QualifiedTypeName TypeName { get; }
|
|
||||||
public string Constructor { get; }
|
|
||||||
|
|
||||||
public override string ToString() => $"{base.ToString()} {Name} = {Constructor}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public record StringEncodedConstantDirective : ConstantDirective
|
|
||||||
{
|
|
||||||
public StringEncodedConstantDirective(string name, QualifiedTypeName typeName, string content)
|
|
||||||
: base(name, typeName, $"{typeName}({content})")
|
|
||||||
{
|
|
||||||
Content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Content { get; }
|
|
||||||
|
|
||||||
public override string ToString() => base.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public record BinaryEncodedConstantDirective : ConstantDirective
|
|
||||||
{
|
|
||||||
public BinaryEncodedConstantDirective(string name, QualifiedTypeName typeName, byte[] content)
|
|
||||||
: base(name, typeName, GetConstructor(typeName, content))
|
|
||||||
{
|
|
||||||
Content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] Content { get; }
|
|
||||||
|
|
||||||
public override string ToString() => base.ToString();
|
|
||||||
|
|
||||||
private static string GetConstructor(QualifiedTypeName typeName, byte[] content)
|
|
||||||
{
|
|
||||||
string contentStr;
|
|
||||||
if (content.Length == 1)
|
|
||||||
contentStr = $"0x{content[0]:X2}";
|
|
||||||
else if (content.Length == 2)
|
|
||||||
contentStr = $"0x{BitConverter.ToUInt16(content, 0):X4}";
|
|
||||||
else if (content.Length == 4)
|
|
||||||
contentStr = $"0x{BitConverter.ToUInt32(content, 0):X8}";
|
|
||||||
else
|
|
||||||
contentStr = string.Join(", ", content.Select(b => $"0x{b:X2}"));
|
|
||||||
|
|
||||||
return $"{typeName}.bin({contentStr})";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record ExportDirective : Directive
|
public record ExportDirective : Directive
|
||||||
{
|
{
|
||||||
public ExportDirective(string labelPrefix, uint listenerCount, string baseTypeName) : base("export")
|
public ExportDirective(string labelPrefix, uint listenerCount, string baseTypeName) : base("export")
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ public class Assembly(ITestOutputHelper output)
|
|||||||
.import-mbrs UI{Locals, Content}
|
.import-mbrs UI{Locals, Content}
|
||||||
.import-mbrs Text{Color, Content, Font}
|
.import-mbrs Text{Color, Content, Font}
|
||||||
|
|
||||||
.constant const0 = Color(255, 255, 0, 0)
|
.constant const0 = Color.str(255, 255, 0, 0)
|
||||||
.constant const1 = String(Howdy from Microsoft.Iris!)
|
.constant const1 = String.str(Howdy from Microsoft.Iris!)
|
||||||
.constant const2 = String(SelectCommand)
|
.constant const2 = String.str(SelectCommand)
|
||||||
.constant const3 = Color(255, 0, 0, 255)
|
.constant const3 = Color.str(255, 0, 0, 255)
|
||||||
.constant const4 = Font(JetBrains Mono)
|
.constant const4 = Font.str(JetBrains Mono)
|
||||||
.constant const5 = String(This is some blue text)
|
.constant const5 = String.str(This is some blue text)
|
||||||
.section object
|
.section object
|
||||||
|
|
||||||
Default_cont:
|
Default_cont:
|
||||||
|
|||||||
Reference in New Issue
Block a user