Allow defining abstract Results

This commit is contained in:
Alex Barney 2021-01-07 20:42:17 -07:00
parent 7b38708c11
commit 9eeed9c225
4 changed files with 1430 additions and 1327 deletions

View file

@ -131,6 +131,7 @@ namespace LibHacBuild.CodeGen.Stage1
foreach (ModuleInfo module in modules)
{
var descriptionSet = new HashSet<int>();
var descriptionSetAbstract = new HashSet<int>();
if (!moduleIndexSet.Add(module.Index))
{
@ -144,9 +145,21 @@ namespace LibHacBuild.CodeGen.Stage1
foreach (ResultInfo result in module.Results)
{
if (!descriptionSet.Add(result.DescriptionStart))
if (result.IsAbstract)
{
throw new InvalidDataException($"Duplicate result {result.Module}-{result.DescriptionStart}-{result.DescriptionEnd}.");
if (!descriptionSetAbstract.Add(result.DescriptionStart))
{
throw new InvalidDataException(
$"Duplicate abstract result {result.Module}-{result.DescriptionStart}-{result.DescriptionEnd}.");
}
}
else
{
if (!descriptionSet.Add(result.DescriptionStart))
{
throw new InvalidDataException(
$"Duplicate result {result.Module}-{result.DescriptionStart}-{result.DescriptionEnd}.");
}
}
}
}
@ -263,8 +276,10 @@ namespace LibHacBuild.CodeGen.Stage1
sb.AppendLine(GetXmlDoc(result));
string resultCtor = $"new Result.Base(Module{moduleName}, {descriptionArgs});";
sb.Append($"public static Result.Base {result.Name} ");
string type = result.IsAbstract ? "Result.Base.Abstract" : "Result.Base";
string resultCtor = $"new {type}(Module{moduleName}, {descriptionArgs});";
sb.Append($"public static {type} {result.Name} ");
if (EstimateCilSize(result) > InlineThreshold)
{
@ -292,7 +307,11 @@ namespace LibHacBuild.CodeGen.Stage1
doc += $"; Range: {result.DescriptionStart}-{result.DescriptionEnd}";
}
doc += $"; Inner value: 0x{result.InnerValue:x}";
if (!result.IsAbstract)
{
doc += $"; Inner value: 0x{result.InnerValue:x}";
}
doc += "</summary>";
return doc;
@ -473,6 +492,7 @@ namespace LibHacBuild.CodeGen.Stage1
element.Module = (short)result.Module;
element.DescriptionStart = (short)result.DescriptionStart;
element.DescriptionEnd = (short)result.DescriptionEnd;
element.IsAbstract = result.IsAbstract;
Span<byte> utf8Name = Encoding.UTF8.GetBytes(result.FullName);
utf8Name.CopyTo(nameTable.Slice(curNameOffset));
@ -515,6 +535,7 @@ namespace LibHacBuild.CodeGen.Stage1
public short Module;
public short DescriptionStart;
public short DescriptionEnd;
public bool IsAbstract;
}
// ReSharper restore NotAccessedField.Local
}
@ -550,6 +571,7 @@ namespace LibHacBuild.CodeGen.Stage1
public int Module { get; set; }
public int DescriptionStart { get; set; }
public int DescriptionEnd { get; set; }
public ResultInfoFlags Flags { get; set; }
public string Name { get; set; }
public string FullName { get; set; }
public string Summary { get; set; }
@ -557,6 +579,14 @@ namespace LibHacBuild.CodeGen.Stage1
public bool IsRange => DescriptionStart != DescriptionEnd;
public string ErrorCode => $"{2000 + Module:d4}-{DescriptionStart:d4}";
public int InnerValue => Module & 0x1ff | ((DescriptionStart & 0x7ffff) << 9);
public bool IsAbstract => Flags.HasFlag(ResultInfoFlags.Abstract);
}
[Flags]
public enum ResultInfoFlags
{
None = 0,
Abstract = 1 << 0
}
public sealed class ResultMap : ClassMap<ResultInfo>
@ -575,6 +605,27 @@ namespace LibHacBuild.CodeGen.Stage1
return int.Parse(field);
});
Map(m => m.Flags).ConvertUsing(row =>
{
string field = row.GetField("Flags");
var flags = ResultInfoFlags.None;
foreach (char c in field)
{
switch (c)
{
case 'a':
flags |= ResultInfoFlags.Abstract;
break;
default:
throw new InvalidDataException($"Invalid Result flag '{c}'");
}
}
return flags;
});
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -311,6 +311,54 @@ namespace LibHac
{
return ((uint)value & ~(~default(ulong) << bitsCount)) << bitsOffset;
}
/// <summary>
/// A <see cref="Base"/> that can't be used as a <see cref="Result"/> itself, but can check if its
/// range includes other <see cref="Result"/>s.
/// </summary>
public readonly struct Abstract
{
private readonly ulong _value;
public Abstract(int module, int description) : this(module, description, description) { }
public Abstract(int module, int descriptionStart, int descriptionEnd)
{
Debug.Assert(ModuleBegin <= module && module < ModuleEnd, "Invalid Module");
Debug.Assert(DescriptionBegin <= descriptionStart && descriptionStart < DescriptionEnd, "Invalid Description Start");
Debug.Assert(DescriptionBegin <= descriptionEnd && descriptionEnd < DescriptionEnd, "Invalid Description End");
Debug.Assert(descriptionStart <= descriptionEnd, "descriptionStart must be <= descriptionEnd");
_value = SetBitsValueLong(module, ModuleBitsOffset, ModuleBitsCount) |
SetBitsValueLong(descriptionStart, DescriptionBitsOffset, DescriptionBitsCount) |
SetBitsValueLong(descriptionEnd, DescriptionEndBitsOffset, DescriptionBitsCount);
}
public BaseType Module => GetBitsValueLong(_value, ModuleBitsOffset, ModuleBitsCount);
public BaseType DescriptionRangeStart => GetBitsValueLong(_value, DescriptionBitsOffset, DescriptionBitsCount);
public BaseType DescriptionRangeEnd => GetBitsValueLong(_value, DescriptionEndBitsOffset, DescriptionBitsCount);
private Result Value => new Result((BaseType)_value);
/// <summary>
/// Checks if the range of this <see cref="Result.Base"/> includes the provided <see cref="Result"/>.
/// </summary>
/// <param name="result">The <see cref="Result"/> to check.</param>
/// <returns><see langword="true"/> if the range includes <paramref name="result"/>. Otherwise, <see langword="false"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Includes(Result result)
{
// 99% of the time the values in this struct will be constants.
// This check allows the compiler to optimize this method down to a simple comparison when possible.
if (DescriptionRangeStart == DescriptionRangeEnd)
{
return result.Value == Value.Value;
}
return result.Module == Module &&
result.Description - DescriptionRangeStart <= DescriptionRangeEnd - DescriptionRangeStart;
}
}
}
public interface IResultLogger

View file

@ -57,6 +57,9 @@ namespace LibHac
foreach (ref readonly Element element in elements)
{
if (element.IsAbstract)
continue;
var result = new Result(element.Module, element.DescriptionStart);
if (!dict.TryAdd(result, GetName(element.NameOffset).ToString()))
@ -89,6 +92,7 @@ namespace LibHac
public short Module;
public short DescriptionStart;
public short DescriptionEnd;
public bool IsAbstract;
}
#pragma warning restore 649
}