com.wallstop-studios.dxmessaging 2.2.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +315 -67
- package/CHANGELOG.md.meta +7 -7
- package/Editor/Analyzers/BaseCallIlInspector.cs +277 -0
- package/Editor/Analyzers/BaseCallIlInspector.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs +295 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallReportAggregator.cs +308 -0
- package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallTypeScanner.cs +110 -0
- package/Editor/Analyzers/BaseCallTypeScanner.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallTypeScannerCore.cs +562 -0
- package/Editor/Analyzers/BaseCallTypeScannerCore.cs.meta +11 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +1129 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +11 -0
- package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +44 -44
- package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +44 -44
- package/Editor/Analyzers/System.Collections.Immutable.dll.meta +44 -44
- package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +44 -44
- package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +44 -44
- package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +46 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +72 -72
- package/Editor/Analyzers.meta +8 -8
- package/Editor/AssemblyInfo.cs.meta +11 -3
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +429 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +11 -0
- package/Editor/CustomEditors/MessagingComponentEditor.cs +1 -1
- package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +11 -3
- package/Editor/CustomEditors.meta +2 -2
- package/Editor/DxMessagingEditorIdle.cs +62 -0
- package/Editor/DxMessagingEditorIdle.cs.meta +11 -0
- package/Editor/DxMessagingEditorInitializer.cs +112 -15
- package/Editor/DxMessagingEditorInitializer.cs.meta +11 -3
- package/Editor/DxMessagingEditorLog.cs +32 -0
- package/Editor/DxMessagingEditorLog.cs.meta +11 -0
- package/Editor/DxMessagingMenu.cs.meta +11 -11
- package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +313 -0
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
- package/Editor/Settings/DxMessagingSettings.cs +261 -11
- package/Editor/Settings/DxMessagingSettings.cs.meta +11 -3
- package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
- package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +11 -3
- package/Editor/Settings.meta +2 -2
- package/Editor/SetupCscRsp.cs +406 -39
- package/Editor/SetupCscRsp.cs.meta +11 -3
- package/Editor/Testing/MessagingComponentEditorHarness.cs +2 -2
- package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +11 -3
- package/Editor/Testing.meta +3 -3
- package/Editor/WallstopStudios.DxMessaging.Editor.asmdef +14 -14
- package/Editor/WallstopStudios.DxMessaging.Editor.asmdef.meta +7 -7
- package/Editor.meta +8 -8
- package/LICENSE.md +9 -9
- package/LICENSE.md.meta +7 -7
- package/README.md +940 -900
- package/README.md.meta +7 -7
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +2 -4
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +11 -3
- package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs +56 -0
- package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs.meta +11 -0
- package/Runtime/Core/Attributes.meta +2 -2
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs +195 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs.meta +11 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs +179 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs.meta +11 -0
- package/Runtime/Core/Configuration.meta +9 -0
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +46 -28
- package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +11 -3
- package/Runtime/Core/DataStructure.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +11 -3
- package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +11 -3
- package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +11 -3
- package/Runtime/Core/Diagnostics.meta +2 -2
- package/Runtime/Core/DxMessagingStaticState.cs +19 -0
- package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -11
- package/Runtime/Core/Extensions/EnumExtensions.cs +6 -5
- package/Runtime/Core/Extensions/EnumExtensions.cs.meta +11 -3
- package/Runtime/Core/Extensions/IListExtensions.cs.meta +11 -3
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
- package/Runtime/Core/Extensions/MessageExtensions.cs +0 -60
- package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -11
- package/Runtime/Core/Extensions.meta +8 -8
- package/Runtime/Core/Helper/MessageCache.cs +32 -0
- package/Runtime/Core/Helper/MessageCache.cs.meta +11 -3
- package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +11 -3
- package/Runtime/Core/Helper.meta +2 -2
- package/Runtime/Core/IMessage.cs +3 -3
- package/Runtime/Core/IMessage.cs.meta +11 -11
- package/Runtime/Core/InstanceId.cs +25 -1
- package/Runtime/Core/InstanceId.cs.meta +11 -11
- package/Runtime/Core/Internal/DxUnsafe.cs +60 -0
- package/Runtime/Core/Internal/DxUnsafe.cs.meta +11 -0
- package/Runtime/Core/Internal/FlatDispatch.cs +198 -0
- package/Runtime/Core/Internal/FlatDispatch.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs +38 -0
- package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedSlotIndex.cs +81 -0
- package/Runtime/Core/Internal/TypedSlotIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedSlots.cs +597 -0
- package/Runtime/Core/Internal/TypedSlots.cs.meta +11 -0
- package/Runtime/Core/Internal.meta +9 -0
- package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -11
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBus.cs +189 -15
- package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +1 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -11
- package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs +16 -0
- package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs +40 -0
- package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/BusSlots.cs +719 -0
- package/Runtime/Core/MessageBus/Internal/BusSlots.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchKind.cs +38 -0
- package/Runtime/Core/MessageBus/Internal/DispatchKind.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs +20 -0
- package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs +28 -0
- package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs +48 -0
- package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/ISweepable.cs +15 -0
- package/Runtime/Core/MessageBus/Internal/ISweepable.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs +222 -0
- package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/SlotKey.cs +192 -0
- package/Runtime/Core/MessageBus/Internal/SlotKey.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal.meta +9 -0
- package/Runtime/Core/MessageBus/MessageBus.cs +5366 -3838
- package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +187 -14
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -11
- package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -11
- package/Runtime/Core/MessageBus.meta +8 -8
- package/Runtime/Core/MessageHandler.cs +2399 -1042
- package/Runtime/Core/MessageHandler.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationToken.cs +429 -44
- package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
- package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +11 -3
- package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +11 -3
- package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/StringMessage.cs.meta +11 -3
- package/Runtime/Core/Messages.meta +8 -8
- package/Runtime/Core/MessagingDebug.cs.meta +11 -11
- package/Runtime/Core/Pooling/CollectionPool.cs +266 -0
- package/Runtime/Core/Pooling/CollectionPool.cs.meta +11 -0
- package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs +30 -0
- package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs.meta +11 -0
- package/Runtime/Core/Pooling/DxPools.cs +157 -0
- package/Runtime/Core/Pooling/DxPools.cs.meta +11 -0
- package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs +106 -0
- package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs.meta +11 -0
- package/Runtime/Core/Pooling/IDxMessagingClock.cs +18 -0
- package/Runtime/Core/Pooling/IDxMessagingClock.cs.meta +11 -0
- package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs +55 -0
- package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs.meta +11 -0
- package/Runtime/Core/Pooling/StopwatchClock.cs +27 -0
- package/Runtime/Core/Pooling/StopwatchClock.cs.meta +11 -0
- package/Runtime/Core/Pooling/UnityRealtimeClock.cs +31 -0
- package/Runtime/Core/Pooling/UnityRealtimeClock.cs.meta +11 -0
- package/Runtime/Core/Pooling.meta +9 -0
- package/Runtime/Core.meta +8 -8
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -11
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -11
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -20
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/Reflex.meta +8 -8
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +109 -1
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -11
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -30
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/VContainer.meta +8 -8
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +11 -3
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -30
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +79 -1
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -11
- package/Runtime/Unity/Integrations/Zenject.meta +8 -8
- package/Runtime/Unity/Integrations.meta +8 -8
- package/Runtime/Unity/MessageAwareComponent.cs +74 -0
- package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
- package/Runtime/Unity/MessagingComponent.cs +43 -10
- package/Runtime/Unity/MessagingComponent.cs.meta +11 -11
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -12
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity.meta +8 -8
- package/Runtime/WallstopStudios.DxMessaging.asmdef +14 -14
- package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -7
- package/Runtime.meta +8 -8
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -98
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -7
- package/Samples~/DI/Prefabs.meta +8 -8
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -14
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -8
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -14
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -8
- package/Samples~/DI/Providers.meta +8 -8
- package/Samples~/DI/README.md +51 -51
- package/Samples~/DI/README.md.meta +7 -7
- package/Samples~/DI/Reflex/SampleInstaller.cs +7 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -11
- package/Samples~/DI/Reflex.meta +8 -8
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs +6 -1
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -11
- package/Samples~/DI/VContainer.meta +8 -8
- package/Samples~/DI/Zenject/SampleInstaller.cs +8 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -11
- package/Samples~/DI/Zenject.meta +8 -8
- package/Samples~/DI.meta +8 -8
- package/Samples~/Mini Combat/Boot.cs.meta +11 -11
- package/Samples~/Mini Combat/Enemy.cs.meta +11 -11
- package/Samples~/Mini Combat/Messages.cs.meta +11 -11
- package/Samples~/Mini Combat/Player.cs.meta +11 -11
- package/Samples~/Mini Combat/README.md +324 -323
- package/Samples~/Mini Combat/README.md.meta +7 -7
- package/Samples~/Mini Combat/UIOverlay.cs.meta +11 -11
- package/Samples~/Mini Combat/Walkthrough.md +430 -430
- package/Samples~/Mini Combat/Walkthrough.md.meta +7 -7
- package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef +13 -13
- package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef.meta +7 -7
- package/Samples~/Mini Combat.meta +8 -8
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/Messages.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/README.md +210 -209
- package/Samples~/UI Buttons + Inspector/README.md.meta +7 -7
- package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef +13 -13
- package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef.meta +7 -7
- package/Samples~/UI Buttons + Inspector.meta +8 -8
- package/SourceGenerators/Directory.Build.props +50 -3
- package/SourceGenerators/Directory.Build.props.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +96 -63
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +745 -87
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +11 -3
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +39 -46
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
- package/SourceGenerators/global.json +7 -0
- package/SourceGenerators/global.json.meta +7 -0
- package/SourceGenerators.meta +8 -8
- package/Third Party Notices.md +3 -3
- package/Third Party Notices.md.meta +7 -7
- package/package.json +102 -92
- package/package.json.meta +7 -7
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// The Unity Editor assembly that hosts this file does not enable nullable annotations; the
|
|
2
|
+
// dotnet-test project that compiles a linked copy DOES (`<Nullable>enable</Nullable>`). Pin the
|
|
3
|
+
// nullable state per-file so behavior is identical in both compilation contexts.
|
|
4
|
+
#nullable disable
|
|
5
|
+
namespace DxMessaging.Editor.Analyzers
|
|
6
|
+
{
|
|
7
|
+
using System;
|
|
8
|
+
using System.Collections.Generic;
|
|
9
|
+
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Pure-BCL data-transfer object for an aggregated per-FQN report row. Mirrors the public
|
|
12
|
+
/// shape of <see cref="BaseCallReportEntry"/> but uses property-style PascalCase fields so it
|
|
13
|
+
/// does not depend on Unity's <c>JsonUtility</c> serialization conventions and is safely
|
|
14
|
+
/// constructible from <c>dotnet test</c>.
|
|
15
|
+
/// </summary>
|
|
16
|
+
/// <remarks>
|
|
17
|
+
/// The harvester wraps every DTO in a <see cref="BaseCallReportEntry"/> (with the lowercase
|
|
18
|
+
/// Unity-serialisable field names) before the snapshot crosses into Editor code. Keep this
|
|
19
|
+
/// shape in lock-step with that wrapper -- fields added here must also flow through to the
|
|
20
|
+
/// Unity-facing entry or the inspector overlay won't see them.
|
|
21
|
+
/// </remarks>
|
|
22
|
+
public sealed class BaseCallReportEntryDto
|
|
23
|
+
{
|
|
24
|
+
/// <summary>Fully-qualified name of the offending type (dot-form for nested types).</summary>
|
|
25
|
+
public string TypeName;
|
|
26
|
+
|
|
27
|
+
/// <summary>Method names whose overrides are missing the corresponding <c>base.*()</c> call.</summary>
|
|
28
|
+
public readonly SortedSet<string> MissingBaseFor = new(StringComparer.Ordinal);
|
|
29
|
+
|
|
30
|
+
/// <summary>Diagnostic IDs that contributed to this entry (e.g., DXMSG006/007/008/009).</summary>
|
|
31
|
+
public readonly HashSet<string> DiagnosticIds = new(StringComparer.Ordinal);
|
|
32
|
+
|
|
33
|
+
/// <summary>Source file path (best-effort) for "Open Script" actions in the inspector overlay.</summary>
|
|
34
|
+
public string FilePath;
|
|
35
|
+
|
|
36
|
+
/// <summary>1-based line number of the first relevant diagnostic, when known.</summary>
|
|
37
|
+
public int Line;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// <summary>
|
|
41
|
+
/// Pure (Unity-API-free) aggregator for the per-assembly merge + retirement logic at the heart
|
|
42
|
+
/// of <see cref="DxMessagingConsoleHarvester"/>. Extracted so the merge contract is covered by
|
|
43
|
+
/// <c>dotnet test</c> via the linked-source pattern (see
|
|
44
|
+
/// <c>WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj</c>).
|
|
45
|
+
/// </summary>
|
|
46
|
+
/// <remarks>
|
|
47
|
+
/// <para>
|
|
48
|
+
/// The harvester must own two pieces of cross-call state to do its job correctly:
|
|
49
|
+
/// </para>
|
|
50
|
+
/// <list type="number">
|
|
51
|
+
/// <item><description><c>typesByAssembly</c>: which FQNs each compiled assembly has reported.
|
|
52
|
+
/// When an assembly recompiles WITHOUT reporting a previously-seen FQN, that FQN is retired
|
|
53
|
+
/// -- the user fixed the offending base call.</description></item>
|
|
54
|
+
/// <item><description><c>mergedReports</c>: the per-FQN union of every assembly's latest
|
|
55
|
+
/// report. The final snapshot merges this with whatever the LogEntries scan yielded.</description></item>
|
|
56
|
+
/// </list>
|
|
57
|
+
/// <para>
|
|
58
|
+
/// Both maps mutate in lock-step inside <see cref="ApplyAssemblyReports"/>. Because retirement
|
|
59
|
+
/// crosses assembly boundaries (an FQN may live in two assemblies; only when neither still
|
|
60
|
+
/// reports it does it disappear from <c>mergedReports</c>), the bookkeeping is the most
|
|
61
|
+
/// failure-prone slice of the harvester. Keeping it pure makes it test-driven.
|
|
62
|
+
/// </para>
|
|
63
|
+
/// </remarks>
|
|
64
|
+
public static class BaseCallReportAggregator
|
|
65
|
+
{
|
|
66
|
+
/// <summary>
|
|
67
|
+
/// Apply a single assembly's freshly-parsed report batch. Replaces that assembly's prior
|
|
68
|
+
/// FQN attribution wholesale (so types the user fixed disappear) and rebuilds the merged
|
|
69
|
+
/// per-FQN map by unioning every assembly's latest reports.
|
|
70
|
+
/// </summary>
|
|
71
|
+
/// <param name="assemblyKey">Stable identifier for the source assembly (typically the
|
|
72
|
+
/// assembly path Unity passes via <c>CompilationPipeline.assemblyCompilationFinished</c>).</param>
|
|
73
|
+
/// <param name="latestReportsForAssembly">Reports parsed from this assembly's most recent
|
|
74
|
+
/// compilation. May be empty -- that case is the retirement path (every FQN this assembly
|
|
75
|
+
/// previously reported is dropped).</param>
|
|
76
|
+
/// <param name="typesByAssembly">Per-assembly FQN bookkeeping. Mutated in place.</param>
|
|
77
|
+
/// <param name="mergedReports">Per-FQN union across every assembly. Mutated in place;
|
|
78
|
+
/// rebuilt from scratch on every call so retirement Just Works regardless of which
|
|
79
|
+
/// assembly drops its claim.</param>
|
|
80
|
+
public static void ApplyAssemblyReports(
|
|
81
|
+
string assemblyKey,
|
|
82
|
+
IReadOnlyDictionary<string, ParsedTypeReport> latestReportsForAssembly,
|
|
83
|
+
Dictionary<string, HashSet<string>> typesByAssembly,
|
|
84
|
+
Dictionary<string, ParsedTypeReport> mergedReports
|
|
85
|
+
)
|
|
86
|
+
{
|
|
87
|
+
if (string.IsNullOrEmpty(assemblyKey))
|
|
88
|
+
{
|
|
89
|
+
throw new ArgumentException("Assembly key must be non-empty.", nameof(assemblyKey));
|
|
90
|
+
}
|
|
91
|
+
if (typesByAssembly is null)
|
|
92
|
+
{
|
|
93
|
+
throw new ArgumentNullException(nameof(typesByAssembly));
|
|
94
|
+
}
|
|
95
|
+
if (mergedReports is null)
|
|
96
|
+
{
|
|
97
|
+
throw new ArgumentNullException(nameof(mergedReports));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 1. Replace this assembly's FQN set with the latest batch. Types absent from the new
|
|
101
|
+
// batch are dropped from the assembly's row; that's the per-assembly retirement.
|
|
102
|
+
if (!typesByAssembly.TryGetValue(assemblyKey, out HashSet<string> typeSet))
|
|
103
|
+
{
|
|
104
|
+
typeSet = new HashSet<string>(StringComparer.Ordinal);
|
|
105
|
+
typesByAssembly[assemblyKey] = typeSet;
|
|
106
|
+
}
|
|
107
|
+
typeSet.Clear();
|
|
108
|
+
if (latestReportsForAssembly is not null)
|
|
109
|
+
{
|
|
110
|
+
foreach (string fqn in latestReportsForAssembly.Keys)
|
|
111
|
+
{
|
|
112
|
+
if (!string.IsNullOrEmpty(fqn))
|
|
113
|
+
{
|
|
114
|
+
typeSet.Add(fqn);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 2. Rebuild mergedReports from the per-assembly view. We can't simply remove "the
|
|
120
|
+
// types this assembly retired" because another assembly may still report them; the
|
|
121
|
+
// only correct algorithm is to start fresh from typesByAssembly + the freshest
|
|
122
|
+
// payload for each (assembly, FQN) pair.
|
|
123
|
+
//
|
|
124
|
+
// Per-FQN merge semantics:
|
|
125
|
+
// - Method list: union, deduplicated ordinally, stored in deterministic
|
|
126
|
+
// ordinal-sorted order.
|
|
127
|
+
// - Diagnostic IDs: union via HashSet.
|
|
128
|
+
// - File path / line: first non-empty wins (stable across recompiles).
|
|
129
|
+
Dictionary<string, ParsedTypeReport> rebuilt = new(StringComparer.Ordinal);
|
|
130
|
+
foreach (KeyValuePair<string, HashSet<string>> assemblyEntry in typesByAssembly)
|
|
131
|
+
{
|
|
132
|
+
string thisAssemblyKey = assemblyEntry.Key;
|
|
133
|
+
HashSet<string> fqns = assemblyEntry.Value;
|
|
134
|
+
if (fqns is null || fqns.Count == 0)
|
|
135
|
+
{
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// For the assembly we just updated, prefer the freshly-parsed payload. For other
|
|
140
|
+
// assemblies, we need the previous merge to still carry their data; but that
|
|
141
|
+
// information is only retrievable from the OUTGOING mergedReports, so we read it
|
|
142
|
+
// before clearing.
|
|
143
|
+
IReadOnlyDictionary<string, ParsedTypeReport> source = string.Equals(
|
|
144
|
+
thisAssemblyKey,
|
|
145
|
+
assemblyKey,
|
|
146
|
+
StringComparison.OrdinalIgnoreCase
|
|
147
|
+
)
|
|
148
|
+
? latestReportsForAssembly
|
|
149
|
+
: mergedReports;
|
|
150
|
+
if (source is null)
|
|
151
|
+
{
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
foreach (string fqn in fqns)
|
|
156
|
+
{
|
|
157
|
+
if (!source.TryGetValue(fqn, out ParsedTypeReport contribution))
|
|
158
|
+
{
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (contribution is null)
|
|
162
|
+
{
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!rebuilt.TryGetValue(fqn, out ParsedTypeReport existing))
|
|
167
|
+
{
|
|
168
|
+
// Defensive copy so future ApplyAssemblyReports calls don't mutate state
|
|
169
|
+
// that callers may still hold a reference to.
|
|
170
|
+
existing = new ParsedTypeReport
|
|
171
|
+
{
|
|
172
|
+
TypeFullName = contribution.TypeFullName,
|
|
173
|
+
FilePath = contribution.FilePath,
|
|
174
|
+
Line = contribution.Line,
|
|
175
|
+
};
|
|
176
|
+
foreach (string method in contribution.MissingBaseFor)
|
|
177
|
+
{
|
|
178
|
+
if (!string.IsNullOrEmpty(method))
|
|
179
|
+
{
|
|
180
|
+
existing.MissingBaseFor.Add(method);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
foreach (string id in contribution.DiagnosticIds)
|
|
184
|
+
{
|
|
185
|
+
existing.DiagnosticIds.Add(id);
|
|
186
|
+
}
|
|
187
|
+
rebuilt[fqn] = existing;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
foreach (string method in contribution.MissingBaseFor)
|
|
192
|
+
{
|
|
193
|
+
if (!string.IsNullOrEmpty(method))
|
|
194
|
+
{
|
|
195
|
+
existing.MissingBaseFor.Add(method);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
foreach (string id in contribution.DiagnosticIds)
|
|
199
|
+
{
|
|
200
|
+
existing.DiagnosticIds.Add(id);
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
string.IsNullOrEmpty(existing.FilePath)
|
|
204
|
+
&& !string.IsNullOrEmpty(contribution.FilePath)
|
|
205
|
+
)
|
|
206
|
+
{
|
|
207
|
+
existing.FilePath = contribution.FilePath;
|
|
208
|
+
existing.Line = contribution.Line;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
mergedReports.Clear();
|
|
214
|
+
foreach (KeyValuePair<string, ParsedTypeReport> kvp in rebuilt)
|
|
215
|
+
{
|
|
216
|
+
mergedReports[kvp.Key] = kvp.Value;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// <summary>
|
|
221
|
+
/// Builds the final flat snapshot for the inspector overlay by unioning the LogEntries
|
|
222
|
+
/// scan with the per-assembly merged reports. Both inputs are read-only; the result is a
|
|
223
|
+
/// fresh dictionary the caller owns.
|
|
224
|
+
/// </summary>
|
|
225
|
+
/// <param name="logEntriesReports">Reports harvested from <c>UnityEditor.LogEntries</c>.
|
|
226
|
+
/// May be empty (Unity 2021 path) or null (LogEntries reflection unavailable).</param>
|
|
227
|
+
/// <param name="mergedReports">Per-FQN merged view of every assembly's latest reports
|
|
228
|
+
/// produced by <see cref="ApplyAssemblyReports"/>. May be empty when no compilation
|
|
229
|
+
/// callbacks have fired yet.</param>
|
|
230
|
+
public static Dictionary<string, BaseCallReportEntryDto> BuildSnapshot(
|
|
231
|
+
IReadOnlyDictionary<string, ParsedTypeReport> logEntriesReports,
|
|
232
|
+
IReadOnlyDictionary<string, ParsedTypeReport> mergedReports
|
|
233
|
+
)
|
|
234
|
+
{
|
|
235
|
+
Dictionary<string, BaseCallReportEntryDto> snapshot = new(StringComparer.Ordinal);
|
|
236
|
+
|
|
237
|
+
if (logEntriesReports is not null)
|
|
238
|
+
{
|
|
239
|
+
foreach (KeyValuePair<string, ParsedTypeReport> kvp in logEntriesReports)
|
|
240
|
+
{
|
|
241
|
+
AddOrMerge(snapshot, kvp.Key, kvp.Value);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (mergedReports is not null)
|
|
246
|
+
{
|
|
247
|
+
foreach (KeyValuePair<string, ParsedTypeReport> kvp in mergedReports)
|
|
248
|
+
{
|
|
249
|
+
AddOrMerge(snapshot, kvp.Key, kvp.Value);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return snapshot;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private static void AddOrMerge(
|
|
257
|
+
Dictionary<string, BaseCallReportEntryDto> snapshot,
|
|
258
|
+
string fqn,
|
|
259
|
+
ParsedTypeReport report
|
|
260
|
+
)
|
|
261
|
+
{
|
|
262
|
+
if (string.IsNullOrEmpty(fqn) || report is null)
|
|
263
|
+
{
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!snapshot.TryGetValue(fqn, out BaseCallReportEntryDto existing))
|
|
268
|
+
{
|
|
269
|
+
existing = new BaseCallReportEntryDto { TypeName = fqn };
|
|
270
|
+
snapshot[fqn] = existing;
|
|
271
|
+
}
|
|
272
|
+
if (string.IsNullOrEmpty(existing.TypeName))
|
|
273
|
+
{
|
|
274
|
+
existing.TypeName = fqn;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
foreach (string method in report.MissingBaseFor)
|
|
278
|
+
{
|
|
279
|
+
if (!string.IsNullOrEmpty(method))
|
|
280
|
+
{
|
|
281
|
+
// Dedupe across the dual-source merge: LogEntries and CompilerMessage may
|
|
282
|
+
// both surface the same `<type>.<method>` pair on Unity 2022+, where both
|
|
283
|
+
// pipes are wired. MissingBaseFor is a SortedSet<string>, so duplicates are
|
|
284
|
+
// removed and HelpBox output remains stable via deterministic ordinal sorting.
|
|
285
|
+
existing.MissingBaseFor.Add(method);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
foreach (string id in report.DiagnosticIds)
|
|
290
|
+
{
|
|
291
|
+
if (!string.IsNullOrEmpty(id))
|
|
292
|
+
{
|
|
293
|
+
// HashSet dedupes repeated IDs surfaced by the dual-source merge
|
|
294
|
+
// (LogEntries + CompilerMessage on Unity 2022+).
|
|
295
|
+
existing.DiagnosticIds.Add(id);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// First seen file/line wins so "Open Script" jumps to a stable location across
|
|
300
|
+
// rebuilds; which is what the user's eye lands on first in the console.
|
|
301
|
+
if (string.IsNullOrEmpty(existing.FilePath) && !string.IsNullOrEmpty(report.FilePath))
|
|
302
|
+
{
|
|
303
|
+
existing.FilePath = report.FilePath;
|
|
304
|
+
existing.Line = report.Line;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
namespace DxMessaging.Editor.Analyzers
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.Collections.Generic;
|
|
6
|
+
using DxMessaging.Editor.Settings;
|
|
7
|
+
using DxMessaging.Unity;
|
|
8
|
+
using UnityEditor;
|
|
9
|
+
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Edit-time scanner that walks loaded <see cref="MessageAwareComponent"/> subclasses via
|
|
12
|
+
/// <see cref="TypeCache"/> and forwards them to the pure (Unity-API-free) classification
|
|
13
|
+
/// helper <see cref="BaseCallTypeScannerCore"/>. Replaces the lossy console-scraping bridge
|
|
14
|
+
/// as the inspector overlay's primary data source.
|
|
15
|
+
/// </summary>
|
|
16
|
+
/// <remarks>
|
|
17
|
+
/// <para>
|
|
18
|
+
/// <b>Why IL reflection?</b> Unity's <c>CompilationPipeline.assemblyCompilationFinished</c> and
|
|
19
|
+
/// the <c>LogEntries</c> console store are both downstream of Unity's decision to actually
|
|
20
|
+
/// surface analyzer warnings. On Bee/csc cache hits -- which happen on most domain reloads
|
|
21
|
+
/// after the first -- Unity skips that surface entirely, so the scrape returns nothing even
|
|
22
|
+
/// though the analyzer ran successfully on the first compile. By contrast, IL reflection over
|
|
23
|
+
/// loaded types is deterministic: the assemblies are in the AppDomain, the methods have IL
|
|
24
|
+
/// bodies, the same scan produces the same result on every reload regardless of whether the
|
|
25
|
+
/// build was a fresh compile or a Bee cache hit.
|
|
26
|
+
/// </para>
|
|
27
|
+
/// <para>
|
|
28
|
+
/// <b>What it detects:</b>
|
|
29
|
+
/// <list type="bullet">
|
|
30
|
+
/// <item><description>DXMSG006 -- overrides one of the five guarded methods but the IL body
|
|
31
|
+
/// lacks a <c>call</c>/<c>callvirt</c> to the parent's same-named method.</description></item>
|
|
32
|
+
/// <item><description>DXMSG007 -- declares the method with the <c>new</c> modifier (IL: name
|
|
33
|
+
/// shadows a base virtual but the descendant method itself is not in an override slot).</description></item>
|
|
34
|
+
/// <item><description>DXMSG009 -- declares the method without override or new -- same IL shape
|
|
35
|
+
/// as DXMSG007 (both compile to a non-virtual hide-by-sig method). The scanner cannot
|
|
36
|
+
/// distinguish the two perfectly from IL alone, so it conservatively classifies this case as
|
|
37
|
+
/// DXMSG007. The compile-time analyzer is authoritative for the precise ID classification;
|
|
38
|
+
/// the scanner's job is just to make sure the inspector overlay lights up.</description></item>
|
|
39
|
+
/// <item><description>DXMSG010 -- overrides correctly (calls base) but an intermediate
|
|
40
|
+
/// ancestor's override in the chain does NOT call base. Walks parent-by-parent and re-runs the
|
|
41
|
+
/// IL check at every link until the chain terminates at <see cref="MessageAwareComponent"/>
|
|
42
|
+
/// or hits a broken link.</description></item>
|
|
43
|
+
/// </list>
|
|
44
|
+
/// </para>
|
|
45
|
+
/// <para>
|
|
46
|
+
/// <b>Cross-assembly assume-clean:</b> ancestors whose IL is unavailable
|
|
47
|
+
/// (<see cref="System.Reflection.MethodBase.GetMethodBody"/> returns null -- e.g., abstract or
|
|
48
|
+
/// extern methods) are trusted. Emitting an unactionable warning against a closed-source
|
|
49
|
+
/// third-party library would be hostile.
|
|
50
|
+
/// </para>
|
|
51
|
+
/// <para>
|
|
52
|
+
/// <b>Implementation split:</b> the Unity-coupled work (<see cref="TypeCache"/> lookup,
|
|
53
|
+
/// <see cref="DxMessagingSettings"/> read, conversion to the Unity-serializable
|
|
54
|
+
/// <see cref="BaseCallReportEntry"/>) lives here; everything else (chain walk, IL probe,
|
|
55
|
+
/// FQN normalisation, opt-out handling) lives in <see cref="BaseCallTypeScannerCore"/> so the
|
|
56
|
+
/// dotnet-test project can cover the classification logic via Roslyn-compiled fixtures.
|
|
57
|
+
/// </para>
|
|
58
|
+
/// </remarks>
|
|
59
|
+
internal static class BaseCallTypeScanner
|
|
60
|
+
{
|
|
61
|
+
/// <summary>
|
|
62
|
+
/// Scan all loaded <see cref="MessageAwareComponent"/> subclasses and return a per-type
|
|
63
|
+
/// report keyed by fully-qualified type name. The report shape matches what the
|
|
64
|
+
/// console-bridge produced, so the inspector overlay code path needs no changes.
|
|
65
|
+
/// </summary>
|
|
66
|
+
/// <remarks>
|
|
67
|
+
/// Types opted out via <c>[DxIgnoreMissingBaseCall]</c> or via the project's ignored-types
|
|
68
|
+
/// list are intentionally NOT included in the returned dictionary -- the overlay reads the
|
|
69
|
+
/// project ignore list directly to render its "Stop ignoring" HelpBox, and the snapshot
|
|
70
|
+
/// semantics here match the bridge path (DXMSG008-equivalent rows were never present in
|
|
71
|
+
/// the snapshot's <c>missingBaseFor</c> either).
|
|
72
|
+
/// </remarks>
|
|
73
|
+
internal static Dictionary<string, BaseCallReportEntry> Scan(DxMessagingSettings settings)
|
|
74
|
+
{
|
|
75
|
+
// TypeCache is Unity's domain-reload-cached type lookup. Effectively O(1) after the
|
|
76
|
+
// first call and survives across reloads via Unity's serialization layer. Using
|
|
77
|
+
// TypeCache (rather than scanning every loaded assembly via AppDomain) is important
|
|
78
|
+
// for performance; a fresh project can have hundreds of assemblies loaded.
|
|
79
|
+
TypeCache.TypeCollection candidates =
|
|
80
|
+
TypeCache.GetTypesDerivedFrom<MessageAwareComponent>();
|
|
81
|
+
|
|
82
|
+
// Defensive: TypeCache.GetTypesDerivedFrom<T>() returns strict subclasses, but
|
|
83
|
+
// belt-and-braces in case a future Unity version changes the contract; we feed the
|
|
84
|
+
// list through Core.Scan which itself skips MessageAwareComponent by FQN match.
|
|
85
|
+
// The Core handles abstract / generic-definition / null-FQN skipping uniformly.
|
|
86
|
+
Dictionary<string, BaseCallTypeScannerCore.ScanEntry> coreResult =
|
|
87
|
+
BaseCallTypeScannerCore.Scan(
|
|
88
|
+
candidates,
|
|
89
|
+
settings != null ? settings._baseCallIgnoredTypes : null
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
Dictionary<string, BaseCallReportEntry> result = new(StringComparer.Ordinal);
|
|
93
|
+
foreach (KeyValuePair<string, BaseCallTypeScannerCore.ScanEntry> kvp in coreResult)
|
|
94
|
+
{
|
|
95
|
+
BaseCallTypeScannerCore.ScanEntry core = kvp.Value;
|
|
96
|
+
BaseCallReportEntry entry = new()
|
|
97
|
+
{
|
|
98
|
+
typeName = core.TypeName,
|
|
99
|
+
missingBaseFor = new List<string>(core.MissingBaseFor),
|
|
100
|
+
diagnosticIds = new List<string>(core.DiagnosticIds),
|
|
101
|
+
filePath = string.Empty,
|
|
102
|
+
line = 0,
|
|
103
|
+
};
|
|
104
|
+
result[kvp.Key] = entry;
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
#endif
|
|
110
|
+
}
|