fix(json): iterate sce::Json::Array values (#1087)
This commit is contained in:
parent
28c922c7a5
commit
b307b2f8fd
|
|
@ -29,6 +29,18 @@ public static class JsonExports
|
|||
private static readonly ConcurrentDictionary<ulong, JsonStringState> _strings = new();
|
||||
private static readonly JsonElement _nullElement = CreateNullElement();
|
||||
|
||||
// sce::Json::Array is reached through Value::getArray and walked with an 8-byte iterator. The
|
||||
// elements are laid out once as consecutive Value objects in guest memory; the Array object
|
||||
// holds their base and count, and an iterator holds the address of its element, so stepping
|
||||
// adds ValueObjectSize and dereferencing returns the element itself.
|
||||
private sealed record JsonArrayState(ulong Elements, int Count);
|
||||
|
||||
private const int ArrayObjectSize = 0x10;
|
||||
private static readonly ConcurrentDictionary<ulong, JsonArrayState> _arrays = new();
|
||||
|
||||
// The Array built for a parsed value, reused while that value is unchanged.
|
||||
private static readonly ConcurrentDictionary<ulong, (JsonValueState Source, ulong Array)> _valueArrays = new();
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "-hJRce8wn1U",
|
||||
ExportName = "_ZN3sce4Json12MemAllocatorC2Ev",
|
||||
|
|
@ -598,6 +610,289 @@ public static class JsonExports
|
|||
return 0;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "ONT8As5R1ug",
|
||||
ExportName = "_ZNK3sce4Json5Value8getArrayEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ValueGetArray(CpuContext ctx)
|
||||
{
|
||||
var valueAddress = ctx[CpuRegister.Rdi];
|
||||
JsonValueState? source = null;
|
||||
var hasParsed = !JsonObjectHeap.Values.ContainsKey(valueAddress) &&
|
||||
_values.TryGetValue(valueAddress, out source);
|
||||
if (hasParsed &&
|
||||
_valueArrays.TryGetValue(valueAddress, out var cached) &&
|
||||
ReferenceEquals(cached.Source, source))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = cached.Array;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A value that is not an array reads as an empty one.
|
||||
var element = GetValue(valueAddress);
|
||||
var count = element.ValueKind == System.Text.Json.JsonValueKind.Array ? element.GetArrayLength() : 0;
|
||||
if (!TryAllocateGuestObject(ctx, ArrayObjectSize + (count * ValueObjectSize), out var arrayAddress))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
var elements = arrayAddress + ArrayObjectSize;
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
StoreValue(ctx, elements + (ulong)(index * ValueObjectSize), element[index]);
|
||||
}
|
||||
|
||||
_arrays[arrayAddress] = new JsonArrayState(elements, count);
|
||||
ctx.TryWriteUInt64(arrayAddress, elements);
|
||||
ctx.TryWriteUInt64(arrayAddress + 8, (ulong)count);
|
||||
if (hasParsed)
|
||||
{
|
||||
_valueArrays[valueAddress] = (source!, arrayAddress);
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = arrayAddress;
|
||||
TraceJson("Value.getArray", valueAddress, (ulong)count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "JP-PtKMiI1E",
|
||||
ExportName = "_ZN3sce4Json5ArrayC1Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayConstructor(CpuContext ctx) => ConstructArray(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "t6AHhkWu4mY",
|
||||
ExportName = "_ZN3sce4Json5ArrayC2Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayBaseConstructor(CpuContext ctx) => ConstructArray(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "HJ8GpRT1aiw",
|
||||
ExportName = "_ZN3sce4Json5ArrayD1Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayDestructor(CpuContext ctx) => DestroyArray(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "MIyMPw7YTGM",
|
||||
ExportName = "_ZN3sce4Json5ArrayD2Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayBaseDestructor(CpuContext ctx) => DestroyArray(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "rQGJeNjOuUk",
|
||||
ExportName = "_ZNK3sce4Json5Array4sizeEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArraySize(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = (ulong)GetArray(ctx[CpuRegister.Rdi]).Count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9uP25i6ipno",
|
||||
ExportName = "_ZNK3sce4Json5Array5emptyEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayEmpty(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = GetArray(ctx[CpuRegister.Rdi]).Count == 0 ? 1UL : 0UL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// begin() and end() return the iterator through the hidden result pointer in RDI; the Array
|
||||
// is in RSI.
|
||||
[SysAbiExport(
|
||||
Nid = "bcH5EnFE2xY",
|
||||
ExportName = "_ZNK3sce4Json5Array5beginEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayBegin(CpuContext ctx) =>
|
||||
ReturnIterator(ctx, GetArray(ctx[CpuRegister.Rsi]).Elements);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "WXF2ihRF+B8",
|
||||
ExportName = "_ZNK3sce4Json5Array3endEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int ArrayEnd(CpuContext ctx)
|
||||
{
|
||||
var array = GetArray(ctx[CpuRegister.Rsi]);
|
||||
return ReturnIterator(ctx, array.Elements + (ulong)(array.Count * ValueObjectSize));
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "TAPTWYaYf8Q",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorC1Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorConstructor(CpuContext ctx) => SetIterator(ctx, ctx[CpuRegister.Rdi], 0);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "JSg1MSo7NKE",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorC2Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorBaseConstructor(CpuContext ctx) => SetIterator(ctx, ctx[CpuRegister.Rdi], 0);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "IaIvX3kgyb0",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorC1ERKS2_",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorCopyConstructor(CpuContext ctx) => CopyIterator(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "Hdc3DSIsG2I",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorC2ERKS2_",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorBaseCopyConstructor(CpuContext ctx) => CopyIterator(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "T3WReElX4BY",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratoraSERKS2_",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorAssignment(CpuContext ctx) => CopyIterator(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9yLjn46Ypfs",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorD1Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorDestructor(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9niNrXsF3Bc",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorD2Ev",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorBaseDestructor(CpuContext ctx) => IteratorDestructor(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "5AZPp99ogrc",
|
||||
ExportName = "_ZNK3sce4Json5Array8iteratorneERKS2_",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorNotEqual(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rax] =
|
||||
ReadIterator(ctx, ctx[CpuRegister.Rdi]) != ReadIterator(ctx, ctx[CpuRegister.Rsi]) ? 1UL : 0UL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "wcgr5mte7T8",
|
||||
ExportName = "_ZNK3sce4Json5Array8iteratordeEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorDereference(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = ReadIterator(ctx, ctx[CpuRegister.Rdi]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "iAIYn4oAWvI",
|
||||
ExportName = "_ZNK3sce4Json5Array8iteratorptEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorArrow(CpuContext ctx) => IteratorDereference(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "w5+VCznos5E",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorppEv",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorPreIncrement(CpuContext ctx)
|
||||
{
|
||||
var iterator = ctx[CpuRegister.Rdi];
|
||||
return SetIterator(ctx, iterator, ReadIterator(ctx, iterator) + ValueObjectSize);
|
||||
}
|
||||
|
||||
// Postfix ++ returns the old position through the hidden result pointer in RDI; the iterator
|
||||
// is in RSI.
|
||||
[SysAbiExport(
|
||||
Nid = "GEylwSsiGG8",
|
||||
ExportName = "_ZN3sce4Json5Array8iteratorppEi",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorPostIncrement(CpuContext ctx)
|
||||
{
|
||||
var iterator = ctx[CpuRegister.Rsi];
|
||||
var position = ReadIterator(ctx, iterator);
|
||||
ctx.TryWriteUInt64(iterator, position + ValueObjectSize);
|
||||
return ReturnIterator(ctx, position);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "43MESLndGvQ",
|
||||
ExportName = "_ZN3sce4Json5Array8iterator7advanceEm",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceJson")]
|
||||
public static int IteratorAdvance(CpuContext ctx)
|
||||
{
|
||||
var iterator = ctx[CpuRegister.Rdi];
|
||||
var step = ctx[CpuRegister.Rsi] * ValueObjectSize;
|
||||
return SetIterator(ctx, iterator, ReadIterator(ctx, iterator) + step);
|
||||
}
|
||||
|
||||
private static int ConstructArray(CpuContext ctx)
|
||||
{
|
||||
var thisAddress = ctx[CpuRegister.Rdi];
|
||||
if (thisAddress != 0)
|
||||
{
|
||||
_arrays[thisAddress] = new JsonArrayState(0, 0);
|
||||
ctx.TryWriteUInt64(thisAddress, 0);
|
||||
ctx.TryWriteUInt64(thisAddress + 8, 0);
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = thisAddress;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int DestroyArray(CpuContext ctx)
|
||||
{
|
||||
_arrays.TryRemove(ctx[CpuRegister.Rdi], out _);
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static JsonArrayState GetArray(ulong address) =>
|
||||
_arrays.TryGetValue(address, out var array) ? array : new JsonArrayState(0, 0);
|
||||
|
||||
private static ulong ReadIterator(CpuContext ctx, ulong iterator) =>
|
||||
iterator != 0 && ctx.TryReadUInt64(iterator, out var position) ? position : 0;
|
||||
|
||||
private static int SetIterator(CpuContext ctx, ulong iterator, ulong position)
|
||||
{
|
||||
if (iterator != 0)
|
||||
{
|
||||
ctx.TryWriteUInt64(iterator, position);
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = iterator;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int CopyIterator(CpuContext ctx) =>
|
||||
SetIterator(ctx, ctx[CpuRegister.Rdi], ReadIterator(ctx, ctx[CpuRegister.Rsi]));
|
||||
|
||||
private static int ReturnIterator(CpuContext ctx, ulong position) =>
|
||||
SetIterator(ctx, ctx[CpuRegister.Rdi], position);
|
||||
|
||||
private static int ConstructValue(CpuContext ctx)
|
||||
{
|
||||
var thisAddress = ctx[CpuRegister.Rdi];
|
||||
|
|
@ -642,6 +937,7 @@ public static class JsonExports
|
|||
{
|
||||
var thisAddress = ctx[CpuRegister.Rdi];
|
||||
_values.TryRemove(thisAddress, out _);
|
||||
_valueArrays.TryRemove(thisAddress, out _);
|
||||
JsonObjectHeap.RemoveValue(thisAddress);
|
||||
if (thisAddress != 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -165,4 +165,167 @@ public sealed class JsonExportRegistrationTests
|
|||
Assert.Equal(userDataAddress, BitConverter.ToUInt64(parameter, 8));
|
||||
Assert.All(parameter[0x10..], value => Assert.Equal(0, value));
|
||||
}
|
||||
|
||||
private const ulong ArrayMemoryBase = 0x1_0000_0000;
|
||||
private const ulong ArrayRoot = ArrayMemoryBase;
|
||||
private const ulong ArrayText = ArrayMemoryBase + 0x100;
|
||||
private const ulong ArrayKey = ArrayMemoryBase + 0x300;
|
||||
private const ulong ArrayBeginIterator = ArrayMemoryBase + 0x400;
|
||||
private const ulong ArrayEndIterator = ArrayMemoryBase + 0x408;
|
||||
|
||||
private static (CpuContext Context, FakeCpuMemory Memory) Parse(string json)
|
||||
{
|
||||
JsonObjectHeap.ResetForTests();
|
||||
var memory = new FakeCpuMemory(ArrayMemoryBase, 0x10000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
ctx[CpuRegister.Rdi] = ArrayRoot;
|
||||
JsonExports.ValueConstructor(ctx);
|
||||
memory.WriteCString(ArrayText, json);
|
||||
ctx[CpuRegister.Rdi] = ArrayRoot;
|
||||
ctx[CpuRegister.Rsi] = ArrayText;
|
||||
ctx[CpuRegister.Rdx] = (ulong)json.Length;
|
||||
Assert.Equal(0, JsonExports.ParserParseBuffer(ctx));
|
||||
return (ctx, memory);
|
||||
}
|
||||
|
||||
private static ulong Member(CpuContext ctx, FakeCpuMemory memory, string name)
|
||||
{
|
||||
memory.WriteCString(ArrayKey, name);
|
||||
ctx[CpuRegister.Rdi] = ArrayRoot;
|
||||
ctx[CpuRegister.Rsi] = ArrayKey;
|
||||
JsonExports.ValueIndexCString(ctx);
|
||||
return ctx[CpuRegister.Rax];
|
||||
}
|
||||
|
||||
private static ulong GetArray(CpuContext ctx, ulong value)
|
||||
{
|
||||
ctx[CpuRegister.Rdi] = value;
|
||||
JsonExports.ValueGetArray(ctx);
|
||||
return ctx[CpuRegister.Rax];
|
||||
}
|
||||
|
||||
private static void Range(CpuContext ctx, ulong array)
|
||||
{
|
||||
ctx[CpuRegister.Rdi] = ArrayBeginIterator;
|
||||
ctx[CpuRegister.Rsi] = array;
|
||||
JsonExports.ArrayBegin(ctx);
|
||||
Assert.Equal(ArrayBeginIterator, ctx[CpuRegister.Rax]);
|
||||
ctx[CpuRegister.Rdi] = ArrayEndIterator;
|
||||
ctx[CpuRegister.Rsi] = array;
|
||||
JsonExports.ArrayEnd(ctx);
|
||||
Assert.Equal(ArrayEndIterator, ctx[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
private static bool NotAtEnd(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rdi] = ArrayBeginIterator;
|
||||
ctx[CpuRegister.Rsi] = ArrayEndIterator;
|
||||
JsonExports.IteratorNotEqual(ctx);
|
||||
return ctx[CpuRegister.Rax] != 0;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnArrayOfRealsIsWalkedFromBeginToEnd()
|
||||
{
|
||||
var (ctx, memory) = Parse("{\"scale\":[1.5,-2.25,3.0]}");
|
||||
var array = GetArray(ctx, Member(ctx, memory, "scale"));
|
||||
Range(ctx, array);
|
||||
|
||||
var reals = new List<double>();
|
||||
while (NotAtEnd(ctx))
|
||||
{
|
||||
ctx[CpuRegister.Rdi] = ArrayBeginIterator;
|
||||
JsonExports.IteratorDereference(ctx);
|
||||
var element = ctx[CpuRegister.Rax];
|
||||
|
||||
ctx[CpuRegister.Rdi] = element;
|
||||
JsonExports.ValueGetType(ctx);
|
||||
Assert.Equal(4UL, ctx[CpuRegister.Rax]);
|
||||
|
||||
ctx[CpuRegister.Rdi] = element;
|
||||
JsonExports.ValueGetReal(ctx);
|
||||
Assert.True(ctx.TryReadUInt64(ctx[CpuRegister.Rax], out var bits));
|
||||
reals.Add(BitConverter.Int64BitsToDouble(unchecked((long)bits)));
|
||||
|
||||
ctx[CpuRegister.Rdi] = ArrayBeginIterator;
|
||||
JsonExports.IteratorPreIncrement(ctx);
|
||||
Assert.Equal(ArrayBeginIterator, ctx[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
Assert.Equal([1.5, -2.25, 3.0], reals);
|
||||
ctx[CpuRegister.Rdi] = array;
|
||||
JsonExports.ArraySize(ctx);
|
||||
Assert.Equal(3UL, ctx[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadingTheSameValueAgainReusesItsArray()
|
||||
{
|
||||
var (ctx, memory) = Parse("{\"list\":[1,2]}");
|
||||
var member = Member(ctx, memory, "list");
|
||||
|
||||
Assert.Equal(GetArray(ctx, member), GetArray(ctx, member));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AValueThatIsNotAnArrayReadsAsEmpty()
|
||||
{
|
||||
var (ctx, memory) = Parse("{\"number\":7}");
|
||||
var array = GetArray(ctx, Member(ctx, memory, "number"));
|
||||
Assert.NotEqual(0UL, array);
|
||||
Range(ctx, array);
|
||||
|
||||
Assert.False(NotAtEnd(ctx));
|
||||
ctx[CpuRegister.Rdi] = array;
|
||||
JsonExports.ArrayEmpty(ctx);
|
||||
Assert.Equal(1UL, ctx[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PostfixIncrementAndCopiesKeepTheirOwnPosition()
|
||||
{
|
||||
const ulong old = ArrayMemoryBase + 0x410;
|
||||
const ulong copy = ArrayMemoryBase + 0x418;
|
||||
var (ctx, memory) = Parse("{\"list\":[10,20,30]}");
|
||||
Range(ctx, GetArray(ctx, Member(ctx, memory, "list")));
|
||||
Assert.True(ctx.TryReadUInt64(ArrayBeginIterator, out var first));
|
||||
|
||||
ctx[CpuRegister.Rdi] = copy;
|
||||
ctx[CpuRegister.Rsi] = ArrayBeginIterator;
|
||||
JsonExports.IteratorCopyConstructor(ctx);
|
||||
ctx[CpuRegister.Rdi] = old;
|
||||
ctx[CpuRegister.Rsi] = ArrayBeginIterator;
|
||||
ctx[CpuRegister.Rdx] = 0;
|
||||
JsonExports.IteratorPostIncrement(ctx);
|
||||
|
||||
Assert.Equal(old, ctx[CpuRegister.Rax]);
|
||||
Assert.True(ctx.TryReadUInt64(old, out var oldPosition));
|
||||
Assert.True(ctx.TryReadUInt64(copy, out var copyPosition));
|
||||
Assert.True(ctx.TryReadUInt64(ArrayBeginIterator, out var advanced));
|
||||
Assert.Equal(first, oldPosition);
|
||||
Assert.Equal(first, copyPosition);
|
||||
Assert.Equal(first + 0x20, advanced);
|
||||
|
||||
ctx[CpuRegister.Rdi] = ArrayBeginIterator;
|
||||
ctx[CpuRegister.Rsi] = 2;
|
||||
JsonExports.IteratorAdvance(ctx);
|
||||
Assert.False(NotAtEnd(ctx));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ONT8As5R1ug", "_ZNK3sce4Json5Value8getArrayEv")]
|
||||
[InlineData("bcH5EnFE2xY", "_ZNK3sce4Json5Array5beginEv")]
|
||||
[InlineData("WXF2ihRF+B8", "_ZNK3sce4Json5Array3endEv")]
|
||||
[InlineData("5AZPp99ogrc", "_ZNK3sce4Json5Array8iteratorneERKS2_")]
|
||||
[InlineData("wcgr5mte7T8", "_ZNK3sce4Json5Array8iteratordeEv")]
|
||||
[InlineData("w5+VCznos5E", "_ZN3sce4Json5Array8iteratorppEv")]
|
||||
[InlineData("9yLjn46Ypfs", "_ZN3sce4Json5Array8iteratorD1Ev")]
|
||||
public void ArrayExportsAreRegistered(string nid, string name)
|
||||
{
|
||||
var manager = CreateRegisteredManager();
|
||||
|
||||
Assert.True(manager.TryGetExport(nid, out var export));
|
||||
Assert.Equal(name, export.Name);
|
||||
Assert.Equal("libSceJson", export.LibraryName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue