From 2eb6a2a0d4310aa0faf717789d80e85dfa5e08a1 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 19 Nov 2024 18:44:57 -0800 Subject: [PATCH] [cdac] Simplify usage of TestPlaceholderTarget in tests (#109873) - Make `TestPlaceholderTarget` (mock target) constructor take type infos and global values - Handle reading of global values in mock target implementation - Use Moq for creating a `ContractRegistry` instead of our own explicit implementation - Remove subclasses of `TestPlaceholderTarget` --- .../cdacreader/tests/CodeVersionsTests.cs | 64 ++++++------ .../ExecutionManagerTestBuilder.cs | 23 +++-- .../ExecutionManager/ExecutionManagerTests.cs | 95 ++++-------------- .../tests/ExecutionManager/HashMapTests.cs | 32 +----- .../tests/ExecutionManager/NibbleMapTests.cs | 18 ++-- .../ExecutionManager/RangeSectionMapTests.cs | 17 +--- .../MockDescriptors.CodeVersions.cs | 2 - .../MockDescriptors.HashMap.cs | 41 ++++---- .../cdacreader/tests/MockMemorySpace.cs | 11 +-- .../cdacreader/tests/PrecodeStubsTests.cs | 83 ++++++---------- .../cdacreader/tests/TargetTestHelpers.cs | 3 + .../cdacreader/tests/TestPlaceholderTarget.cs | 97 ++++++++----------- 12 files changed, 168 insertions(+), 318 deletions(-) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index a7628695731433..69e39c597feda8 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Moq; using Xunit; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -226,43 +227,36 @@ TargetPointer ILoader.GetModuleLookupMapElement(TargetPointer tableAddress, uint } } - internal class CVTestTarget : TestPlaceholderTarget + internal static Target CreateTarget( + MockTarget.Architecture arch, + IReadOnlyCollection methodDescs = null, + IReadOnlyCollection methodTables = null, + IReadOnlyCollection codeBlocks = null, + IReadOnlyCollection modules = null, + MockCodeVersions builder = null) { - public static CVTestTarget FromBuilder(MockTarget.Architecture arch, IReadOnlyCollection methodDescs, IReadOnlyCollection methodTables, IReadOnlyCollection codeBlocks, IReadOnlyCollection modules, MockCodeVersions builder) - { - builder.MarkCreated(); - return new CVTestTarget(arch, reader: builder.Builder.GetReadContext().ReadFromTarget, typeInfoCache: builder.Types, - methodDescs: methodDescs, methodTables: methodTables, codeBlocks: codeBlocks, modules: modules); - } - - public CVTestTarget(MockTarget.Architecture arch, IReadOnlyCollection? methodDescs = null, - IReadOnlyCollection? methodTables = null, - IReadOnlyCollection? codeBlocks = null, - IReadOnlyCollection? modules = null, - ReadFromTargetDelegate reader = null, - Dictionary? typeInfoCache = null) - : base(arch, reader) - { - IExecutionManager mockExecutionManager = new MockExecutionManager(codeBlocks ?? []); - IRuntimeTypeSystem mockRuntimeTypeSystem = new MockRuntimeTypeSystem(this, methodDescs ?? [], methodTables ?? []); - ILoader loader = new MockLoader(modules ?? []); - if (typeInfoCache != null) - SetTypeInfoCache(typeInfoCache); - IContractFactory cvfactory = new CodeVersionsFactory(); - SetContracts(new TestRegistry() { - CodeVersionsContract = new (() => cvfactory.CreateContract(this, 1)), - ExecutionManagerContract = new (() => mockExecutionManager), - RuntimeTypeSystemContract = new (() => mockRuntimeTypeSystem), - LoaderContract = new (() => loader), - }); - } + TestPlaceholderTarget target = builder != null + ? new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types) + : new TestPlaceholderTarget(arch, null); + + IExecutionManager mockExecutionManager = new MockExecutionManager(codeBlocks ?? []); + IRuntimeTypeSystem mockRuntimeTypeSystem = new MockRuntimeTypeSystem(target, methodDescs ?? [], methodTables ?? []); + ILoader loader = new MockLoader(modules ?? []); + IContractFactory cvfactory = new CodeVersionsFactory(); + ContractRegistry reg = Mock.Of( + c => c.CodeVersions == cvfactory.CreateContract(target, 1) + && c.ExecutionManager == mockExecutionManager + && c.RuntimeTypeSystem == mockRuntimeTypeSystem + && c.Loader == loader); + target.SetContracts(reg); + return target; } [Theory] [ClassData(typeof(MockTarget.StdArch))] public void GetNativeCodeVersion_Null(MockTarget.Architecture arch) { - var target = new CVTestTarget(arch); + var target = CreateTarget(arch); var codeVersions = target.Contracts.CodeVersions; Assert.NotNull(codeVersions); @@ -286,7 +280,7 @@ public void GetNativeCodeVersion_OneVersion_NonVersionable(MockTarget.Architectu MethodDesc = oneMethod, }; - var target = new CVTestTarget(arch, methodDescs: [oneMethod], codeBlocks: [oneBlock]); + var target = CreateTarget(arch, methodDescs: [oneMethod], codeBlocks: [oneBlock]); var codeVersions = target.Contracts.CodeVersions; Assert.NotNull(codeVersions); @@ -320,7 +314,7 @@ public void GetNativeCodeVersion_OneVersion_Versionable(MockTarget.Architecture }; builder.FillNativeCodeVersionNode(nativeCodeVersionNode, methodDesc: oneMethod.Address, nativeCode: codeBlockStart, next: TargetPointer.Null, isActive: false, ilVersionId: default); - var target = CVTestTarget.FromBuilder(arch, [oneMethod], [], [oneBlock], [], builder); + var target = CreateTarget(arch, [oneMethod], [], [oneBlock], [], builder); // TEST @@ -367,7 +361,7 @@ public void GetActiveNativeCodeVersion_DefaultCase(MockTarget.Architecture arch) oneMethod.MethodTable = oneMethodTable; oneMethod.RowId = methodRowId; - var target = CVTestTarget.FromBuilder(arch, [oneMethod], [oneMethodTable], [], [oneModule], builder); + var target = CreateTarget(arch, [oneMethod], [oneMethodTable], [], [oneModule], builder); // TEST @@ -430,7 +424,7 @@ private void GetActiveNativeCodeVersion_IterateVersionNodes_Impl(MockTarget.Arch methodDesc.MethodTable = methodTable; methodDesc.RowId = methodRowId; - var target = CVTestTarget.FromBuilder(arch, [methodDesc], [methodTable], [], [module], builder); + var target = CreateTarget(arch, [methodDesc], [methodTable], [], [module], builder); // TEST @@ -501,7 +495,7 @@ private void GetActiveNativeCodeVersion_ExplicitILCodeVersion_Impl(MockTarget.Ar oneMethod.MethodTable = methodTable; oneMethod.RowId = methodRowId; - var target = CVTestTarget.FromBuilder(arch, [oneMethod], [methodTable], [], [module], builder); + var target = CreateTarget(arch, [oneMethod], [methodTable], [], [module], builder); // TEST diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs index 5e2228efd80d95..67f18342b6e5cc 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; using InteriorMapValue = Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers.RangeSectionMap.InteriorMapValue; @@ -13,6 +14,7 @@ internal class ExecutionManagerTestBuilder { public const ulong ExecutionManagerCodeRangeMapAddress = 0x000a_fff0; + const bool UseFunclets = true; const int RealCodeHeaderSize = 0x08; // must be big enough for the offsets of RealCodeHeader size in ExecutionManagerTestTarget, below public struct AllocationRange @@ -159,10 +161,6 @@ public void InsertAddressRange(TargetCodePointer start, uint length, ulong value cur = new TargetCodePointer(cur.Value + (ulong)BytesAtLastLevel); // FIXME: round ? } while (cur.Value < end); } - public void MarkCreated() - { - _builder.MarkCreated(); - } public MockMemorySpace.ReadContext GetReadContext() { @@ -259,6 +257,7 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect internal MockMemorySpace.Builder Builder { get; } internal Dictionary Types { get; } + internal (string Name, ulong Value)[] Globals { get; } private readonly RangeSectionMapTestBuilder _rsmBuilder; @@ -287,11 +286,19 @@ internal ExecutionManagerTestBuilder(int version, MockMemorySpace.Builder builde CodeHeapListNodeFields, RealCodeHeaderFields, RuntimeFunctionFields, - MockDescriptors.HashMap.HashMapFields, - MockDescriptors.HashMap.BucketFields(Builder.TargetTestHelpers), ReadyToRunInfoFields(Builder.TargetTestHelpers), MockDescriptors.ModuleFields, - ]); + ]).Concat(MockDescriptors.HashMap.GetTypes(Builder.TargetTestHelpers)) + .ToDictionary(); + Globals = + [ + (nameof(Constants.Globals.ExecutionManagerCodeRangeMapAddress), ExecutionManagerCodeRangeMapAddress), + (nameof(Constants.Globals.StubCodeBlockLast), 0x0Fu), + (nameof(Constants.Globals.FeatureEHFunclets), UseFunclets ? 1 : 0), + ]; + Globals = Globals + .Concat(MockDescriptors.HashMap.GetGlobals(Builder.TargetTestHelpers)) + .ToArray(); } internal NibbleMapTestBuilderBase CreateNibbleMap(ulong codeRangeStart, uint codeRangeSize) @@ -469,6 +476,4 @@ public TargetPointer AddReadyToRunModule(TargetPointer r2rInfo) return r2rModule.Address; } - - public void MarkCreated() => Builder.MarkCreated(); } diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index 78f34095132df9..f222e0149604b4 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -11,66 +11,17 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; public class ExecutionManagerTests { - internal class ExecutionManagerTestTarget : TestPlaceholderTarget + private static Target CreateTarget(ExecutionManagerTestBuilder emBuilder) { - private readonly ulong _topRangeSectionMap; - - public static ExecutionManagerTestTarget FromBuilder(ExecutionManagerTestBuilder emBuilder) - { - var arch = emBuilder.Builder.TargetTestHelpers.Arch; - ReadFromTargetDelegate reader = emBuilder.Builder.GetReadContext().ReadFromTarget; - var topRangeSectionMap = ExecutionManagerTestBuilder.ExecutionManagerCodeRangeMapAddress; - var typeInfo = emBuilder.Types; - return new ExecutionManagerTestTarget(emBuilder.Version, arch, reader, topRangeSectionMap, typeInfo); - } - - public ExecutionManagerTestTarget(int version, MockTarget.Architecture arch, ReadFromTargetDelegate dataReader, TargetPointer topRangeSectionMap, Dictionary typeInfoCache) - : base(arch, dataReader) - { - _topRangeSectionMap = topRangeSectionMap; - SetTypeInfoCache(typeInfoCache); - IContractFactory emfactory = new ExecutionManagerFactory(); - SetContracts(new TestRegistry() { - ExecutionManagerContract = new (() => emfactory.CreateContract(this, version)), - PlatformMetadataContract = new (() => new Mock().Object) - }); - } - public override TargetPointer ReadGlobalPointer(string global) - { - switch (global) - { - case Constants.Globals.ExecutionManagerCodeRangeMapAddress: - return new TargetPointer(_topRangeSectionMap); - default: - return base.ReadGlobalPointer(global); - } - } - - public override T ReadGlobal(string name) - { - switch (name) - { - case Constants.Globals.StubCodeBlockLast: - if (typeof(T) == typeof(byte)) - return (T)(object)(byte)0x0Fu; - break; - case Constants.Globals.FeatureEHFunclets: - if (typeof(T) == typeof(byte)) - return (T)(object)(byte)1; - break; - case Constants.Globals.HashMapValueMask: - if (typeof(T) == typeof(ulong)) - return (T)(object)(PointerSize == 4 ? 0x7FFFFFFFu : 0x7FFFFFFFFFFFFFFFu); - break; - case Constants.Globals.HashMapSlotsPerBucket: - if (typeof(T) == typeof(uint)) - return (T)(object)4u; - break; - default: - break; - } - return base.ReadGlobal(name); - } + var arch = emBuilder.Builder.TargetTestHelpers.Arch; + TestPlaceholderTarget.ReadFromTargetDelegate reader = emBuilder.Builder.GetReadContext().ReadFromTarget; + var target = new TestPlaceholderTarget(arch, reader, emBuilder.Types, emBuilder.Globals); + IContractFactory emfactory = new ExecutionManagerFactory(); + ContractRegistry reg = Mock.Of( + c => c.ExecutionManager == emfactory.CreateContract(target, emBuilder.Version) + && c.PlatformMetadata == new Mock().Object); + target.SetContracts(reg); + return target; } [Theory] @@ -78,8 +29,7 @@ public override T ReadGlobal(string name) public void GetCodeBlockHandle_Null(int version, MockTarget.Architecture arch) { ExecutionManagerTestBuilder emBuilder = new (version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); - emBuilder.MarkCreated(); - var target = ExecutionManagerTestTarget.FromBuilder (emBuilder); + var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; Assert.NotNull(em); @@ -92,8 +42,7 @@ public void GetCodeBlockHandle_Null(int version, MockTarget.Architecture arch) public void GetCodeBlockHandle_NoRangeSections(int version, MockTarget.Architecture arch) { ExecutionManagerTestBuilder emBuilder = new (version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); - emBuilder.MarkCreated(); - var target = ExecutionManagerTestTarget.FromBuilder (emBuilder); + var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; Assert.NotNull(em); @@ -125,9 +74,7 @@ public void GetMethodDesc_OneRangeOneMethod(int version, MockTarget.Architecture TargetPointer rangeSectionAddress = emBuilder.AddRangeSection(jittedCode, jitManagerAddress: jitManagerAddress, codeHeapListNodeAddress: codeHeapListNodeAddress); TargetPointer rangeSectionFragmentAddress = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); - emBuilder.MarkCreated(); - - var target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; Assert.NotNull(em); @@ -169,9 +116,7 @@ public void GetCodeBlockHandle_OneRangeZeroMethod(int version, MockTarget.Archit TargetPointer rangeSectionAddress = emBuilder.AddRangeSection(jittedCode, jitManagerAddress: jitManagerAddress, codeHeapListNodeAddress: codeHeapListNodeAddress); TargetPointer rangeSectionFragmentAddress = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); - emBuilder.MarkCreated(); - - var target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; Assert.NotNull(em); @@ -212,9 +157,7 @@ public void GetCodeBlockHandle_R2R_NoRuntimeFunctionMatch(int version, MockTarge TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); - emBuilder.MarkCreated(); - - Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + Target target = CreateTarget(emBuilder); IExecutionManager em = target.Contracts.ExecutionManager; Assert.NotNull(em); @@ -249,9 +192,7 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); - emBuilder.MarkCreated(); - - Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + Target target = CreateTarget(emBuilder); IExecutionManager em = target.Contracts.ExecutionManager; Assert.NotNull(em); @@ -300,9 +241,7 @@ public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.A TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); - emBuilder.MarkCreated(); - - Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + Target target = CreateTarget(emBuilder); IExecutionManager em = target.Contracts.ExecutionManager; Assert.NotNull(em); diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs index 61b207abad2399..53c7b2ffcb8623 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs @@ -9,29 +9,6 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; public class HashMapTests { - internal class HashMapTestTarget : TestPlaceholderTarget - { - private readonly (string Name, ulong Value, string? Type)[] _globals; - - public HashMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext, MockDescriptors.HashMap hashMap) - : base (arch, readContext.ReadFromTarget) - { - _globals = hashMap.Globals; - SetTypeInfoCache(hashMap.Types); - } - - public override T ReadGlobal(string name) - { - foreach (var global in _globals) - { - if (global.Name == name) - return T.CreateChecked(global.Value); - } - - return base.ReadGlobal(name); - } - } - [Theory] [ClassData(typeof(MockTarget.StdArch))] public void GetValue(MockTarget.Architecture arch) @@ -47,9 +24,8 @@ public void GetValue(MockTarget.Architecture arch) ]; TargetPointer mapAddress = hashMap.CreateMap(entries); TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries); - builder.MarkCreated(); - Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap); + Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals); var lookup = HashMapLookup.Create(target); var ptrLookup = PtrHashMapLookup.Create(target); @@ -84,9 +60,8 @@ public void GetValue_Collision(MockTarget.Architecture arch) ]; TargetPointer mapAddress = hashMap.CreateMap(entries); TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries); - builder.MarkCreated(); - Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap); + Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals); var lookup = HashMapLookup.Create(target); var ptrLookup = PtrHashMapLookup.Create(target); @@ -110,9 +85,8 @@ public void GetValue_NoMatch(MockTarget.Architecture arch) (TargetPointer Key, TargetPointer Value)[] entries = [(0x100, 0x010)]; TargetPointer mapAddress = hashMap.CreateMap(entries); TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries); - builder.MarkCreated(); - Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap); + Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals); { var lookup = HashMapLookup.Create(target); diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs index e9c8369ade4104..6a71fbafcbb67b 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs @@ -9,19 +9,13 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; public class NibbleMapTestsBase { - internal class NibbleMapTestTarget : TestPlaceholderTarget + internal static Target CreateTarget(NibbleMapTestBuilderBase nibbleMapTestBuilder) { - public NibbleMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext) - : base(arch, readContext.ReadFromTarget) + MockMemorySpace.ReadContext readContext = new() { - } - } - - internal static NibbleMapTestTarget CreateTarget(NibbleMapTestBuilderBase nibbleMapTestBuilder) - { - return new NibbleMapTestTarget(nibbleMapTestBuilder.Arch, new MockMemorySpace.ReadContext() { HeapFragments = new[] { nibbleMapTestBuilder.NibbleMapFragment } - }); + }; + return new TestPlaceholderTarget(nibbleMapTestBuilder.Arch, readContext.ReadFromTarget); } } @@ -84,7 +78,7 @@ public void NibbleMapOneItemLookupOk(MockTarget.Architecture arch) TargetCodePointer inputPC = new(mapBase + 0x0200u); uint codeSize = 0x80; // doesn't matter builder.AllocateCodeChunk (inputPC, codeSize); - NibbleMapTestTarget target = CreateTarget(builder); + Target target = CreateTarget(builder); // TESTCASE: @@ -144,7 +138,7 @@ public void NibbleMapOneItemLookupOk(MockTarget.Architecture arch) TargetCodePointer inputPC = new(mapBase + 0x0200u); uint codeSize = 0x400; builder.AllocateCodeChunk (inputPC, codeSize); - NibbleMapTestTarget target = CreateTarget(builder); + Target target = CreateTarget(builder); // TESTCASE: diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs index fed8cc942ad442..914c5ec93223aa 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs @@ -6,26 +6,16 @@ using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; - namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; public class RangeSectionMapTests { - internal class RSMTestTarget : TestPlaceholderTarget - { - public RSMTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext) - : base (arch, readContext.ReadFromTarget) - { - } - } - [Theory] [ClassData(typeof(MockTarget.StdArch))] public void TestLookupFail(MockTarget.Architecture arch) { var builder = ExecutionManagerTestBuilder.CreateRangeSection(arch); - builder.MarkCreated(); - var target = new RSMTestTarget(arch, builder.GetReadContext()); + var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget); var rsla = RangeSectionMap.Create(target); @@ -43,8 +33,7 @@ public void TestLookupOne(MockTarget.Architecture arch) var length = 0x1000u; var value = 0x0a0a_0a0au; builder.InsertAddressRange(inputPC, length, value); - builder.MarkCreated(); - var target = new RSMTestTarget(arch, builder.GetReadContext()); + var target = new TestPlaceholderTarget(arch, builder.GetReadContext().ReadFromTarget); var rsla = RangeSectionMap.Create(target); @@ -59,7 +48,7 @@ public void TestLookupOne(MockTarget.Architecture arch) public void TestGetIndexForLevel(MockTarget.Architecture arch) { // Exhaustively test GetIndexForLevel for all possible values of the byte for each level - var target = new RSMTestTarget(arch, new MockMemorySpace.ReadContext()); + var target = new TestPlaceholderTarget(arch, new MockMemorySpace.ReadContext().ReadFromTarget); var rsla = RangeSectionMap.Create(target); int numLevels = arch.Is64Bit ? 5 : 2; // the bits 0..effectiveRange - 1 are not handled the map and are irrelevant diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index a6b833044abd7f..05df0bace8d6e5 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -90,8 +90,6 @@ public CodeVersions(MockMemorySpace.Builder builder, (ulong Start, ulong End) al ]); } - public void MarkCreated() => Builder.MarkCreated(); - public TargetPointer AddMethodDescVersioningState(TargetPointer nativeCodeVersionNode, bool isDefaultVersionActive) { Target.TypeInfo info = Types[DataType.MethodDescVersioningState]; diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs index 597d75cf6001fd..499c5da0ef7b3d 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs @@ -33,7 +33,9 @@ public class HashMap }; internal Dictionary Types { get; } - internal (string Name, ulong Value, string? Type)[] Globals { get; } + internal (string Name, ulong Value)[] Globals { get; } + + internal MockMemorySpace.Builder Builder { get; } private const ulong DefaultAllocationRangeStart = 0x0003_0000; private const ulong DefaultAllocationRangeEnd = 0x0004_0000; @@ -41,7 +43,6 @@ public class HashMap // See g_rgPrimes in hash.cpp private static readonly uint[] PossibleSizes = [5, 11, 17, 23, 29, 37]; - private readonly MockMemorySpace.Builder _builder; private readonly MockMemorySpace.BumpAllocator _allocator; public HashMap(MockMemorySpace.Builder builder) @@ -50,38 +51,42 @@ public HashMap(MockMemorySpace.Builder builder) public HashMap(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocationRange) { - _builder = builder; - _allocator = _builder.CreateAllocator(allocationRange.Start, allocationRange.End); - Types = GetTypes(); - Globals = - [ - (nameof(Constants.Globals.HashMapSlotsPerBucket), HashMapSlotsPerBucket, "uint32"), - (nameof(Constants.Globals.HashMapValueMask), _builder.TargetTestHelpers.PointerSize == 4 ? 0x7FFFFFFFu : 0x7FFFFFFFFFFFFFFFu, "uint64"), - ]; + Builder = builder; + _allocator = Builder.CreateAllocator(allocationRange.Start, allocationRange.End); + Types = GetTypes(builder.TargetTestHelpers); + Globals = GetGlobals(builder.TargetTestHelpers); } - private Dictionary GetTypes() + internal static Dictionary GetTypes(TargetTestHelpers helpers) { return GetTypesForTypeFields( - _builder.TargetTestHelpers, + helpers, [ HashMapFields, - BucketFields(_builder.TargetTestHelpers), + BucketFields(helpers), ]); } + internal static (string Name, ulong Value)[] GetGlobals(TargetTestHelpers helpers) + { + return [ + (nameof(Constants.Globals.HashMapSlotsPerBucket), HashMapSlotsPerBucket), + (nameof(Constants.Globals.HashMapValueMask), helpers.MaxSignedTargetAddress), + ]; + } + public TargetPointer CreateMap((TargetPointer Key, TargetPointer Value)[] entries) { Target.TypeInfo hashMapType = Types[DataType.HashMap]; MockMemorySpace.HeapFragment map = _allocator.Allocate(hashMapType.Size!.Value, "HashMap"); - _builder.AddHeapFragment(map); + Builder.AddHeapFragment(map); PopulateMap(map.Address, entries); return map.Address; } public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPointer Value)[] entries) { - TargetTestHelpers helpers = _builder.TargetTestHelpers; + TargetTestHelpers helpers = Builder.TargetTestHelpers; // HashMap::NewSize int requiredSlots = entries.Length * 3 / 2; @@ -95,7 +100,7 @@ public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPoin uint numBuckets = size + 1; uint totalBucketsSize = bucketSize * numBuckets; MockMemorySpace.HeapFragment buckets = _allocator.Allocate(totalBucketsSize, $"Buckets[{numBuckets}]"); - _builder.AddHeapFragment(buckets); + Builder.AddHeapFragment(buckets); helpers.Write(buckets.Data.AsSpan().Slice(0, sizeof(uint)), size); const int maxRetry = 8; @@ -120,7 +125,7 @@ public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPoin // Update the map to point at the buckets Target.TypeInfo hashMapType = Types[DataType.HashMap]; - Span map = _builder.BorrowAddressRange(mapAddress, (int)hashMapType.Size!.Value); + Span map = Builder.BorrowAddressRange(mapAddress, (int)hashMapType.Size!.Value); helpers.WritePointer(map.Slice(hashMapType.Fields[nameof(Data.HashMap.Buckets)].Offset, helpers.PointerSize), buckets.Address); } @@ -144,7 +149,7 @@ public void PopulatePtrMap(TargetPointer mapAddress, (TargetPointer Key, TargetP private bool TryAddEntryToBucket(Span bucket, TargetPointer key, TargetPointer value) { - TargetTestHelpers helpers = _builder.TargetTestHelpers; + TargetTestHelpers helpers = Builder.TargetTestHelpers; Target.TypeInfo bucketType = Types[DataType.Bucket]; for (int i = 0; i < HashMapSlotsPerBucket; i++) { diff --git a/src/native/managed/cdacreader/tests/MockMemorySpace.cs b/src/native/managed/cdacreader/tests/MockMemorySpace.cs index f7cbacf2515227..59a4026bc2454f 100644 --- a/src/native/managed/cdacreader/tests/MockMemorySpace.cs +++ b/src/native/managed/cdacreader/tests/MockMemorySpace.cs @@ -216,21 +216,12 @@ private ulong CreateDescriptorFragments() if (pointerData.Data.Length > 0) AddHeapFragment(pointerData); - MarkCreated(); - return descriptor.Address; - } - - internal void MarkCreated() - { - if (_created) - throw new InvalidOperationException("Context already created"); _created = true; + return descriptor.Address; } internal ReadContext GetReadContext() { - if (!_created) - throw new InvalidOperationException("Context not created"); ReadContext context = new ReadContext { HeapFragments = _heapFragments, diff --git a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs index 1647d5f2639524..c8ad543c5e2227 100644 --- a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs +++ b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs @@ -179,7 +179,8 @@ internal class PrecodeBuilder { public readonly MockMemorySpace.Builder Builder; public readonly MockMemorySpace.BumpAllocator PrecodeAllocator; public readonly MockMemorySpace.BumpAllocator StubDataPageAllocator; - public readonly Dictionary? TypeInfoCache; + + internal Dictionary Types { get; } public TargetPointer MachineDescriptorAddress; public CodePointerFlags CodePointerFlags {get; private set;} @@ -189,16 +190,11 @@ public PrecodeBuilder(AllocationRange allocationRange, MockMemorySpace.Builder b Builder = builder; PrecodeAllocator = builder.CreateAllocator(allocationRange.PrecodeDescriptorStart, allocationRange.PrecodeDescriptorEnd); StubDataPageAllocator = builder.CreateAllocator(allocationRange.StubDataPageStart, allocationRange.StubDataPageEnd); - TypeInfoCache = typeInfoCache ?? CreateTypeInfoCache(Builder.TargetTestHelpers); - } - - public Dictionary CreateTypeInfoCache(TargetTestHelpers targetTestHelpers) { - var typeInfo = new Dictionary(); - AddToTypeInfoCache(typeInfo, targetTestHelpers); - return typeInfo; + Types = typeInfoCache ?? GetTypes(Builder.TargetTestHelpers); } - public void AddToTypeInfoCache(Dictionary typeInfoCache, TargetTestHelpers targetTestHelpers) { + public Dictionary GetTypes(TargetTestHelpers targetTestHelpers) { + Dictionary types = new(); var layout = targetTestHelpers.LayoutFields([ new(nameof(Data.PrecodeMachineDescriptor.StubCodePageSize), DataType.uint32), new(nameof(Data.PrecodeMachineDescriptor.OffsetOfPrecodeType), DataType.uint8), @@ -210,7 +206,7 @@ public void AddToTypeInfoCache(Dictionary typeInfoCac new(nameof(Data.PrecodeMachineDescriptor.FixupPrecodeType), DataType.uint8), new(nameof(Data.PrecodeMachineDescriptor.ThisPointerRetBufPrecodeType), DataType.uint8), ]); - typeInfoCache[DataType.PrecodeMachineDescriptor] = new Target.TypeInfo() { + types[DataType.PrecodeMachineDescriptor] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; @@ -218,10 +214,11 @@ public void AddToTypeInfoCache(Dictionary typeInfoCac new(nameof(Data.StubPrecodeData.Type), DataType.uint8), new(nameof(Data.StubPrecodeData.MethodDesc), DataType.pointer), ]); - typeInfoCache[DataType.StubPrecodeData] = new Target.TypeInfo() { + types[DataType.StubPrecodeData] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; + return types; } private void SetCodePointerFlags(PrecodeTestDescriptor test) @@ -234,7 +231,7 @@ private void SetCodePointerFlags(PrecodeTestDescriptor test) public void AddPlatformMetadata(PrecodeTestDescriptor descriptor) { SetCodePointerFlags(descriptor); - var typeInfo = TypeInfoCache[DataType.PrecodeMachineDescriptor]; + var typeInfo = Types[DataType.PrecodeMachineDescriptor]; var fragment = PrecodeAllocator.Allocate((ulong)typeInfo.Size, $"{descriptor.Name} Precode Machine Descriptor"); Builder.AddHeapFragment(fragment); MachineDescriptorAddress = fragment.Address; @@ -250,7 +247,7 @@ public void AddPlatformMetadata(PrecodeTestDescriptor descriptor) { public TargetCodePointer AddStubPrecodeEntry(string name, PrecodeTestDescriptor test, TargetPointer methodDesc) { // TODO[cdac]: allow writing other kinds of stub precode subtypes ulong stubCodeSize = (ulong)test.StubPrecodeSize; - var stubDataTypeInfo = TypeInfoCache[DataType.StubPrecodeData]; + var stubDataTypeInfo = Types[DataType.StubPrecodeData]; MockMemorySpace.HeapFragment stubDataFragment = StubDataPageAllocator.Allocate((ulong)stubDataTypeInfo.Size, $"Stub data for {name} on {test.Name}"); Builder.AddHeapFragment(stubDataFragment); // allocate the code one page before the stub data @@ -272,47 +269,29 @@ public TargetCodePointer AddStubPrecodeEntry(string name, PrecodeTestDescriptor } return address; } - - public void MarkCreated() => Builder.MarkCreated(); } - internal class PrecodeTestTarget : TestPlaceholderTarget + private static Target CreateTarget(PrecodeBuilder precodeBuilder) { - internal readonly TargetPointer PrecodeMachineDescriptorAddress; + var arch = precodeBuilder.Builder.TargetTestHelpers.Arch; + TestPlaceholderTarget.ReadFromTargetDelegate reader = precodeBuilder.Builder.GetReadContext().ReadFromTarget; // hack for this test put the precode machine descriptor at the same address as the PlatformMetadata - internal TargetPointer PlatformMetadataAddress => PrecodeMachineDescriptorAddress; - public static PrecodeTestTarget FromBuilder(PrecodeBuilder precodeBuilder) - { - precodeBuilder.MarkCreated(); - var arch = precodeBuilder.Builder.TargetTestHelpers.Arch; - ReadFromTargetDelegate reader = precodeBuilder.Builder.GetReadContext().ReadFromTarget; - var typeInfo = precodeBuilder.TypeInfoCache; - return new PrecodeTestTarget(arch, reader, precodeBuilder.CodePointerFlags, precodeBuilder.MachineDescriptorAddress, typeInfo); - } - public PrecodeTestTarget(MockTarget.Architecture arch, ReadFromTargetDelegate reader, CodePointerFlags codePointerFlags, TargetPointer platformMetadataAddress, Dictionary typeInfoCache) - : base(arch, reader) - { - PrecodeMachineDescriptorAddress = platformMetadataAddress; - SetTypeInfoCache(typeInfoCache); - IContractFactory precodeFactory = new PrecodeStubsFactory(); - - Mock platformMetadata = new(MockBehavior.Strict); - platformMetadata.Setup(p => p.GetCodePointerFlags()).Returns(codePointerFlags); - platformMetadata.Setup(p => p.GetPrecodeMachineDescriptor()).Returns(PrecodeMachineDescriptorAddress); - - SetContracts(new TestRegistry() { - PlatformMetadataContract = new (() => platformMetadata.Object), - PrecodeStubsContract = new (() => precodeFactory.CreateContract(this, 1)), - }); - } - - public override TargetPointer ReadGlobalPointer (string name) - { - if (name == Constants.Globals.PlatformMetadata) { - return PlatformMetadataAddress; - } - return base.ReadGlobalPointer(name); - } + (string Name, ulong Value)[] globals = [(Constants.Globals.PlatformMetadata, precodeBuilder.MachineDescriptorAddress)]; + var target = new TestPlaceholderTarget(arch, reader, precodeBuilder.Types, globals); + + IContractFactory precodeFactory = new PrecodeStubsFactory(); + Mock platformMetadata = new(); + platformMetadata.Setup(p => p.GetCodePointerFlags()).Returns(precodeBuilder.CodePointerFlags); + platformMetadata.Setup(p => p.GetPrecodeMachineDescriptor()).Returns(precodeBuilder.MachineDescriptorAddress); + + // Creating the PrecodeStubs contract depends on the PlatformMetadata contract, so we need + // to set it up such that it will only be created after the target's targets are set up + Mock reg = new(); + reg.SetupGet(c => c.PlatformMetadata).Returns(platformMetadata.Object); + reg.SetupGet(c => c.PrecodeStubs).Returns(() => precodeFactory.CreateContract(target, 1)); + target.SetContracts(reg.Object); + + return target; } [Theory] @@ -325,7 +304,7 @@ public void TestPrecodeStubPrecodeExpectedMethodDesc(PrecodeTestDescriptor test) TargetPointer expectedMethodDesc = new TargetPointer(0xeeee_eee0u); // arbitrary TargetCodePointer stub1 = builder.AddStubPrecodeEntry("Stub 1", test, expectedMethodDesc); - var target = PrecodeTestTarget.FromBuilder(builder); + var target = CreateTarget(builder); Assert.NotNull(target); var precodeContract = target.Contracts.PrecodeStubs; @@ -334,7 +313,5 @@ public void TestPrecodeStubPrecodeExpectedMethodDesc(PrecodeTestDescriptor test) var actualMethodDesc = precodeContract.GetMethodDescFromStubAddress(stub1); Assert.Equal(expectedMethodDesc, actualMethodDesc); - - } } diff --git a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs index 41e47ca83766b7..4496edd8305a9a 100644 --- a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs +++ b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs @@ -19,6 +19,9 @@ public TargetTestHelpers(MockTarget.Architecture arch) } public int PointerSize => Arch.Is64Bit ? sizeof(ulong) : sizeof(uint); + + public ulong MaxSignedTargetAddress => (ulong)(Arch.Is64Bit ? long.MaxValue : int.MaxValue); + public int ContractDescriptorSize => ContractDescriptor.Size(Arch.Is64Bit); diff --git a/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs b/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs index 7c94e57b011ffa..11f68f27847c97 100644 --- a/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs +++ b/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs @@ -11,41 +11,35 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; /// -/// A base class implementation of Target that throws NotImplementedException for all methods. +/// A mock implementation of Target that has basic implementations of getting types/globals and reading data /// internal class TestPlaceholderTarget : Target { - private protected ContractRegistry contractRegistry; - private protected Target.IDataCache dataCache; - private protected Dictionary typeInfoCache; + private ContractRegistry _contractRegistry; + private readonly Target.IDataCache _dataCache; + private readonly Dictionary _typeInfoCache; + private readonly (string Name, ulong Value)[] _globals; internal delegate int ReadFromTargetDelegate(ulong address, Span buffer); - protected ReadFromTargetDelegate _dataReader = (address, buffer) => throw new NotImplementedException(); + private readonly ReadFromTargetDelegate _dataReader; -#region Setup - public TestPlaceholderTarget(MockTarget.Architecture arch, ReadFromTargetDelegate reader) + public TestPlaceholderTarget(MockTarget.Architecture arch, ReadFromTargetDelegate reader, Dictionary types = null, (string Name, ulong Value)[] globals = null) { IsLittleEndian = arch.IsLittleEndian; PointerSize = arch.Is64Bit ? 8 : 4; - contractRegistry = new TestRegistry(); - dataCache = new DefaultDataCache(this); - typeInfoCache = null; + _contractRegistry = new Mock().Object; + _dataCache = new DefaultDataCache(this); + _typeInfoCache = types ?? []; _dataReader = reader; + _globals = globals ?? []; } internal void SetContracts(ContractRegistry contracts) { - contractRegistry = contracts; + _contractRegistry = contracts; } - internal void SetTypeInfoCache(Dictionary cache) - { - typeInfoCache = cache; - } - -#endregion Setup - public override int PointerSize { get; } public override bool IsLittleEndian { get; } @@ -54,14 +48,34 @@ public override bool IsAlignedToPointerSize(TargetPointer pointer) return (pointer.Value & (ulong)(PointerSize - 1)) == 0; } - public override TargetPointer ReadGlobalPointer(string global) => throw new NotImplementedException(); + public override TargetPointer ReadGlobalPointer(string name) + { + foreach (var global in _globals) + { + if (global.Name == name) + return new TargetPointer(global.Value); + } + + throw new NotImplementedException(); + } + public override TargetPointer ReadPointer(ulong address) => DefaultReadPointer(address); public override TargetCodePointer ReadCodePointer(ulong address) => DefaultReadCodePointer(address); public override void ReadBuffer(ulong address, Span buffer) => throw new NotImplementedException(); public override string ReadUtf8String(ulong address) => throw new NotImplementedException(); public override string ReadUtf16String(ulong address) => throw new NotImplementedException(); public override TargetNUInt ReadNUInt(ulong address) => DefaultReadNUInt(address); - public override T ReadGlobal(string name) => throw new NotImplementedException(); + public override T ReadGlobal(string name) + { + foreach (var global in _globals) + { + if (global.Name == name) + return T.CreateChecked(global.Value); + } + + throw new NotImplementedException(); + } + public override T Read(ulong address) => DefaultRead(address); #region subclass reader helpers @@ -178,49 +192,16 @@ protected TargetCodePointer DefaultReadCodePointer(ulong address) public override TargetPointer ReadPointerFromSpan(ReadOnlySpan bytes) => throw new NotImplementedException(); - public override Target.TypeInfo GetTypeInfo(DataType dataType) => typeInfoCache != null ? GetTypeInfoImpl(dataType) : throw new NotImplementedException(); - - private protected virtual Target.TypeInfo GetTypeInfoImpl(DataType dataType) + public override Target.TypeInfo GetTypeInfo(DataType dataType) { - if (typeInfoCache!.TryGetValue(dataType, out var info)) - { + if (_typeInfoCache.TryGetValue(dataType, out var info)) return info; - } + throw new NotImplementedException(); } - public override Target.IDataCache ProcessedData => dataCache; - public override ContractRegistry Contracts => contractRegistry; - - internal class TestRegistry : ContractRegistry - { - public TestRegistry() { } - internal Lazy? ExceptionContract { get; set; } - internal Lazy? LoaderContract { get; set; } - internal Lazy? EcmaMetadataContract { get; set; } - internal Lazy? ObjectContract { get; set; } - internal Lazy? ThreadContract { get; set; } - internal Lazy? RuntimeTypeSystemContract { get; set; } - internal Lazy? DacStreamsContract { get; set; } - internal Lazy ExecutionManagerContract { get; set; } - internal Lazy? CodeVersionsContract { get; set; } - internal Lazy? PlatformMetadataContract { get; set; } - internal Lazy? PrecodeStubsContract { get; set; } - internal Lazy? ReJITContract { get; set; } - - public override Contracts.IException Exception => ExceptionContract.Value ?? throw new NotImplementedException(); - public override Contracts.ILoader Loader => LoaderContract.Value ?? throw new NotImplementedException(); - public override Contracts.IEcmaMetadata EcmaMetadata => EcmaMetadataContract.Value ?? throw new NotImplementedException(); - public override Contracts.IObject Object => ObjectContract.Value ?? throw new NotImplementedException(); - public override Contracts.IThread Thread => ThreadContract.Value ?? throw new NotImplementedException(); - public override Contracts.IRuntimeTypeSystem RuntimeTypeSystem => RuntimeTypeSystemContract.Value ?? throw new NotImplementedException(); - public override Contracts.IDacStreams DacStreams => DacStreamsContract.Value ?? throw new NotImplementedException(); - public override Contracts.IExecutionManager ExecutionManager => ExecutionManagerContract.Value ?? throw new NotImplementedException(); - public override Contracts.ICodeVersions CodeVersions => CodeVersionsContract.Value ?? throw new NotImplementedException(); - public override Contracts.IPlatformMetadata PlatformMetadata => PlatformMetadataContract.Value ?? throw new NotImplementedException(); - public override Contracts.IPrecodeStubs PrecodeStubs => PrecodeStubsContract.Value ?? throw new NotImplementedException(); - public override Contracts.IReJIT ReJIT => ReJITContract.Value ?? throw new NotImplementedException(); - } + public override Target.IDataCache ProcessedData => _dataCache; + public override ContractRegistry Contracts => _contractRegistry; // A data cache that stores data in a dictionary and calls IData.Create to construct the data. private class DefaultDataCache : Target.IDataCache