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 Dictionary<string, string> _importedUris;
|
||||
private readonly Dictionary<uint, List<Label>> _offsetLabelMap = new();
|
||||
private static readonly TypeSchema _stringTypeSchema = UIXTypes.MapIDToType(UIXTypeID.String);
|
||||
|
||||
private Disassembler(MarkupLoadResult loadResult)
|
||||
{
|
||||
@@ -224,70 +225,10 @@ public class Disassembler
|
||||
}
|
||||
}
|
||||
|
||||
var stringTypeSchema = UIXTypes.MapIDToType(UIXTypeID.String);
|
||||
|
||||
foreach (var (c, typeSchema, constantValue) in constants)
|
||||
{
|
||||
var constantName = $"const{c:D}";
|
||||
QualifiedTypeName qualifiedTypeName = GetQualifiedName(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);
|
||||
}
|
||||
yield return EncodeConstant(constantValue, constantName, typeSchema);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +317,78 @@ public class Disassembler
|
||||
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)
|
||||
{
|
||||
return value is IStringEncodable encodable
|
||||
|
||||
@@ -87,9 +87,23 @@ partial class Lexer
|
||||
if (!typeNameResult.WasSuccessful)
|
||||
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]);
|
||||
|
||||
var binaryEncodingMarkerResult = Parse.String(".bin")(input);
|
||||
input = binaryEncodingMarkerResult.Remainder;
|
||||
var binaryEncoded = binaryEncodingMarkerResult.WasSuccessful;
|
||||
var encodingMarkerResult = Parse.Char('.')(input);
|
||||
input = encodingMarkerResult.Remainder;
|
||||
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);
|
||||
input = openBracketResult.Remainder;
|
||||
@@ -109,7 +123,7 @@ partial class Lexer
|
||||
var constantName = constNameResult.Value;
|
||||
var typeName = typeNameResult.Value;
|
||||
|
||||
if (!binaryEncoded)
|
||||
if (persistMode == Markup.MarkupConstantPersistMode.FromString)
|
||||
{
|
||||
directive = new StringEncodedConstantDirective(constantName, typeName, contentResult.Value)
|
||||
{
|
||||
@@ -117,7 +131,15 @@ partial class Lexer
|
||||
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;
|
||||
var byteParts = contentResult.Value.Split(',');
|
||||
@@ -156,6 +178,10 @@ partial class Lexer
|
||||
Column = column,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result.Failure<IDirective>(input, "Invalid constant directive", [$"'{persistModeResult.Value}' is not a valid persist mode."]);
|
||||
}
|
||||
}
|
||||
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 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 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 Text{Color, Content, Font}
|
||||
|
||||
.constant const0 = Color(255, 255, 0, 0)
|
||||
.constant const1 = String(Howdy from Microsoft.Iris!)
|
||||
.constant const2 = String(SelectCommand)
|
||||
.constant const3 = Color(255, 0, 0, 255)
|
||||
.constant const4 = Font(JetBrains Mono)
|
||||
.constant const5 = String(This is some blue text)
|
||||
.constant const0 = Color.str(255, 255, 0, 0)
|
||||
.constant const1 = String.str(Howdy from Microsoft.Iris!)
|
||||
.constant const2 = String.str(SelectCommand)
|
||||
.constant const3 = Color.str(255, 0, 0, 255)
|
||||
.constant const4 = Font.str(JetBrains Mono)
|
||||
.constant const5 = String.str(This is some blue text)
|
||||
.section object
|
||||
|
||||
Default_cont:
|
||||
|
||||
Reference in New Issue
Block a user