From 4f9b8787759dac07f5b8ea1ef0a0fb9dc1326dd5 Mon Sep 17 00:00:00 2001 From: Konstantin Dyachenko Date: Tue, 7 Apr 2026 04:51:37 +0700 Subject: [PATCH] [Add] VContainer --- .../jp.hadashikick.vcontainer/Editor.meta | 8 + .../Editor/Diagnostics.meta | 3 + .../Editor/Diagnostics/SplitterGUILayout.cs | 81 +++ .../Diagnostics/SplitterGUILayout.cs.meta | 3 + .../Editor/Diagnostics/TypeNameHelper.cs | 43 ++ .../Editor/Diagnostics/TypeNameHelper.cs.meta | 3 + .../VContainerDiagnosticsTreeView.cs | 323 ++++++++++++ .../VContainerDiagnosticsTreeView.cs.meta | 3 + .../VContainerDiagnosticsWindow.cs | 216 ++++++++ .../VContainerDiagnosticsWindow.cs.meta | 3 + .../Diagnostics/VContainerInstanceTreeView.cs | 115 +++++ .../VContainerInstanceTreeView.cs.meta | 3 + .../Editor/ParentReferencePropertyDrawer.cs | 65 +++ .../ParentReferencePropertyDrawer.cs.meta | 3 + .../Editor/ScriptTemplateModifier.cs | 113 +++++ .../Editor/ScriptTemplateModifier.cs.meta | 11 + .../Editor/VContainer.Editor.asmdef | 18 + .../Editor/VContainer.Editor.asmdef.meta | 7 + .../jp.hadashikick.vcontainer/Runtime.meta | 8 + .../Runtime/Annotations.meta | 8 + .../Annotations/AwaitableExtensions.cs | 53 ++ .../Annotations/AwaitableExtensions.cs.meta | 3 + .../Runtime/Annotations/IAsyncStartable.cs | 16 + .../Annotations/IAsyncStartable.cs.meta | 3 + .../Runtime/Annotations/IFixedTickable.cs | 7 + .../Annotations/IFixedTickable.cs.meta | 3 + .../Runtime/Annotations/IInitializable.cs | 7 + .../Annotations/IInitializable.cs.meta | 3 + .../Runtime/Annotations/ILateTickable.cs | 7 + .../Runtime/Annotations/ILateTickable.cs.meta | 3 + .../Runtime/Annotations/IPostFixedTickable.cs | 7 + .../Annotations/IPostFixedTickable.cs.meta | 3 + .../Runtime/Annotations/IPostInitializable.cs | 7 + .../Annotations/IPostInitializable.cs.meta | 3 + .../Runtime/Annotations/IPostLateTickable.cs | 7 + .../Annotations/IPostLateTickable.cs.meta | 3 + .../Runtime/Annotations/IPostStartable.cs | 7 + .../Annotations/IPostStartable.cs.meta | 3 + .../Runtime/Annotations/IPostTickable.cs | 7 + .../Runtime/Annotations/IPostTickable.cs.meta | 3 + .../Runtime/Annotations/IStartable.cs | 7 + .../Runtime/Annotations/IStartable.cs.meta | 3 + .../Runtime/Annotations/ITickable.cs | 7 + .../Runtime/Annotations/ITickable.cs.meta | 3 + .../Runtime/Annotations/InjectAttribute.cs | 24 + .../Annotations/InjectAttribute.cs.meta | 11 + .../Runtime/Annotations/KeyAttribute.cs | 15 + .../Runtime/Annotations/KeyAttribute.cs.meta | 11 + .../Runtime/AssemblyInfo.cs | 6 + .../Runtime/AssemblyInfo.cs.meta | 11 + .../Runtime/Container.cs | 310 ++++++++++++ .../Runtime/Container.cs.meta | 11 + .../Runtime/ContainerBuilder.cs | 179 +++++++ .../Runtime/ContainerBuilder.cs.meta | 11 + .../Runtime/ContainerBuilderExtensions.cs | 169 +++++++ .../ContainerBuilderExtensions.cs.meta | 11 + .../Runtime/Diagnostics.meta | 3 + .../Diagnostics/DiagnosticsCollector.cs | 115 +++++ .../Diagnostics/DiagnosticsCollector.cs.meta | 3 + .../Runtime/Diagnostics/DiagnosticsContext.cs | 64 +++ .../Diagnostics/DiagnosticsContext.cs.meta | 3 + .../Runtime/Diagnostics/DiagnosticsInfo.cs | 18 + .../Diagnostics/DiagnosticsInfo.cs.meta | 3 + .../Runtime/Diagnostics/RegisterInfo.cs | 119 +++++ .../Runtime/Diagnostics/RegisterInfo.cs.meta | 3 + .../Runtime/Diagnostics/ResolveInfo.cs | 18 + .../Runtime/Diagnostics/ResolveInfo.cs.meta | 3 + .../Runtime/IInjectParameter.cs | 10 + .../Runtime/IInjectParameter.cs.meta | 3 + .../Runtime/IInjector.cs | 10 + .../Runtime/IInjector.cs.meta | 11 + .../Runtime/IInstanceProvider.cs | 7 + .../Runtime/IInstanceProvider.cs.meta | 3 + .../Runtime/IObjectResolverExtensions.cs | 68 +++ .../Runtime/IObjectResolverExtensions.cs.meta | 3 + .../Runtime/Internal.meta | 8 + .../Internal/BuilderCallbackDisposable.cs | 20 + .../BuilderCallbackDisposable.cs.meta | 3 + .../Runtime/Internal/CappedArrayPool.cs | 76 +++ .../Runtime/Internal/CappedArrayPool.cs.meta | 11 + .../Runtime/Internal/CompositeDisposable.cs | 35 ++ .../Internal/CompositeDisposable.cs.meta | 11 + .../Runtime/Internal/ContainerLocal.cs | 13 + .../Runtime/Internal/ContainerLocal.cs.meta | 3 + .../Internal/FixedTypeObjectKeyHashtable.cs | 109 ++++ .../FixedTypeObjectKeyHashtable.cs.meta | 11 + .../Runtime/Internal/FreeList.cs | 190 +++++++ .../Runtime/Internal/FreeList.cs.meta | 11 + .../Internal/FuncRegistrationBuilder.cs | 23 + .../Internal/FuncRegistrationBuilder.cs.meta | 3 + .../Runtime/Internal/InjectParameter.cs | 80 +++ .../Runtime/Internal/InjectParameter.cs.meta | 3 + .../Runtime/Internal/InjectorCache.cs | 32 ++ .../Runtime/Internal/InjectorCache.cs.meta | 3 + .../Runtime/Internal/InstanceProviders.meta | 3 + .../CollectionInstanceProvider.cs | 131 +++++ .../CollectionInstanceProvider.cs.meta | 3 + .../ContainerInstanceProvider.cs | 12 + .../ContainerInstanceProvider.cs.meta | 3 + .../ContainerLocalInstanceProvider.cs | 45 ++ .../ContainerLocalInstanceProvider.cs.meta | 3 + .../ExistingInstanceProvider.cs | 17 + .../ExistingInstanceProvider.cs.meta | 3 + .../InstanceProviders/FuncInstanceProvider.cs | 18 + .../FuncInstanceProvider.cs.meta | 3 + .../InstanceProviders/InstanceProvider.cs | 23 + .../InstanceProvider.cs.meta | 3 + .../OpenGenericInstanceProvider.cs | 86 ++++ .../OpenGenericInstanceProvider.cs.meta | 11 + .../Internal/InstanceRegistrationBuilder.cs | 22 + .../InstanceRegistrationBuilder.cs.meta | 3 + .../Runtime/Internal/ListPool.cs | 71 +++ .../Runtime/Internal/ListPool.cs.meta | 3 + .../OpenGenericRegistrationBuilder.cs | 58 +++ .../OpenGenericRegistrationBuilder.cs.meta | 11 + .../Runtime/Internal/ReflectionInjector.cs | 124 +++++ .../Internal/ReflectionInjector.cs.meta | 11 + .../Runtime/Internal/RuntimeTypeCache.cs | 36 ++ .../Runtime/Internal/RuntimeTypeCache.cs.meta | 3 + .../Runtime/Internal/TypeAnalyzer.cs | 469 ++++++++++++++++++ .../Runtime/Internal/TypeAnalyzer.cs.meta | 11 + .../Runtime/Internal/TypeKeyHashTable2.cs | 146 ++++++ .../Internal/TypeKeyHashTable2.cs.meta | 3 + .../Runtime/Registration.cs | 39 ++ .../Runtime/Registration.cs.meta | 3 + .../Runtime/RegistrationBuilder.cs | 149 ++++++ .../Runtime/RegistrationBuilder.cs.meta | 11 + .../Runtime/Registry.cs | 208 ++++++++ .../Runtime/Registry.cs.meta | 11 + .../Runtime/Unity.meta | 8 + .../Runtime/Unity/ActionInstaller.cs | 22 + .../Runtime/Unity/ActionInstaller.cs.meta | 3 + .../Unity/ComponentRegistrationBuilder.cs | 121 +++++ .../ComponentRegistrationBuilder.cs.meta | 3 + .../Unity/ContainerBuilderUnityExtensions.cs | 420 ++++++++++++++++ .../ContainerBuilderUnityExtensions.cs.meta | 3 + .../Runtime/Unity/DisposeLoopItem.cs | 20 + .../Runtime/Unity/DisposeLoopItem.cs.meta | 3 + .../Runtime/Unity/EntryPointDispatcher.cs | 146 ++++++ .../Unity/EntryPointDispatcher.cs.meta | 3 + .../Unity/EntryPointExceptionHandler.cs | 19 + .../Unity/EntryPointExceptionHandler.cs.meta | 11 + .../Runtime/Unity/IInstaller.cs | 7 + .../Runtime/Unity/IInstaller.cs.meta | 3 + .../Runtime/Unity/InstanceProviders.meta | 3 + .../ExistingComponentProvider.cs | 44 ++ .../ExistingComponentProvider.cs.meta | 3 + .../FindComponentProvider.cs | 72 +++ .../FindComponentProvider.cs.meta | 3 + .../NewGameObjectProvider.cs | 51 ++ .../NewGameObjectProvider.cs.meta | 3 + .../PrefabComponentProvider.cs | 63 +++ .../PrefabComponentProvider.cs.meta | 3 + .../SystemInstanceProvider.cs | 81 +++ .../SystemInstanceProvider.cs.meta | 3 + .../UnmanagedSystemInstanceProvider.cs | 71 +++ .../UnmanagedSystemInstanceProvider.cs.meta | 3 + .../WorldInstanceProvider.cs | 48 ++ .../WorldInstanceProvider.cs.meta | 3 + .../Unity/LifetimeScope.AwakeScheduler.cs | 93 ++++ .../LifetimeScope.AwakeScheduler.cs.meta | 11 + .../Runtime/Unity/LifetimeScope.cs | 369 ++++++++++++++ .../Runtime/Unity/LifetimeScope.cs.meta | 11 + .../Unity/ObjectResolverUnityExtensions.cs | 292 +++++++++++ .../ObjectResolverUnityExtensions.cs.meta | 3 + .../Runtime/Unity/ParentReference.cs | 47 ++ .../Runtime/Unity/ParentReference.cs.meta | 3 + .../Runtime/Unity/PlayerLoopHelper.cs | 212 ++++++++ .../Runtime/Unity/PlayerLoopHelper.cs.meta | 11 + .../Runtime/Unity/PlayerLoopItem.cs | 349 +++++++++++++ .../Runtime/Unity/PlayerLoopItem.cs.meta | 11 + .../Runtime/Unity/PlayerLoopRunner.cs | 43 ++ .../Runtime/Unity/PlayerLoopRunner.cs.meta | 11 + .../Unity/SystemRegistrationBuilder.cs | 43 ++ .../Unity/SystemRegistrationBuilder.cs.meta | 3 + .../Runtime/Unity/UnmanagedSystemReference.cs | 70 +++ .../Unity/UnmanagedSystemReference.cs.meta | 3 + .../UnmanagedSystemRegistrationBuilder.cs | 47 ++ ...UnmanagedSystemRegistrationBuilder.cs.meta | 3 + .../Runtime/Unity/VContainerSettings.cs | 131 +++++ .../Runtime/Unity/VContainerSettings.cs.meta | 3 + .../Runtime/Unity/WorldConfigurationHelper.cs | 48 ++ .../Unity/WorldConfigurationHelper.cs.meta | 3 + .../Runtime/Unity/WorldRegistrationBuilder.cs | 26 + .../Unity/WorldRegistrationBuilder.cs.meta | 3 + .../Runtime/VContainer.asmdef | 29 ++ .../Runtime/VContainer.asmdef.meta | 7 + .../Runtime/VContainerException.cs | 14 + .../Runtime/VContainerException.cs.meta | 11 + .../jp.hadashikick.vcontainer/package.json | 17 + .../package.json.meta | 7 + Packages/manifest.json | 1 + Packages/packages-lock.json | 6 + 193 files changed, 8036 insertions(+) create mode 100644 Packages/jp.hadashikick.vcontainer/Editor.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef create mode 100644 Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Container.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Container.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef.meta create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs create mode 100644 Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs.meta create mode 100644 Packages/jp.hadashikick.vcontainer/package.json create mode 100644 Packages/jp.hadashikick.vcontainer/package.json.meta diff --git a/Packages/jp.hadashikick.vcontainer/Editor.meta b/Packages/jp.hadashikick.vcontainer/Editor.meta new file mode 100644 index 00000000..1ef9a115 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 08f5f24dc5b90493d9e57618faa4868f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics.meta b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics.meta new file mode 100644 index 00000000..b6113dff --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c9d3500c8eb84e07acfb1cb574d9f9e2 +timeCreated: 1632057810 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs new file mode 100644 index 00000000..4ded8f37 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace VContainer.Editor.Diagnostics +{ + // reflection call of UnityEditor.SplitterGUILayout + static class SplitterGUILayout + { + static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + + static readonly Lazy SplitterStateType = new Lazy(() => + { + var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState"); + return type; + }); + + static readonly Lazy SplitterStateCtor = new Lazy(() => + { + var type = SplitterStateType.Value; + return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null); + }); + + static readonly Lazy SplitterGUILayoutType = new Lazy(() => + { + var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout"); + return type; + }); + + static readonly Lazy BeginVerticalSplitInfo = new Lazy(() => + { + var type = SplitterGUILayoutType.Value; + return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { SplitterStateType.Value, typeof(GUILayoutOption[]) }, null); + }); + + static readonly Lazy EndVerticalSplitInfo = new Lazy(() => + { + var type = SplitterGUILayoutType.Value; + return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null); + }); + + static readonly Lazy BeginHorizontalSplitInfo = new Lazy(() => + { + var type = SplitterGUILayoutType.Value; + return type.GetMethod("BeginHorizontalSplit", flags, null, new Type[] { SplitterStateType.Value, typeof(GUILayoutOption[]) }, null); + }); + + static readonly Lazy EndHorizontalSplitInfo = new Lazy(() => + { + var type = SplitterGUILayoutType.Value; + return type.GetMethod("EndHorizontalSplit", flags, null, Type.EmptyTypes, null); + }); + + public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes) + { + return SplitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes }); + } + + public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options) + { + BeginVerticalSplitInfo.Value.Invoke(null, new object[] { splitterState, options }); + } + + public static void EndVerticalSplit() + { + EndVerticalSplitInfo.Value.Invoke(null, Array.Empty()); + } + + public static void BeginHorizontalSplit(object splitterState, params GUILayoutOption[] options) + { + BeginHorizontalSplitInfo.Value.Invoke(null, new object[] { splitterState, options }); + } + + public static void EndHorizontalSplit() + { + EndHorizontalSplitInfo.Value.Invoke(null, Array.Empty()); + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs.meta new file mode 100644 index 00000000..51623989 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/SplitterGUILayout.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: af963129cd84433ba74949eeb1c00542 +timeCreated: 1633102883 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs new file mode 100644 index 00000000..ce0d5e98 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace VContainer.Editor.Diagnostics +{ + static class TypeNameHelper + { + static readonly IReadOnlyDictionary TypeAlias = new Dictionary + { + { typeof(bool), "bool" }, + { typeof(byte), "byte" }, + { typeof(char), "char" }, + { typeof(decimal), "decimal" }, + { typeof(double), "double" }, + { typeof(float), "float" }, + { typeof(int), "int" }, + { typeof(long), "long" }, + { typeof(object), "object" }, + { typeof(sbyte), "sbyte" }, + { typeof(short), "short" }, + { typeof(string), "string" }, + { typeof(uint), "uint" }, + { typeof(ulong), "ulong" }, + { typeof(void), "void" } + }; + + public static string GetTypeAlias(Type type) + { + if (TypeAlias.TryGetValue(type, out var alias)) + { + return alias; + } + if (type.IsGenericType) + { + var typeParameters = type.GetGenericArguments().Select(GetTypeAlias); + var typeNameBase = type.Name.Split('`')[0]; + return $"{typeNameBase}<{string.Join(",", typeParameters)}>"; + } + return type.Name; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs.meta new file mode 100644 index 00000000..96d97188 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/TypeNameHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 639563c4569244d8a54a150c42c8de33 +timeCreated: 1633621836 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs new file mode 100644 index 00000000..f2d11676 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs @@ -0,0 +1,323 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using VContainer.Diagnostics; +using VContainer.Unity; + +namespace VContainer.Editor.Diagnostics +{ + public sealed class DiagnosticsInfoTreeViewItem : TreeViewItem + { + public string ScopeName { get; set; } + public DiagnosticsInfo DiagnosticsInfo { get; } + + public RegistrationBuilder RegistrationBuilder => DiagnosticsInfo.RegisterInfo.RegistrationBuilder; + public Registration Registration => DiagnosticsInfo.ResolveInfo.Registration; + public int? RefCount => DiagnosticsInfo.ResolveInfo.RefCount; + public long ResolveTime => DiagnosticsInfo.ResolveInfo.ResolveTime; + + public string TypeSummary => TypeNameHelper.GetTypeAlias(Registration.ImplementationType); + + public string ContractTypesSummary + { + get + { + if (Registration.InterfaceTypes != null) + { + var values = Registration.InterfaceTypes.Select(TypeNameHelper.GetTypeAlias); + return string.Join(", ", values); + } + return ""; + } + } + + public string RegisterSummary + { + get + { + if (RegistrationBuilder == null) + return ""; + + var type = RegistrationBuilder.GetType(); + if (type == typeof(RegistrationBuilder)) + { + return ""; + } + + var typeName = type.Name; + var suffixIndex = typeName.IndexOf("Builder"); + if (suffixIndex > 0) + { + typeName = typeName.Substring(0, suffixIndex); + } + suffixIndex = typeName.IndexOf("Registration"); + if (suffixIndex > 0) + { + typeName = typeName.Substring(0, suffixIndex); + } + + if (typeName.StartsWith("Instance") && TypeSummary.StartsWith("Func<")) + { + return "FuncFactory"; + } + + return typeName; + } + } + + public DiagnosticsInfoTreeViewItem(DiagnosticsInfo info) + { + ScopeName = info.ScopeName; + DiagnosticsInfo = info; + displayName = TypeSummary; + } + } + + public sealed class VContainerDiagnosticsInfoTreeView : TreeView + { + static readonly MultiColumnHeaderState.Column[] Columns = + { + new MultiColumnHeaderState.Column { headerContent = new GUIContent("Type") }, + new MultiColumnHeaderState.Column { headerContent = new GUIContent("ContractTypes"), canSort = false }, + new MultiColumnHeaderState.Column { headerContent = new GUIContent("Lifetime"), width = 15f }, + new MultiColumnHeaderState.Column { headerContent = new GUIContent("Register"), width = 15f }, + new MultiColumnHeaderState.Column { headerContent = new GUIContent("RefCount"), width = 5f }, + new MultiColumnHeaderState.Column { headerContent = new GUIContent("Scope"), width = 20f }, + new MultiColumnHeaderState.Column { headerContent = new GUIContent("Time"), width = 20f }, + }; + + static int idSeed; + static int NextId() => ++idSeed; + + const string SessionStateKeySortedColumnIndex = "VContainer.Editor.DiagnosticsInfoTreeView:sortedColumnIndex"; + + public bool Flatten + { + get => flatten; + set + { + flatten = value; + multiColumnHeader.ResizeToFit(); + } + } + + bool flatten; + + public VContainerDiagnosticsInfoTreeView() + : this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(Columns))) + { + } + + VContainerDiagnosticsInfoTreeView(TreeViewState state, MultiColumnHeader header) + : base(state, header) + { + rowHeight = 20; + showAlternatingRowBackgrounds = true; + showBorder = true; + header.sortingChanged += OnSortedChanged; + + header.ResizeToFit(); + Reload(); + + header.sortedColumnIndex = Math.Min( + header.state.columns.Length - 1, + SessionState.GetInt(SessionStateKeySortedColumnIndex, 0)); + } + + public DiagnosticsInfoTreeViewItem GetSelectedItem() + { + if (state.selectedIDs.Count <= 0) return null; + + var selectedId = state.selectedIDs[0]; + return GetRows().FirstOrDefault(x => x.id == selectedId) as DiagnosticsInfoTreeViewItem; + } + + public void ReloadAndSort() + { + var currentSelected = state.selectedIDs; + Reload(); + OnSortedChanged(multiColumnHeader); + state.selectedIDs = currentSelected; + } + + void OnSortedChanged(MultiColumnHeader multiColumnHeader) + { + var columnIndex = multiColumnHeader.sortedColumnIndex; + if (columnIndex < 0) return; + + SessionState.SetInt(SessionStateKeySortedColumnIndex, columnIndex); + var ascending = multiColumnHeader.IsSortedAscending(columnIndex); + + if (Flatten) + { + var items = rootItem.children.Cast(); + rootItem.children = new List(Sort(items, columnIndex, ascending)); + } + else + { + foreach (var sectionHeaderItem in rootItem.children) + { + var items = sectionHeaderItem.children.Cast(); + sectionHeaderItem.children = new List(Sort(items, columnIndex, ascending)); + } + } + BuildRows(rootItem); + } + + protected override TreeViewItem BuildRoot() + { + var root = new TreeViewItem { depth = -1 }; + var children = new List(); + + if (VContainerSettings.DiagnosticsEnabled) + { + if (Flatten) + { + var infos = DiagnositcsContext.GetDiagnosticsInfos(); + foreach (var info in infos) + { + children.Add(new DiagnosticsInfoTreeViewItem(info) + { + id = NextId(), + depth = 0, + ScopeName = info.ScopeName, + }); + } + } + else + { + var grouped = DiagnositcsContext.GetGroupedDiagnosticsInfos(); + foreach (var scope in grouped) + { + var sectionHeaderItem = new TreeViewItem(NextId(), 0, scope.Key); + children.Add(sectionHeaderItem); + SetExpanded(sectionHeaderItem.id, true); + + foreach (var info in scope) + { + AddDependencyItemsRecursive(info, sectionHeaderItem); + } + } + } + } + + root.children = children; + return root; + } + + protected override bool CanMultiSelect(TreeViewItem item) => false; + + protected override void RowGUI(RowGUIArgs args) + { + var item = args.item as DiagnosticsInfoTreeViewItem; + if (item is null) + { + var cellRect = args.GetCellRect(0); + GUI.BeginGroup(cellRect); + { + args.rowRect = new Rect(0, 0, cellRect.width, cellRect.height); + base.RowGUI(args); + } + GUI.EndGroup(); + return; + } + + for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++) + { + var cellRect = args.GetCellRect(visibleColumnIndex); + // CenterRectUsingSingleLineHeight(ref cellRect); + var columnIndex = args.GetColumn(visibleColumnIndex); + + var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label; + labelStyle.alignment = TextAnchor.MiddleLeft; + + switch (columnIndex) + { + case 0: + GUI.BeginGroup(cellRect); + { + args.rowRect = new Rect(0, 0, cellRect.width, cellRect.height); + base.RowGUI(args); + } + GUI.EndGroup(); + break; + case 1: + EditorGUI.LabelField(cellRect, item.ContractTypesSummary, labelStyle); + break; + case 2: + EditorGUI.LabelField(cellRect, item.Registration.Lifetime.ToString(), labelStyle); + break; + case 3: + EditorGUI.LabelField(cellRect, item.RegisterSummary, labelStyle); + break; + case 4: + EditorGUI.LabelField(cellRect, item.RefCount.ToString(), labelStyle); + break; + case 5: + EditorGUI.LabelField(cellRect, item.ScopeName, labelStyle); + break; + case 6: + EditorGUI.LabelField(cellRect, item.ResolveTime.ToString(), labelStyle); + break; + default: + throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null); + } + } + } + + void AddDependencyItemsRecursive(DiagnosticsInfo info, TreeViewItem parent) + { + var item = new DiagnosticsInfoTreeViewItem(info) + { + id = NextId(), + depth = parent.depth + 1 + }; + parent.AddChild(item); + SetExpanded(item.id, item.depth <= 1); + + foreach (var dependency in info.Dependencies) + { + AddDependencyItemsRecursive(dependency, item); + } + } + + IEnumerable Sort( + IEnumerable items, + int sortedColumnIndex, + bool ascending) + { + switch (sortedColumnIndex) + { + case 0: + return ascending + ? items.OrderBy(x => x.TypeSummary) + : items.OrderByDescending(x => x.TypeSummary); + case 2: + return ascending + ? items.OrderBy(x => x.Registration.Lifetime) + : items.OrderByDescending(x => x.Registration.Lifetime); + case 3: + return ascending + ? items.OrderBy(x => x.RegisterSummary) + : items.OrderByDescending(x => x.RegisterSummary); + case 4: + return ascending + ? items.OrderBy(x => x.RefCount) + : items.OrderByDescending(x => x.RefCount); + case 5: + return ascending + ? items.OrderBy(x => x.ScopeName) + : items.OrderByDescending(x => x.ScopeName); + case 6: + return ascending + ? items.OrderBy(x => x.ResolveTime) + : items.OrderByDescending(x => x.ResolveTime); + default: + throw new ArgumentOutOfRangeException(nameof(sortedColumnIndex), sortedColumnIndex, null); + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs.meta new file mode 100644 index 00000000..ac76619f --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsTreeView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0d65b5790cc048599cb6e65bb859e6aa +timeCreated: 1632058224 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs new file mode 100644 index 00000000..d4f70530 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs @@ -0,0 +1,216 @@ +using System; +using UnityEditor; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using VContainer.Diagnostics; +using VContainer.Unity; + +namespace VContainer.Editor.Diagnostics +{ + public sealed class VContainerDiagnosticsWindow : EditorWindow + { + static VContainerDiagnosticsWindow window; + + static readonly GUIContent FlattenHeadContent = EditorGUIUtility.TrTextContent("Flatten", "Flatten dependencies"); + static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View"); + + internal static bool EnableAutoReload; + internal static bool EnableCaptureStackTrace; + + [MenuItem("Window/VContainer Diagnostics")] + public static void OpenWindow() + { + if (window != null) + { + window.Close(); + } + GetWindow("VContainer Diagnostics").Show(); + } + + GUIStyle TableListStyle + { + get + { + var style = new GUIStyle("CN Box"); + style.margin.top = 0; + style.padding.left = 3; + return style; + } + } + + GUIStyle DetailsStyle + { + get + { + var detailsStyle = new GUIStyle("CN Message"); + detailsStyle.wordWrap = false; + detailsStyle.stretchHeight = true; + detailsStyle.margin.right = 15; + return detailsStyle; + } + } + + VContainerDiagnosticsInfoTreeView treeView; + VContainerInstanceTreeView instanceTreeView; + SearchField searchField; + + object verticalSplitterState; + object horizontalSplitterState; + Vector2 tableScrollPosition; + Vector2 detailsScrollPosition; + Vector2 instanceScrollPosition; + + public void Reload(IObjectResolver resolver) + { + treeView.ReloadAndSort(); + Repaint(); + } + + void OnPlayModeStateChange(PlayModeStateChange state) + { + treeView.ReloadAndSort(); + Repaint(); + } + + void OnEnable() + { + window = this; // set singleton. + verticalSplitterState = SplitterGUILayout.CreateSplitterState(new [] { 75f, 25f }, new [] { 32, 32 }, null); + horizontalSplitterState = SplitterGUILayout.CreateSplitterState(new[] { 75, 25f }, new[] { 32, 32 }, null); + treeView = new VContainerDiagnosticsInfoTreeView(); + instanceTreeView = new VContainerInstanceTreeView(); + searchField = new SearchField(); + + DiagnositcsContext.OnContainerBuilt += Reload; + EditorApplication.playModeStateChanged += OnPlayModeStateChange; + } + + void OnDisable() + { + DiagnositcsContext.OnContainerBuilt -= Reload; + EditorApplication.playModeStateChanged -= OnPlayModeStateChange; + } + + void OnGUI() + { + RenderHeadPanel(); + + SplitterGUILayout.BeginVerticalSplit(verticalSplitterState, Array.Empty()); + { + SplitterGUILayout.BeginHorizontalSplit(horizontalSplitterState); + { + RenderBuildPanel(); + RenderInstancePanel(); + } + SplitterGUILayout.EndHorizontalSplit(); + + RenderStackTracePanel(); + } + SplitterGUILayout.EndVerticalSplit(); + } + + void RenderHeadPanel() + { + using (new EditorGUILayout.VerticalScope()) + using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar)) + { + var flattenOn = GUILayout.Toggle(treeView.Flatten, FlattenHeadContent, EditorStyles.toolbarButton); + if (flattenOn != treeView.Flatten) + { + treeView.Flatten = flattenOn; + treeView.ReloadAndSort(); + Repaint(); + } + + GUILayout.FlexibleSpace(); + + treeView.searchString = searchField.OnToolbarGUI(treeView.searchString); + + if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton)) + { + treeView.ReloadAndSort(); + Repaint(); + } + } + } + + void RenderBuildPanel() + { + using (new EditorGUILayout.VerticalScope(TableListStyle)) + using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(tableScrollPosition, + // true, + // true, + GUILayout.ExpandWidth(true), + GUILayout.MaxWidth(2000f))) + { + tableScrollPosition = scrollViewScope.scrollPosition; + + var controlRect = EditorGUILayout.GetControlRect( + GUILayout.ExpandHeight(true), + GUILayout.ExpandWidth(true)); + treeView?.OnGUI(controlRect); + } + } + + void RenderInstancePanel() + { + if (!VContainerSettings.DiagnosticsEnabled) + { + return; + } + + var selectedItem = treeView.GetSelectedItem(); + if (selectedItem?.DiagnosticsInfo.ResolveInfo is ResolveInfo resolveInfo) + { + if (resolveInfo.Instances.Count > 0) + { + instanceTreeView.CurrentDiagnosticsInfo = selectedItem.DiagnosticsInfo; + instanceTreeView.Reload(); + + using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(instanceScrollPosition, GUILayout.ExpandHeight(true))) + { + instanceScrollPosition = scrollViewScope.scrollPosition; + var controlRect = EditorGUILayout.GetControlRect( + GUILayout.ExpandHeight(true), + GUILayout.ExpandWidth(true)); + instanceTreeView?.OnGUI(controlRect); + } + } + else + { + EditorGUILayout.SelectableLabel("No instance reference"); + } + } + } + + void RenderStackTracePanel() + { + var message = ""; + if (VContainerSettings.DiagnosticsEnabled) + { + var selectedItem = treeView.GetSelectedItem(); + if (selectedItem?.DiagnosticsInfo?.RegisterInfo is RegisterInfo registerInfo) + { + message = $"Register at {registerInfo.GetHeadline()}" + + Environment.NewLine + + Environment.NewLine + + selectedItem.DiagnosticsInfo.RegisterInfo.StackTrace; + } + } + else + { + message = "VContainer Diagnostics collector is disabled. To enable, please check VContainerSettings."; + } + using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(detailsScrollPosition)) + { + detailsScrollPosition = scrollViewScope.scrollPosition; + var vector = DetailsStyle.CalcSize(new GUIContent(message)); + EditorGUILayout.SelectableLabel(message, DetailsStyle, + GUILayout.ExpandHeight(true), + GUILayout.ExpandWidth(true), + GUILayout.MinWidth(vector.x), + GUILayout.MinHeight(vector.y)); + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs.meta new file mode 100644 index 00000000..e43c1faf --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerDiagnosticsWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e3e4c1066bed4185a96639d5b1017922 +timeCreated: 1632057811 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs new file mode 100644 index 00000000..d0dc68c4 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using VContainer.Diagnostics; + +namespace VContainer.Editor.Diagnostics +{ + public sealed class VContainerInstanceTreeView : TreeView + { + static int idSeed; + static int NextId() => ++idSeed; + + static string Stringify(object instance) + { + if (ReferenceEquals(instance, null)) + return "null"; + if (instance is UnityEngine.Object obj && obj == null) + return "null or destroyed"; + return instance.ToString(); + } + + public DiagnosticsInfo CurrentDiagnosticsInfo { get; set; } + + public VContainerInstanceTreeView() : base(new TreeViewState()) + { + Reload(); + } + + protected override TreeViewItem BuildRoot() + { + var root = new TreeViewItem(NextId(), -1, "Root"); + var children = new List(); + if (CurrentDiagnosticsInfo is DiagnosticsInfo info) + { + for (var i = 0; i < info.ResolveInfo.Instances.Count; i++) + { + var instance = info.ResolveInfo.Instances[i]; + var item = new TreeViewItem(NextId(), 0, + $"({TypeNameHelper.GetTypeAlias(instance.GetType())}) {Stringify(instance)}"); + AddProperties(instance, item); + children.Add(item); + SetExpanded(item.id, true); + } + } + + root.children = children; + return root; + } + + void AddProperties(object instance, TreeViewItem parent) + { + if (instance == null) return; + + var type = instance.GetType(); + var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + foreach (var prop in props) + { + if (prop.PropertyType.IsSubclassOf(typeof(UnityEngine.Object)) && + prop.IsDefined(typeof(ObsoleteAttribute), true)) + { + continue; + } + + try + { + if (prop.CanRead) + { + var value = prop.GetValue(instance); + var displayName = $"{prop.Name} = ({TypeNameHelper.GetTypeAlias(prop.PropertyType)}) {Stringify(value)}"; + parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName)); + } + else + { + var displayName = $"{prop.Name} = (write-only {TypeNameHelper.GetTypeAlias(prop.PropertyType)})"; + parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName)); + } + } + catch (MissingReferenceException) + { + } + catch (NotSupportedException) + { + } + } + + foreach (var field in fields) + { + if ((field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) && + field.IsDefined(typeof(ObsoleteAttribute), true)) || + field.IsDefined(typeof(CompilerGeneratedAttribute), true)) + { + continue; + } + + try + { + var value = field.GetValue(instance); + var displayName = $"{field.Name} = ({TypeNameHelper.GetTypeAlias(field.FieldType)}) {Stringify(value)}"; + parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName)); + } + catch (MissingReferenceException) + { + } + catch (NotSupportedException) + { + } + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs.meta new file mode 100644 index 00000000..53433c88 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/Diagnostics/VContainerInstanceTreeView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 538613b38d4a4facb28765eb06374863 +timeCreated: 1633569303 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs b/Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs new file mode 100644 index 00000000..27c84710 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using VContainer.Unity; + +namespace VContainer.Editor +{ + [CustomPropertyDrawer(typeof(ParentReference))] + public sealed class ParentReferencePropertyDrawer : PropertyDrawer + { + static string[] GetAllTypeNames() + { + return new List { "None" } + .Concat(TypeCache.GetTypesDerivedFrom() + .Where(x => !x.IsAbstract) + .Select(type => type.FullName)) + .ToArray(); + } + + static string GetLabel(Type type) => $"{type.Namespace}/{type.Name}"; + + string[] names; + + public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label) + { + if (names == null) + { + names = GetAllTypeNames(); + if (prop.serializedObject.targetObject is LifetimeScope lifetimeScope) + { + var lifetimeScopeName = lifetimeScope.GetType().FullName; + names = names.Where(name => name != lifetimeScopeName).ToArray(); + } + } + + var typeNameProp = prop.FindPropertyRelative("TypeName"); + + using (new EditorGUI.PropertyScope(rect, label, prop)) + { + var labelRect = new Rect(rect.x, rect.y, rect.width, 18f); + var popupRect = new Rect(rect.x, rect.y + labelRect.height, rect.width , 18f); + + var index = Array.IndexOf(names, typeNameProp.stringValue); + if (index < 0) index = 0; + + EditorGUI.LabelField(labelRect, "Parent"); + using (var check = new EditorGUI.ChangeCheckScope()) + { + index = EditorGUI.Popup(popupRect, index, names); + if (check.changed) + { + typeNameProp.stringValue = names[index]; + } + } + } + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return 18f + 18f + 4f; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs.meta new file mode 100644 index 00000000..6dd44c3a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/ParentReferencePropertyDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 51521f95bbd442b08bc2b53efcdeff29 +timeCreated: 1594556689 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs b/Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs new file mode 100644 index 00000000..bd4b9ad0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs @@ -0,0 +1,113 @@ +using System.IO; +using UnityEditor; +using UnityEngine; +using VContainer.Unity; + +#if UNITY_2020_2_OR_NEWER +using UnityEditor.Compilation; +#endif + +namespace VContainer.Editor +{ + public sealed class ScriptTemplateProcessor : UnityEditor.AssetModificationProcessor + { +#if UNITY_2020_2_OR_NEWER + const string RootNamespaceBeginTag = "#ROOTNAMESPACEBEGIN#"; + const string RootNamespaceEndTag = "#ROOTNAMESPACEEND#"; +#endif + + const string MonoInstallerTemplate = + "using VContainer;\n" + + "using VContainer.Unity;\n" + + "\n" + +#if UNITY_2020_2_OR_NEWER + RootNamespaceBeginTag + "\n" + +#endif + "public class #SCRIPTNAME# : LifetimeScope\n" + + "{\n" + + " protected override void Configure(IContainerBuilder builder)\n" + + " {\n" + + " }\n" + + "}\n" + +#if UNITY_2020_2_OR_NEWER + RootNamespaceEndTag + "\n" + +#endif + ""; + + public static void OnWillCreateAsset(string metaPath) + { + if (VContainerSettings.Instance != null && VContainerSettings.Instance.DisableScriptModifier) + { + return; + } + + var suffixIndex = metaPath.LastIndexOf(".meta"); + if (suffixIndex < 0) + { + return; + } + + var scriptPath = metaPath.Substring(0, suffixIndex); + var basename = Path.GetFileNameWithoutExtension(scriptPath); + var extname = Path.GetExtension(scriptPath); + if (extname != ".cs") + { + return; + } + + if (!scriptPath.EndsWith("LifetimeScope.cs")) + { + return; + } + + var content = MonoInstallerTemplate.Replace("#SCRIPTNAME#", basename); + +#if UNITY_2020_2_OR_NEWER + { + var rootNamespace = CompilationPipeline.GetAssemblyRootNamespaceFromScriptPath(scriptPath); + content = RemoveOrInsertNamespaceSimple(content, rootNamespace); + } +#endif + + if (scriptPath.StartsWith("Assets/")) + { + scriptPath = scriptPath.Substring("Assets/".Length); + } + + var fullPath = Path.Combine(Application.dataPath, scriptPath); + File.WriteAllText(fullPath, content); + AssetDatabase.Refresh(); + } + +#if UNITY_2020_2_OR_NEWER + // https://github.com/Unity-Technologies/UnityCsReference/blob/2020.2/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs#L495-L550 + static string RemoveOrInsertNamespaceSimple(string content, string rootNamespace) + { + const char eol = '\n'; + + if (string.IsNullOrWhiteSpace(rootNamespace)) + { + return content + .Replace(RootNamespaceBeginTag + eol, "") + .Replace(RootNamespaceEndTag + eol, ""); + } + + var lines = content.Split(eol); + + var startAt = ArrayUtility.IndexOf(lines, RootNamespaceBeginTag); + var endAt = ArrayUtility.IndexOf(lines, RootNamespaceEndTag); + + lines[startAt] = $"namespace {rootNamespace}\n{{"; + { + for (var i = startAt + 1; i < endAt; ++i) + { + lines[i] = $" {lines[i]}"; + } + } + lines[endAt] = "}"; + + return string.Join(eol.ToString(), lines); + } +#endif + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs.meta b/Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs.meta new file mode 100644 index 00000000..1d61d621 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/ScriptTemplateModifier.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e4beca40a4df74e7787b464769f15620 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef b/Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef new file mode 100644 index 00000000..ce5a2140 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef @@ -0,0 +1,18 @@ +{ + "name": "VContainer.Editor", + "rootNamespace": "", + "references": [ + "VContainer" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef.meta b/Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef.meta new file mode 100644 index 00000000..5a30e58d --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Editor/VContainer.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7bce08a00b4d54cd694096abadbb897e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime.meta b/Packages/jp.hadashikick.vcontainer/Runtime.meta new file mode 100644 index 00000000..494abce4 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 652e91fc271c04624b08f658dc62397e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations.meta new file mode 100644 index 00000000..018fddcd --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1eb05967f0cc40c59ebf679dcc359905 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs new file mode 100644 index 00000000..5733e348 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) 2024 left (https://github.com/left-/) +// Copyright (c) 2020 hadashiA +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#if UNITY_2021_3_OR_NEWER +using System; +#if UNITY_2023_1_OR_NEWER +using UnityEngine; +#else +using Awaitable = System.Threading.Tasks.Task; +#endif + +namespace VContainer.Unity +{ + internal static class AwaitableExtensions + { + public static async Awaitable Forget(this Awaitable awaitable, + EntryPointExceptionHandler exceptionHandler = null) + { + try + { + await awaitable; + } + catch (Exception ex) + { + if (exceptionHandler != null) + exceptionHandler.Publish(ex); + else + throw; + } + } + } +} +#endif diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs.meta new file mode 100644 index 00000000..a5ce8afe --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/AwaitableExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9cbf97ce69c64b73a70d6514c63fba00 +timeCreated: 1717651770 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs new file mode 100644 index 00000000..5426a765 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs @@ -0,0 +1,16 @@ +using System.Threading; +#if VCONTAINER_UNITASK_INTEGRATION +using Awaitable = Cysharp.Threading.Tasks.UniTask; +#elif UNITY_2023_1_OR_NEWER +using UnityEngine; +#else +using Awaitable = System.Threading.Tasks.Task; +#endif + +namespace VContainer.Unity +{ + public interface IAsyncStartable + { + Awaitable StartAsync(CancellationToken cancellation = default); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs.meta new file mode 100644 index 00000000..e5684a80 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IAsyncStartable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cf8c4b2a3efd465ba893956bd3e16207 +timeCreated: 1612009971 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs new file mode 100644 index 00000000..421462b9 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IFixedTickable + { + void FixedTick(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs.meta new file mode 100644 index 00000000..6975c05c --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IFixedTickable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e83f6a64504c4d1da11c1f6d046a0ecc +timeCreated: 1612009716 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs new file mode 100644 index 00000000..5e324aa5 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IInitializable + { + void Initialize(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs.meta new file mode 100644 index 00000000..db43c7a9 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IInitializable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7e3cb2445077468db349bf4dadae84be +timeCreated: 1612009493 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs new file mode 100644 index 00000000..e141093d --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface ILateTickable + { + void LateTick(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs.meta new file mode 100644 index 00000000..e6350859 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ILateTickable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9b82ce93c2104d6c91ce9868c02dafc7 +timeCreated: 1612009914 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs new file mode 100644 index 00000000..8c5172d0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IPostFixedTickable + { + void PostFixedTick(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs.meta new file mode 100644 index 00000000..e32dd005 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostFixedTickable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2bf16fe4c16a465b8a8f1d68a8685e11 +timeCreated: 1612009787 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs new file mode 100644 index 00000000..08e9da38 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IPostInitializable + { + void PostInitialize(); + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs.meta new file mode 100644 index 00000000..f168fa6f --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostInitializable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ff6e6d963c0544649d8fedd94d25b8da +timeCreated: 1612009543 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs new file mode 100644 index 00000000..5d7fdd8c --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IPostLateTickable + { + void PostLateTick(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs.meta new file mode 100644 index 00000000..91eeb101 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostLateTickable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dcd967d55bd245408860956770845c3d +timeCreated: 1612009947 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs new file mode 100644 index 00000000..5307a2c7 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IPostStartable + { + void PostStart(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs.meta new file mode 100644 index 00000000..dd73afe1 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostStartable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 90378302abd9404eb23d078553c60031 +timeCreated: 1612009663 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs new file mode 100644 index 00000000..b3d4eca2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IPostTickable + { + void PostTick(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs.meta new file mode 100644 index 00000000..a1bacf3e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IPostTickable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 968f5691c67f4f64b8deed98e6a62cc2 +timeCreated: 1612009893 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs new file mode 100644 index 00000000..bb6378c5 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IStartable + { + void Start(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs.meta new file mode 100644 index 00000000..27ea39dd --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/IStartable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 29296d5987f24dce8ee347cc39fdf1e0 +timeCreated: 1612009633 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs new file mode 100644 index 00000000..f8cfb080 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface ITickable + { + void Tick(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs.meta new file mode 100644 index 00000000..7fd20e91 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/ITickable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e6460471a1864836afaf0617f182a721 +timeCreated: 1612009837 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs new file mode 100644 index 00000000..19a353f5 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace VContainer +{ + public class PreserveAttribute : Attribute + { + } + +#if UNITY_2018_4_OR_NEWER + [JetBrains.Annotations.MeansImplicitUse( + JetBrains.Annotations.ImplicitUseKindFlags.Access | + JetBrains.Annotations.ImplicitUseKindFlags.Assign | + JetBrains.Annotations.ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +#endif + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] + public class InjectAttribute : PreserveAttribute + { + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)] + public class InjectIgnoreAttribute : Attribute + { + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs.meta new file mode 100644 index 00000000..d7b93c8a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/InjectAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9445a99bad3a60af9f20ca441c14bca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs new file mode 100644 index 00000000..ab0b72ba --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace VContainer +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] + public class KeyAttribute : PreserveAttribute + { + public object Key { get; } + + public KeyAttribute(object key = null) + { + Key = key; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs.meta new file mode 100644 index 00000000..8000b0be --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Annotations/KeyAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53840c03444a04ed09ef84747bf8d2af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs b/Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs new file mode 100644 index 00000000..31cd7fc0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("VContainer.Tests")] +[assembly: InternalsVisibleTo("VContainer.StandaloneTests")] +[assembly: InternalsVisibleTo("VContainer.Editor")] +[assembly: InternalsVisibleTo("Unity.VContainer.CodeGen")] \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs.meta new file mode 100644 index 00000000..b4942a4e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6158fbf642b64910aac0b04e9a4d62ea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Container.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Container.cs new file mode 100644 index 00000000..5f7abfc5 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Container.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using VContainer.Diagnostics; +using VContainer.Internal; + +namespace VContainer +{ + public interface IObjectResolver : IDisposable + { + object ApplicationOrigin { get; } + DiagnosticsCollector Diagnostics { get; set; } + + /// + /// Resolve from type with or without key + /// + /// + /// This version of resolve looks for all of scopes + /// + object Resolve(Type type, object key = null); + + /// + /// Try resolve from type with or without key + /// + /// + /// This version of resolve looks for all of scopes + /// + /// Successfully resolved + bool TryResolve(Type type, out object resolved, object key = null); + + /// + /// Resolve from meta with registration + /// + /// + /// This version of resolve will look for instances from only the registration information already founds. + /// + object Resolve(Registration registration); + + IScopedObjectResolver CreateScope(Action installation = null); + + void Inject(object instance); + bool TryGetRegistration(Type type, out Registration registration, object key = null); + } + + public interface IScopedObjectResolver : IObjectResolver + { + IObjectResolver Root { get; } + IScopedObjectResolver Parent { get; } + } + + public enum Lifetime + { + Transient, + Singleton, + Scoped + } + + public sealed class ScopedContainer : IScopedObjectResolver + { + public IObjectResolver Root { get; } + public IScopedObjectResolver Parent { get; } + public object ApplicationOrigin { get; } + public DiagnosticsCollector Diagnostics { get; set; } + + readonly Registry registry; + readonly ConcurrentDictionary> sharedInstances = new ConcurrentDictionary>(); + readonly CompositeDisposable disposables = new CompositeDisposable(); + readonly Func> createInstance; + + internal ScopedContainer( + Registry registry, + IObjectResolver root, + IScopedObjectResolver parent = null, + object applicationOrigin = null) + { + Root = root; + Parent = parent; + ApplicationOrigin = applicationOrigin; + this.registry = registry; + createInstance = registration => + { + return new Lazy(() => registration.SpawnInstance(this)); + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object Resolve(Type type, object key = null) + { + if (TryFindRegistration(type, key, out var registration)) + { + return Resolve(registration); + } + throw new VContainerException(type, $"No such registration of type: {type} {(key == null ? string.Empty : $"with Key: {key}")}"); + } + + public bool TryResolve(Type type, out object resolved, object key = null) + { + if (TryFindRegistration(type, key, out var registration)) + { + resolved = Resolve(registration); + return true; + } + + resolved = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object Resolve(Registration registration) + { + if (Diagnostics != null) + { + return Diagnostics.TraceResolve(registration, ResolveCore); + } + return ResolveCore(registration); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IScopedObjectResolver CreateScope(Action installation = null) + { + var containerBuilder = new ScopedContainerBuilder(Root, this) + { + ApplicationOrigin = ApplicationOrigin + }; + installation?.Invoke(containerBuilder); + return containerBuilder.BuildScope(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inject(object instance) + { + var injector = InjectorCache.GetOrBuild(instance.GetType()); + injector.Inject(instance, this, null); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetRegistration(Type type, out Registration registration, object key = null) + => registry.TryGet(type, key, out registration); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + if (Diagnostics != null) + { + Diagnostics.Clear(); + } + disposables.Dispose(); + sharedInstances.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object ResolveCore(Registration registration) + { + switch (registration.Lifetime) + { + case Lifetime.Singleton: + if (Parent is null) + return Root.Resolve(registration); + + if (!registry.Exists(registration.ImplementationType, registration.Key)) + return Parent.Resolve(registration); + + return CreateTrackedInstance(registration); + + case Lifetime.Scoped: + return CreateTrackedInstance(registration); + + default: + return registration.SpawnInstance(this); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object CreateTrackedInstance(Registration registration) + { + var lazy = sharedInstances.GetOrAdd(registration, createInstance); + var created = lazy.IsValueCreated; + var instance = lazy.Value; + if (!created && instance is IDisposable disposable && !(registration.Provider is ExistingInstanceProvider)) + { + disposables.Add(disposable); + } + return instance; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool TryFindRegistration(Type type, object key, out Registration registration) + { + IScopedObjectResolver scope = this; + while (scope != null) + { + if (scope.TryGetRegistration(type, out registration, key)) + { + return true; + } + scope = scope.Parent; + } + + registration = default; + return false; + } + } + + public sealed class Container : IObjectResolver + { + public object ApplicationOrigin { get; } + public DiagnosticsCollector Diagnostics { get; set; } + + readonly Registry registry; + readonly IScopedObjectResolver rootScope; + readonly ConcurrentDictionary> sharedInstances = new ConcurrentDictionary>(); + readonly CompositeDisposable disposables = new CompositeDisposable(); + readonly Func> createInstance; + + internal Container(Registry registry, object applicationOrigin = null) + { + this.registry = registry; + rootScope = new ScopedContainer(registry, this, applicationOrigin: applicationOrigin); + + createInstance = registration => + { + return new Lazy(() => registration.SpawnInstance(this)); + }; + + ApplicationOrigin = applicationOrigin; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object Resolve(Type type, object key = null) + { + if (TryGetRegistration(type, out var registration, key)) + { + return Resolve(registration); + } + throw new VContainerException(type, $"No such registration of type: {type} with Key: {key}"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryResolve(Type type, out object resolved, object key = null) + { + if (TryGetRegistration(type, out var registration, key)) + { + resolved = Resolve(registration); + return true; + } + + resolved = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object Resolve(Registration registration) + { + if (Diagnostics != null) + { + return Diagnostics.TraceResolve(registration, ResolveCore); + } + return ResolveCore(registration); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IScopedObjectResolver CreateScope(Action installation = null) + => rootScope.CreateScope(installation); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inject(object instance) + { + var injector = InjectorCache.GetOrBuild(instance.GetType()); + injector.Inject(instance, this, null); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetRegistration(Type type, out Registration registration, object key = null) + => registry.TryGet(type, key, out registration); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + if (Diagnostics != null) + { + Diagnostics.Clear(); + } + rootScope.Dispose(); + disposables.Dispose(); + sharedInstances.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object ResolveCore(Registration registration) + { + switch (registration.Lifetime) + { + case Lifetime.Singleton: + var singleton = sharedInstances.GetOrAdd(registration, createInstance); + if (!singleton.IsValueCreated && singleton.Value is IDisposable disposable && !(registration.Provider is ExistingInstanceProvider)) + { + disposables.Add(disposable); + } + return singleton.Value; + + case Lifetime.Scoped: + return rootScope.Resolve(registration); + + default: + return registration.SpawnInstance(this); + } + } + } +} + diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Container.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Container.cs.meta new file mode 100644 index 00000000..4ca648e2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Container.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb5df50ba586a47e5a93a5045e69bca7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs new file mode 100644 index 00000000..22969228 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using VContainer.Diagnostics; +using VContainer.Internal; +#if VCONTAINER_PARALLEL_CONTAINER_BUILD +using System.Threading.Tasks; +#endif + +namespace VContainer +{ + public interface IContainerBuilder + { + object ApplicationOrigin { get; set; } + DiagnosticsCollector Diagnostics { get; set; } + int Count { get; } + RegistrationBuilder this[int index] { get; set; } + + T Register(T registrationBuilder) where T : RegistrationBuilder; + void RegisterBuildCallback(Action container); + bool Exists(Type type, bool includeInterfaceTypes = false, bool findParentScopes = false); + } + + public sealed class ScopedContainerBuilder : ContainerBuilder + { + readonly IObjectResolver root; + readonly IScopedObjectResolver parent; + + internal ScopedContainerBuilder(IObjectResolver root, IScopedObjectResolver parent) + { + this.root = root; + this.parent = parent; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IScopedObjectResolver BuildScope() + { + var registry = BuildRegistry(); + var container = new ScopedContainer(registry, root, parent, ApplicationOrigin); + container.Diagnostics = Diagnostics; + EmitCallbacks(container); + return container; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override IObjectResolver Build() => BuildScope(); + + public override bool Exists(Type type, bool includeInterfaceTypes = false, bool findParentScopes = false) + { + if (base.Exists(type, includeInterfaceTypes, findParentScopes)) + { + return true; + } + + if (findParentScopes) + { + var next = parent; + while (next != null) + { + if (next.TryGetRegistration(type, out var registration)) + { + if (includeInterfaceTypes || registration.ImplementationType == type) + { + return true; + } + } + next = next.Parent; + } + } + return false; + } + } + + public class ContainerBuilder : IContainerBuilder + { + public object ApplicationOrigin { get; set; } + + public int Count => registrationBuilders.Count; + + public RegistrationBuilder this[int index] + { + get => registrationBuilders[index]; + set => registrationBuilders[index] = value; + } + + public DiagnosticsCollector Diagnostics + { + get => diagnostics; + set + { + diagnostics = value; + diagnostics?.Clear(); + } + } + + readonly List registrationBuilders = new List(); + Action buildCallback; + DiagnosticsCollector diagnostics; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Register(T registrationBuilder) where T : RegistrationBuilder + { + registrationBuilders.Add(registrationBuilder); + Diagnostics?.TraceRegister(new RegisterInfo(registrationBuilder)); + return registrationBuilder; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RegisterBuildCallback(Action callback) + { + buildCallback += callback; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual bool Exists(Type type, bool includeInterfaceTypes = false, bool findParentScopes = false) + { + foreach (var registrationBuilder in registrationBuilders) + { + if (registrationBuilder.ImplementationType == type || + includeInterfaceTypes && registrationBuilder.InterfaceTypes?.Contains(type) == true) + { + return true; + } + } + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual IObjectResolver Build() + { + var registry = BuildRegistry(); + var container = new Container(registry, ApplicationOrigin); + container.Diagnostics = Diagnostics; + EmitCallbacks(container); + return container; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected Registry BuildRegistry() + { + var registrations = new Registration[registrationBuilders.Count + 1]; + +#if VCONTAINER_PARALLEL_CONTAINER_BUILD + Parallel.For(0, registrationBuilders.Count, i => + { + var registrationBuilder = registrationBuilders[i]; + var registration = registrationBuilder.Build(); + Diagnostics?.TraceBuild(registrationBuilder, registration); + registrations[i] = registration; + }); +#else + for (var i = 0; i < registrationBuilders.Count; i++) + { + var registrationBuilder = registrationBuilders[i]; + var registration = registrationBuilder.Build(); + Diagnostics?.TraceBuild(registrationBuilder, registration); + registrations[i] = registration; + } +#endif + registrations[registrations.Length - 1] = new Registration( + typeof(IObjectResolver), + Lifetime.Transient, + null, + ContainerInstanceProvider.Default); + + var registry = Registry.Build(registrations); + TypeAnalyzer.CheckCircularDependency(registrations, registry); + + return registry; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void EmitCallbacks(IObjectResolver container) + { + buildCallback?.Invoke(container); + Diagnostics?.NotifyContainerBuilt(container); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs.meta new file mode 100644 index 00000000..dab5f9f5 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc776071549c945db9e79411839a1bb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs new file mode 100644 index 00000000..113b949f --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using VContainer.Internal; + +namespace VContainer +{ + public static class ContainerBuilderExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Type type, + Lifetime lifetime) => + builder.Register(type.IsGenericType && type.IsGenericTypeDefinition + ? new OpenGenericRegistrationBuilder(type, lifetime) + : new RegistrationBuilder(type, lifetime)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Type interfaceType, + Type implementationType, + Lifetime lifetime) => + builder.Register(implementationType, lifetime).As(interfaceType); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Lifetime lifetime) => + builder.Register(typeof(T), lifetime); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Lifetime lifetime) + where TImplement : TInterface => + builder.Register(lifetime).As(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Lifetime lifetime) + where TImplement : TInterface1, TInterface2 => + builder.Register(lifetime).As(typeof(TInterface1), typeof(TInterface2)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Lifetime lifetime) + where TImplement : TInterface1, TInterface2, TInterface3 + => builder.Register(lifetime).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder Register( + this IContainerBuilder builder, + Func implementationConfiguration, + Lifetime lifetime) + => builder.Register(new FuncRegistrationBuilder(container => implementationConfiguration(container), typeof(TInterface), lifetime)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterInstance( + this IContainerBuilder builder, + object instance, + Type implementationType) + => builder.Register(new InstanceRegistrationBuilder(instance)).As(implementationType); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterInstance( + this IContainerBuilder builder, + TInterface instance) + => builder.Register(new InstanceRegistrationBuilder(instance)).As(typeof(TInterface)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterInstance( + this IContainerBuilder builder, + TInterface1 instance) + => builder.RegisterInstance(instance).As(typeof(TInterface1), typeof(TInterface2)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterInstance( + this IContainerBuilder builder, + TInterface1 instance) + => builder.RegisterInstance(instance).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func factory) + => builder.RegisterInstance(factory); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + => builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func), lifetime)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + => builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func), lifetime)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + => builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func), lifetime)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + => builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func), lifetime)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RegistrationBuilder RegisterFactory( + this IContainerBuilder builder, + Func> factoryFactory, + Lifetime lifetime) + => builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func), lifetime)); + + public static void RegisterDisposeCallback(this IContainerBuilder builder, Action callback) + { + if (!builder.Exists(typeof(BuilderCallbackDisposable))) + { + builder.Register(Lifetime.Singleton); + } + builder.RegisterBuildCallback(container => + { + var disposable = container.Resolve(); + disposable.Disposing += callback; + }); + } + + [Obsolete("IObjectResolver is registered by default. This method does nothing.")] + public static void RegisterContainer(this IContainerBuilder builder) + { + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs.meta new file mode 100644 index 00000000..6c9fe34b --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/ContainerBuilderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76ede31a370f45ffa838fe2da30f58a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics.meta new file mode 100644 index 00000000..46abc8db --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f6de57dc253147c896d75dc9c2cfeab5 +timeCreated: 1632059553 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs new file mode 100644 index 00000000..89c8abc2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using VContainer.Internal; + +namespace VContainer.Diagnostics +{ + public sealed class DiagnosticsCollector + { + public string ScopeName { get; } + + readonly List diagnosticsInfos = new List(); + readonly ThreadLocal> resolveCallStack + = new ThreadLocal>(() => new Stack()); + + public DiagnosticsCollector(string scopeName) + { + ScopeName = scopeName; + } + + public IReadOnlyList GetDiagnosticsInfos() + { + return diagnosticsInfos; + } + + public void Clear() + { + lock (diagnosticsInfos) + { + diagnosticsInfos.Clear(); + } + } + + public void TraceRegister(RegisterInfo registerInfo) + { + lock (diagnosticsInfos) + { + diagnosticsInfos.Add(new DiagnosticsInfo(ScopeName, registerInfo)); + } + } + + public void TraceBuild(RegistrationBuilder registrationBuilder, Registration registration) + { + lock (diagnosticsInfos) + { + foreach (var x in diagnosticsInfos) + { + if (x.RegisterInfo.RegistrationBuilder == registrationBuilder) + { + x.ResolveInfo = new ResolveInfo(registration); + return; + } + } + } + } + + public object TraceResolve(Registration registration, Func resolving) + { + var current = DiagnositcsContext.FindByRegistration(registration); + var owner = resolveCallStack.Value.Count > 0 ? resolveCallStack.Value.Peek() : null; + + if (!(registration.Provider is CollectionInstanceProvider) && current != null && current != owner) + { + current.ResolveInfo.RefCount += 1; + current.ResolveInfo.MaxDepth = current.ResolveInfo.MaxDepth < 0 + ? resolveCallStack.Value.Count + : Math.Max(current.ResolveInfo.MaxDepth, resolveCallStack.Value.Count); + + owner?.Dependencies.Add(current); + + resolveCallStack.Value.Push(current); + var watch = Stopwatch.StartNew(); + var instance = resolving(registration); + watch.Stop(); + resolveCallStack.Value.Pop(); + + SetResolveTime(current, watch.ElapsedMilliseconds); + + if (!current.ResolveInfo.Instances.Contains(instance)) + { + current.ResolveInfo.Instances.Add(instance); + } + + return instance; + } + return resolving(registration); + } + + private static void SetResolveTime(DiagnosticsInfo current, long elapsedMilliseconds) + { + var resolves = current.ResolveInfo.RefCount; + var resolveTime = current.ResolveInfo.ResolveTime; + + switch (current.ResolveInfo.Registration.Lifetime) + { + case Lifetime.Transient: + resolveTime = (resolveTime * (resolves - 1) + elapsedMilliseconds) / resolves; + break; + case Lifetime.Scoped: + case Lifetime.Singleton: + if (elapsedMilliseconds > resolveTime) + resolveTime = elapsedMilliseconds; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + current.ResolveInfo.ResolveTime = resolveTime; + } + + public void NotifyContainerBuilt(IObjectResolver container) + => DiagnositcsContext.NotifyContainerBuilt(container); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs.meta new file mode 100644 index 00000000..8c5ceaf3 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsCollector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 689fb82515a24c2887f2693a3a11010b +timeCreated: 1632059581 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs new file mode 100644 index 00000000..30904f7d --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace VContainer.Diagnostics +{ + public static class DiagnositcsContext + { + static readonly Dictionary collectors + = new Dictionary(); + + public static event Action OnContainerBuilt; + + public static DiagnosticsCollector GetCollector(string name) + { + lock (collectors) + { + if (!collectors.TryGetValue(name, out var collector)) + { + collector = new DiagnosticsCollector(name); + collectors.Add(name, collector); + } + return collector; + } + } + + public static ILookup GetGroupedDiagnosticsInfos() + { + lock (collectors) + { + return collectors + .SelectMany(x => x.Value.GetDiagnosticsInfos()) + .Where(x => x.ResolveInfo.MaxDepth <= 1) + .ToLookup(x => x.ScopeName); + } + } + + public static IEnumerable GetDiagnosticsInfos() + { + lock (collectors) + { + return collectors.SelectMany(x => x.Value.GetDiagnosticsInfos()); + } + } + + public static void NotifyContainerBuilt(IObjectResolver container) + { + OnContainerBuilt?.Invoke(container); + } + + internal static DiagnosticsInfo FindByRegistration(Registration registration) + { + return GetDiagnosticsInfos().FirstOrDefault(x => x.ResolveInfo.Registration == registration); + } + + public static void RemoveCollector(string name) + { + lock (collectors) + { + collectors.Remove(name); + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs.meta new file mode 100644 index 00000000..3dc17fa4 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsContext.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a87e62523cc246f2917f3ba78f7c6e99 +timeCreated: 1633441412 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs new file mode 100644 index 00000000..b56168a2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace VContainer.Diagnostics +{ + public sealed class DiagnosticsInfo + { + public string ScopeName { get; } + public RegisterInfo RegisterInfo { get; } + public ResolveInfo ResolveInfo { get; set; } + public List Dependencies { get; } = new List(); + + public DiagnosticsInfo(string scopeName, RegisterInfo registerInfo) + { + ScopeName = scopeName; + RegisterInfo = registerInfo; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs.meta new file mode 100644 index 00000000..52f3b4d0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/DiagnosticsInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: af9e78dd14ec4031998bbf9c34cc93ae +timeCreated: 1633441316 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs new file mode 100644 index 00000000..d171b571 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs @@ -0,0 +1,119 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Security; +using System.Threading; + +namespace VContainer.Diagnostics +{ + public sealed class RegisterInfo + { + static bool displayFileNames = true; + static int idSeed; + + public int Id { get; } + public RegistrationBuilder RegistrationBuilder { get; } + public StackTrace StackTrace { get; } + + StackFrame headLineStackFrame; + + internal string formattedStackTrace = default; // cache field for internal use(Unity Editor, etc...) + + public RegisterInfo(RegistrationBuilder registrationBuilder) + { + Id = Interlocked.Increment(ref idSeed); + RegistrationBuilder = registrationBuilder; + StackTrace = new StackTrace(true); + headLineStackFrame = GetHeadlineFrame(StackTrace); + } + + public string GetFilename() + { + if (headLineStackFrame != null && displayFileNames && headLineStackFrame.GetILOffset() != -1) + { + try + { + return headLineStackFrame.GetFileName(); + } + catch (NotSupportedException) + { + displayFileNames = false; + } + catch (SecurityException) + { + displayFileNames = false; + } + } + return null; + } + + public int GetFileLineNumber() + { + if (headLineStackFrame != null && displayFileNames && headLineStackFrame.GetILOffset() != -1) + { + try + { + return headLineStackFrame.GetFileLineNumber(); + } + catch (NotSupportedException) + { + displayFileNames = false; + } + catch (SecurityException) + { + displayFileNames = false; + } + } + return -1; + } + + public string GetScriptAssetPath() + { + var filename = GetFilename(); + if (filename == null) + return ""; + var prefixIndex = filename.LastIndexOf("Assets/"); + return prefixIndex > 0 ? filename.Substring(prefixIndex) : ""; + } + + public string GetHeadline() + { + if (headLineStackFrame == null) + return ""; + + var method = headLineStackFrame.GetMethod(); + var filename = GetFilename(); + if (filename != null) + { + var lineNumber = GetFileLineNumber(); + return $"{method.DeclaringType?.FullName}.{method.Name} (at {Path.GetFileName(filename)}:{lineNumber})"; + } + + var ilOffset = headLineStackFrame.GetILOffset(); + if (ilOffset != -1) + { + return $"{method.DeclaringType?.FullName}.{method.Name}(offset: {ilOffset})"; + } + return $"{method.DeclaringType?.FullName}.{method.Name}"; + } + + StackFrame GetHeadlineFrame(StackTrace stackTrace) + { + for (var i = 0; i < stackTrace.FrameCount; i++) + { + var sf = stackTrace.GetFrame(i); + if (sf == null) continue; + + var m = sf.GetMethod(); + if (m == null) continue; + + if (m.DeclaringType == null) continue; + if (m.DeclaringType.Namespace == null || !m.DeclaringType.Namespace.StartsWith("VContainer")) + { + return sf; + } + } + return stackTrace.FrameCount > 0 ? stackTrace.GetFrame(0) : null; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs.meta new file mode 100644 index 00000000..52661450 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/RegisterInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4359f1f8bb3b4540835998a60eb93049 +timeCreated: 1633272541 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs new file mode 100644 index 00000000..2047b76a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace VContainer.Diagnostics +{ + public sealed class ResolveInfo + { + public Registration Registration { get; } + public List Instances { get; } = new List(); + public int MaxDepth { get; set; } = -1; + public int RefCount { get; set; } + public long ResolveTime { get; set; } + + public ResolveInfo(Registration registration) + { + Registration = registration; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs.meta new file mode 100644 index 00000000..c24d3197 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Diagnostics/ResolveInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3f53a6c226cf4a04ae556c00dc477f4b +timeCreated: 1633102576 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs b/Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs new file mode 100644 index 00000000..337dd2a8 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs @@ -0,0 +1,10 @@ +using System; + +namespace VContainer +{ + public interface IInjectParameter + { + bool Match(Type parameterType, string parameterName); + object GetValue(IObjectResolver resolver); + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs.meta new file mode 100644 index 00000000..a546c882 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IInjectParameter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 890f74c4a6fb42db8340eb8f1a505330 +timeCreated: 1595654174 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs b/Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs new file mode 100644 index 00000000..edf6ed6c --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace VContainer +{ + public interface IInjector + { + void Inject(object instance, IObjectResolver resolver, IReadOnlyList parameters); + object CreateInstance(IObjectResolver resolver, IReadOnlyList parameters); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs.meta new file mode 100644 index 00000000..da197de2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IInjector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ddd3eeb838dd449228703d913f0e9108 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs new file mode 100644 index 00000000..9b6bea38 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs @@ -0,0 +1,7 @@ +namespace VContainer +{ + public interface IInstanceProvider + { + object SpawnInstance(IObjectResolver resolver); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs.meta new file mode 100644 index 00000000..1cd8e6e4 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0474f1261ed04e1e91e41b96a52297e9 +timeCreated: 1637894248 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs b/Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs new file mode 100644 index 00000000..562ce8a2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer +{ + public static class IObjectResolverExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Resolve(this IObjectResolver resolver, object key = null) => (T)resolver.Resolve(typeof(T), key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryResolve(this IObjectResolver resolver, out T resolved, object key = null) + { + if (resolver.TryResolve(typeof(T), out var r, key)) + { + resolved = (T)r; + return true; + } + + resolved = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ResolveOrDefault(this IObjectResolver resolver, T defaultValue = default, object key = null) + { + if (resolver.TryResolve(typeof(T), out var value, key)) + { + return (T)value; + } + + return defaultValue; + } + + // Using from CodeGen + [Preserve] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object ResolveNonGeneric(this IObjectResolver resolve, Type type, object key = null) => resolve.Resolve(type, key); + + public static object ResolveOrParameter( + this IObjectResolver resolver, + Type parameterType, + string parameterName, + IReadOnlyList parameters, + object key = null) + { + if (parameters == null) + { + return resolver.Resolve(parameterType, key); + } + + // ReSharper disable once ForCanBeConvertedToForeach + for (var i = 0; i < parameters.Count; i++) + { + var parameter = parameters[i]; + if (parameter.Match(parameterType, parameterName)) + { + return parameter.GetValue(resolver); + } + } + + return key != null ? + resolver.Resolve(parameterType, key) : + resolver.Resolve(parameterType); + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs.meta new file mode 100644 index 00000000..c23efb7e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/IObjectResolverExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4b7dc27e3935451ba5ca0f67efba7e6f +timeCreated: 1595619880 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal.meta new file mode 100644 index 00000000..0c298794 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 853495921eac94a55bba66a67ce6feb7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs new file mode 100644 index 00000000..7cf49079 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs @@ -0,0 +1,20 @@ +using System; + +namespace VContainer.Internal +{ + class BuilderCallbackDisposable : IDisposable + { + public event Action Disposing; + readonly IObjectResolver container; + + public BuilderCallbackDisposable(IObjectResolver container) + { + this.container = container; + } + + public void Dispose() + { + if (Disposing != null) Disposing.Invoke(container); + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs.meta new file mode 100644 index 00000000..a4d524f3 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/BuilderCallbackDisposable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 04c6203c157b4898b815f392b47d02ba +timeCreated: 1703318719 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs new file mode 100644 index 00000000..dbdb4b14 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs @@ -0,0 +1,76 @@ +using System; + +namespace VContainer.Internal +{ + sealed class CappedArrayPool + { + internal const int InitialBucketSize = 4; + + public static readonly CappedArrayPool Shared8Limit = new CappedArrayPool(8); + + readonly T[][][] buckets; + readonly object syncRoot = new object(); + readonly int[] tails; + + internal CappedArrayPool(int maxLength) + { + buckets = new T[maxLength][][]; + tails = new int[maxLength]; + for (var i = 0; i < maxLength; i++) + { + var arrayLength = i + 1; + buckets[i] = new T[InitialBucketSize][]; + for (var j = 0; j < InitialBucketSize; j++) + { + buckets[i][j] = new T[arrayLength]; + } + tails[i] = 0; + } + } + + public T[] Rent(int length) + { + if (length <= 0) + return Array.Empty(); + + if (length > buckets.Length) + return new T[length]; // Not supported + + var i = length - 1; + + lock (syncRoot) + { + var bucket = buckets[i]; + var tail = tails[i]; + if (tail >= bucket.Length) + { + Array.Resize(ref bucket, bucket.Length * 2); + buckets[i] = bucket; + } + + if (bucket[tail] == null) + { + bucket[tail] = new T[length]; + } + + var result = bucket[tail]; + tails[i] += 1; + return result; + } + } + + public void Return(T[] array) + { + if (array.Length <= 0 || array.Length > buckets.Length) + return; + + var i = array.Length - 1; + lock (syncRoot) + { + Array.Clear(array, 0, array.Length); + if (tails[i] > 0) + tails[i] -= 1; + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs.meta new file mode 100644 index 00000000..cf9b7204 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CappedArrayPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 798af9f34fc7941f6a89a12f634db404 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs new file mode 100644 index 00000000..024d7cd7 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class CompositeDisposable : IDisposable + { + readonly Stack disposables = new Stack(); + + public void Dispose() + { + IDisposable disposable; + do + { + lock (disposables) + { + disposable = disposables.Count > 0 + ? disposables.Pop() + : null; + } + disposable?.Dispose(); + } while (disposable != null); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(IDisposable disposable) + { + lock (disposables) + { + disposables.Push(disposable); + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs.meta new file mode 100644 index 00000000..c2937580 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/CompositeDisposable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4308506e1771d4ec69df54d5a890d67b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs new file mode 100644 index 00000000..113bb1e9 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs @@ -0,0 +1,13 @@ +namespace VContainer.Internal +{ + public sealed class ContainerLocal + { + public readonly T Value; + + [Inject] + public ContainerLocal(T value) + { + Value = value; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs.meta new file mode 100644 index 00000000..532e5e8c --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ContainerLocal.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7868bf6fa13e45b39973d776a3fad130 +timeCreated: 1637458343 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs new file mode 100644 index 00000000..688a9d51 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class FixedTypeObjectKeyHashtable + { + readonly struct HashEntry + { + public readonly Type Type; + public readonly object Key; + public readonly TValue Value; + + public HashEntry(Type type, object key, TValue value) + { + Type = type; + Key = key; + Value = value; + } + } + + readonly HashEntry[][] table; + readonly int indexFor; + + public FixedTypeObjectKeyHashtable(KeyValuePair<(Type, object), TValue>[] values, float loadFactor = 0.75f) + { + var initialCapacity = (int)(values.Length / loadFactor); + + // make power of 2 + var capacity = 1; + while (capacity < initialCapacity) + { + capacity <<= 1; + } + + table = new HashEntry[capacity][]; + indexFor = table.Length - 1; + + foreach (var item in values) + { + var hash = GetHashCode(item.Key.Item1, item.Key.Item2); + var array = table[hash & indexFor]; + if (array == null) + { + array = new HashEntry[1]; + array[0] = new HashEntry(item.Key.Item1, item.Key.Item2, item.Value); + } + else + { + var newArray = new HashEntry[array.Length + 1]; + Array.Copy(array, newArray, array.Length); + array = newArray; + array[array.Length - 1] = new HashEntry(item.Key.Item1, item.Key.Item2, item.Value); + } + + table[hash & indexFor] = array; + } + } + + private int GetHashCode(Type type, object key = null) + { + var typeHash = RuntimeHelpers.GetHashCode(type); + + if(key == null) + return typeHash; + + // Combine the hash codes of Type and Key + var keyHash = key.GetHashCode(); + return (typeHash * 397) ^ keyHash; // FNV-style combination + } + + public bool TryGet(Type type, object key, out TValue value) + { + var hashCode = GetHashCode(type, key); + var buckets = table[hashCode & indexFor]; + + if (buckets == null) goto END; + + if (buckets[0].Type == type) + { + if (key == null || Equals(buckets[0].Key, key)) + { + value = buckets[0].Value; + return true; + } + } + + for (var i = 1; i < buckets.Length; i++) + { + if (buckets[i].Type != type) + { + continue; + } + + if (key == null || Equals(buckets[i].Key, key)) + { + value = buckets[i].Value; + return true; + } + } + + END: + value = default; + return false; + } + } +} + diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs.meta new file mode 100644 index 00000000..c809e4c8 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FixedTypeObjectKeyHashtable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04f08cefc67d04906946d27eef22afb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs new file mode 100644 index 00000000..bbbc67d8 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +#if UNITY_2021_3_OR_NEWER +using System.Runtime.InteropServices; +using Unity.Collections.LowLevel.Unsafe; +#endif + +namespace VContainer.Internal +{ + class FreeList where T : class + { + public bool IsDisposed => lastIndex == -2; + public int Length => lastIndex + 1; + + readonly object gate = new object(); + T[] values; + int lastIndex = -1; + + public FreeList(int initialCapacity) + { + values = new T[initialCapacity]; + } + +#if NETSTANDARD2_1 + public ReadOnlySpan AsSpan() + { + if (lastIndex < 0) + { + return ReadOnlySpan.Empty; + } + return values.AsSpan(0, lastIndex + 1); + } +#endif + + public T this[int index] => values[index]; + + public void Add(T item) + { + lock (gate) + { + CheckDispose(); + + // try find blank + var index = FindNullIndex(values); + if (index == -1) + { + // full, 1, 4, 6,...resize(x1.5) + var len = values.Length; + var newValues = new T[len + len / 2]; + Array.Copy(values, newValues, len); + values = newValues; + index = len; + } + + values[index] = item; + if (lastIndex < index) + { + lastIndex = index; + } + } + } + + public void RemoveAt(int index) + { + lock (gate) + { + if (index < values.Length) + { + ref var v = ref values[index]; + if (v == null) throw new KeyNotFoundException($"key index {index} is not found."); + + v = null; + if (index == lastIndex) + { + lastIndex = FindLastNonNullIndex(values, index); + } + } + } + } + + public bool Remove(T value) + { + lock (gate) + { + if (lastIndex < 0) return false; + + var index = -1; + for (var i = 0; i < values.Length; i++) + { + if (values[i] == value) + { + index = i; + break; + } + } + + if (index != -1) + { + RemoveAt(index); + return true; + } + } + + return false; + } + + public void Clear() + { + lock (gate) + { + if (lastIndex > 0) + { + Array.Clear(values, 0, lastIndex + 1); + lastIndex = -1; + } + } + } + + public void Dispose() + { + lock (gate) + { + lastIndex = -2; // -2 is disposed. + } + } + + void CheckDispose() + { + if (IsDisposed) + { + throw new ObjectDisposedException(GetType().FullName); + } + } + +#if UNITY_2021_3_OR_NEWER + static unsafe int FindNullIndex(T[] target) + { + ref var head = ref UnsafeUtility.As(ref MemoryMarshal.GetReference(target.AsSpan())); + fixed (void* p = &head) + { + var span = new ReadOnlySpan(p, target.Length); + +#if NETSTANDARD2_1 + return span.IndexOf(IntPtr.Zero); +#else + for (int i = 0; i < span.Length; i++) + { + if (span[i] == IntPtr.Zero) return i; + } + return -1; +#endif + } + } + + static unsafe int FindLastNonNullIndex(T[] target, int lastIndex) + { + ref var head = ref UnsafeUtility.As(ref MemoryMarshal.GetReference(target.AsSpan())); + fixed (void* p = &head) + { + var span = new ReadOnlySpan(p, lastIndex); // without lastIndexed value. + + for (var i = span.Length - 1; i >= 0; i--) + { + if (span[i] != IntPtr.Zero) return i; + } + + return -1; + } + } +#else + static int FindNullIndex(T[] target) + { + for (var i = 0; i < target.Length; i++) + { + if (target[i] == null) return i; + } + return -1; + } + + static int FindLastNonNullIndex(T[] target, int lastIndex) + { + for (var i = lastIndex; i >= 0; i--) + { + if (target[i] != null) return i; + } + return -1; + } +#endif + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs.meta new file mode 100644 index 00000000..ea16e759 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FreeList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab65b7c977d342df8c7b3d914e896b30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs new file mode 100644 index 00000000..de596a31 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs @@ -0,0 +1,23 @@ +using System; + +namespace VContainer.Internal +{ + sealed class FuncRegistrationBuilder : RegistrationBuilder + { + readonly Func implementationProvider; + + public FuncRegistrationBuilder( + Func implementationProvider, + Type implementationType, + Lifetime lifetime) : base(implementationType, lifetime) + { + this.implementationProvider = implementationProvider; + } + + public override Registration Build() + { + var spawner = new FuncInstanceProvider(implementationProvider); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, spawner, Key); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs.meta new file mode 100644 index 00000000..4267c4ee --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/FuncRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 959eabb8a5cb4f198c7e96512ea177b8 +timeCreated: 1619866780 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs new file mode 100644 index 00000000..07a0cfcf --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs @@ -0,0 +1,80 @@ +using System; + +namespace VContainer.Internal +{ + sealed class TypedParameter : IInjectParameter + { + public readonly Type Type; + public readonly object Value; + + public TypedParameter(Type type, object value) + { + Type = type; + Value = value; + } + + public bool Match(Type parameterType, string _) => parameterType == Type; + + public object GetValue(IObjectResolver _) + { + return Value; + } + } + + sealed class FuncTypedParameter : IInjectParameter + { + public readonly Type Type; + public readonly Func Func; + + public FuncTypedParameter(Type type, Func func) + { + Type = type; + Func = func; + } + + public bool Match(Type parameterType, string _) => parameterType == Type; + + public object GetValue(IObjectResolver resolver) + { + return Func(resolver); + } + } + + sealed class NamedParameter : IInjectParameter + { + public readonly string Name; + public readonly object Value; + + public NamedParameter(string name, object value) + { + Name = name; + Value = value; + } + + public bool Match(Type _, string parameterName) => parameterName == Name; + + public object GetValue(IObjectResolver _) + { + return Value; + } + } + + sealed class FuncNamedParameter : IInjectParameter + { + public readonly string Name; + public readonly Func Func; + + public FuncNamedParameter(string name, Func func) + { + Name = name; + Func = func; + } + + public bool Match(Type _, string parameterName) => parameterName == Name; + + public object GetValue(IObjectResolver resolver) + { + return Func(resolver); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs.meta new file mode 100644 index 00000000..516e2820 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectParameter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dbed645399934dceb7271819378c67fe +timeCreated: 1593003624 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs new file mode 100644 index 00000000..dfb57c4e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace VContainer.Internal +{ + public static class InjectorCache + { + static readonly ConcurrentDictionary Injectors = new ConcurrentDictionary(); + + public static IInjector GetOrBuild(Type type) + { + return Injectors.GetOrAdd(type, key => + { + // SourceGenerator + var generatedType = key.Assembly.GetType($"{key.FullName}GeneratedInjector", false); + if (generatedType != null) + { + return (IInjector)Activator.CreateInstance(generatedType); + } + + // IL weaving (Deprecated) + var getter = key.GetMethod("__GetGeneratedInjector", BindingFlags.Static | BindingFlags.Public); + if (getter != null) + { + return (IInjector)getter.Invoke(null, null); + } + return ReflectionInjector.Build(key); + }); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs.meta new file mode 100644 index 00000000..90c1b8fb --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InjectorCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9868d07fc52841ac9aaef692536402e6 +timeCreated: 1595675733 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders.meta new file mode 100644 index 00000000..b8f56ab6 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 975a095af910460bb016e0cc5cf9eb07 +timeCreated: 1637894696 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs new file mode 100644 index 00000000..fa7c26bc --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + struct RegistrationElement + { + public Registration Registration; + public IObjectResolver RegisteredContainer; + + public RegistrationElement(Registration registration, IObjectResolver registeredContainer) + { + Registration = registration; + RegisteredContainer = registeredContainer; + } + } + + sealed class CollectionInstanceProvider : IInstanceProvider, IEnumerable + { + public static bool Match(Type openGenericType) => openGenericType == typeof(IEnumerable<>) || + openGenericType == typeof(IReadOnlyList<>); + + public List.Enumerator GetEnumerator() => registrations.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public Type ImplementationType { get; } + public IReadOnlyList InterfaceTypes => interfaceTypes; + public Lifetime Lifetime => Lifetime.Transient; // Collection reference is transient. So its members can have each lifetimes. + + public Type ElementType { get; } + + readonly List interfaceTypes; + readonly List registrations = new List(); + + public CollectionInstanceProvider(Type elementType) + { + ElementType = elementType; + ImplementationType = elementType.MakeArrayType(); + interfaceTypes = new List + { + RuntimeTypeCache.EnumerableTypeOf(elementType), + RuntimeTypeCache.ReadOnlyListTypeOf(elementType), + }; + } + + public override string ToString() + { + var contractTypes = InterfaceTypes != null ? string.Join(", ", InterfaceTypes) : ""; + return $"CollectionRegistration {ImplementationType} ContractTypes=[{contractTypes}] {Lifetime}"; + } + + public void Add(Registration registration) + { + foreach (var x in registrations) + { + if (x.Lifetime == Lifetime.Singleton && x.ImplementationType == registration.ImplementationType && x.Key == registration.Key) + { + throw new VContainerException(registration.ImplementationType, $"Conflict implementation type : {registration}"); + } + } + registrations.Add(registration); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) + { + if (resolver is IScopedObjectResolver scope) + { + using (ListPool.Get(out var entirelyRegistrations)) + { + CollectFromParentScopes(scope, entirelyRegistrations); + return SpawnInstance(resolver, entirelyRegistrations); + } + } + + var array = Array.CreateInstance(ElementType, registrations.Count); + for (var i = 0; i < registrations.Count; i++) + { + array.SetValue(resolver.Resolve(registrations[i]), i); + } + return array; + } + + internal object SpawnInstance(IObjectResolver currentScope, IReadOnlyList entirelyRegistrations) + { + var array = Array.CreateInstance(ElementType, entirelyRegistrations.Count); + for (var i = 0; i < entirelyRegistrations.Count; i++) + { + var x = entirelyRegistrations[i]; + var resolver = x.Registration.Lifetime == Lifetime.Singleton + ? x.RegisteredContainer + : currentScope; + array.SetValue(resolver.Resolve(x.Registration), i); + } + return array; + } + + internal void CollectFromParentScopes( + IScopedObjectResolver scope, + List registrationsBuffer, + bool localScopeOnly = false) + { + foreach (var registration in registrations) + { + registrationsBuffer.Add(new RegistrationElement(registration, scope)); + } + + var finderType = InterfaceTypes[0]; + scope = scope.Parent; + + while (scope != null) + { + if (scope.TryGetRegistration(finderType, out var registration) && + registration.Provider is CollectionInstanceProvider parentCollection) + { + foreach (var x in parentCollection.registrations) + { + if (!localScopeOnly || x.Lifetime != Lifetime.Singleton) + { + registrationsBuffer.Add(new RegistrationElement(x, scope)); + } + } + } + scope = scope.Parent; + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs.meta new file mode 100644 index 00000000..e0f2fa12 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/CollectionInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 24bcedcfe70849f4b609a3c695bee2da +timeCreated: 1637460174 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs new file mode 100644 index 00000000..c94e2ec0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class ContainerInstanceProvider : IInstanceProvider + { + public static readonly ContainerInstanceProvider Default = new ContainerInstanceProvider(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) => resolver; + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs.meta new file mode 100644 index 00000000..b158637d --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e5fee000f6274ce19a7ee65eb42604e7 +timeCreated: 1637894705 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs new file mode 100644 index 00000000..33ce5204 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs @@ -0,0 +1,45 @@ +using System; + +namespace VContainer.Internal +{ + sealed class ContainerLocalInstanceProvider : IInstanceProvider + { + readonly Type wrappedType; + readonly Registration valueRegistration; + + public ContainerLocalInstanceProvider(Type wrappedType, Registration valueRegistration) + { + this.wrappedType = wrappedType; + this.valueRegistration = valueRegistration; + } + + public object SpawnInstance(IObjectResolver resolver) + { + object value; + + if (resolver is ScopedContainer scope && + valueRegistration.Provider is CollectionInstanceProvider collectionProvider) + { + using (ListPool.Get(out var entirelyRegistrations)) + { + collectionProvider.CollectFromParentScopes(scope, entirelyRegistrations, localScopeOnly: true); + value = collectionProvider.SpawnInstance(scope, entirelyRegistrations); + } + } + else + { + value = resolver.Resolve(valueRegistration); + } + var parameterValues = CappedArrayPool.Shared8Limit.Rent(1); + try + { + parameterValues[0] = value; + return Activator.CreateInstance(wrappedType, parameterValues); + } + finally + { + CappedArrayPool.Shared8Limit.Return(parameterValues); + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs.meta new file mode 100644 index 00000000..96991444 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ContainerLocalInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 67ec193044734d97aa59126a9cb83398 +timeCreated: 1637461669 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs new file mode 100644 index 00000000..fa4878ba --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs @@ -0,0 +1,17 @@ +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class ExistingInstanceProvider : IInstanceProvider + { + readonly object implementationInstance; + + public ExistingInstanceProvider(object implementationInstance) + { + this.implementationInstance = implementationInstance; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) => implementationInstance; + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs.meta new file mode 100644 index 00000000..2b2de677 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/ExistingInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4fcef74c11fc4018bbc88bc395ef8657 +timeCreated: 1619863673 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs new file mode 100644 index 00000000..a56a8a91 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class FuncInstanceProvider : IInstanceProvider + { + readonly Func implementationProvider; + + public FuncInstanceProvider(Func implementationProvider) + { + this.implementationProvider = implementationProvider; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) => implementationProvider(resolver); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs.meta new file mode 100644 index 00000000..fbc5870f --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/FuncInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a4fb0cd9294e424bb69b28711f95ccc0 +timeCreated: 1619867067 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs new file mode 100644 index 00000000..e6a291d1 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class InstanceProvider : IInstanceProvider + { + readonly IInjector injector; + readonly IReadOnlyList customParameters; + + public InstanceProvider( + IInjector injector, + IReadOnlyList customParameters = null) + { + this.injector = injector; + this.customParameters = customParameters; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) + => injector.CreateInstance(resolver, customParameters); + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs.meta new file mode 100644 index 00000000..a3117a17 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/InstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d7f72f5485924ec19d7761e215e23a16 +timeCreated: 1637903141 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs new file mode 100644 index 00000000..f2d7f7db --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace VContainer.Internal +{ + public class OpenGenericInstanceProvider : IInstanceProvider + { + class TypeParametersKey + { + public readonly Type[] TypeParameters; + public readonly object Key; + + public TypeParametersKey(Type[] typeParameters, object key) + { + TypeParameters = typeParameters; + Key = key; + } + + public override bool Equals(object obj) + { + if (obj is TypeParametersKey other) + { + if (Key != other.Key) + return false; + + if (TypeParameters.Length != other.TypeParameters.Length) + return false; + + for (var i = 0; i < TypeParameters.Length; i++) + { + if (TypeParameters[i] != other.TypeParameters[i]) + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() + { + var hash = 5381; + foreach (var typeParameter in TypeParameters) + { + hash = ((hash << 5) + hash) ^ typeParameter.GetHashCode(); + } + hash = ((hash << 5) + hash) ^ (Key?.GetHashCode() ?? 0); + return hash; + } + } + + readonly Lifetime lifetime; + readonly Type implementationType; + readonly IReadOnlyList customParameters; + + readonly ConcurrentDictionary constructedRegistrations = new ConcurrentDictionary(); + readonly Func createRegistrationFunc; + + public OpenGenericInstanceProvider(Type implementationType, Lifetime lifetime, List injectParameters) + { + this.implementationType = implementationType; + this.lifetime = lifetime; + customParameters = injectParameters; + createRegistrationFunc = CreateRegistration; + } + + public Registration GetClosedRegistration(Type closedInterfaceType, Type[] typeParameters, object key = null) + { + var typeParametersKey = new TypeParametersKey(typeParameters, key); + return constructedRegistrations.GetOrAdd(typeParametersKey, createRegistrationFunc); + } + + Registration CreateRegistration(TypeParametersKey key) + { + var newType = implementationType.MakeGenericType(key.TypeParameters); + var injector = InjectorCache.GetOrBuild(newType); + var spawner = new InstanceProvider(injector, customParameters); + return new Registration(newType, lifetime, new List(1) { newType }, spawner, key.Key); + } + + public object SpawnInstance(IObjectResolver resolver) + { + throw new InvalidOperationException(); + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs.meta new file mode 100644 index 00000000..104d0219 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceProviders/OpenGenericInstanceProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 927f2cb4e816642cba170d66ffc1fcb9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs new file mode 100644 index 00000000..9b95fb2e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class InstanceRegistrationBuilder : RegistrationBuilder + { + readonly object implementationInstance; + + public InstanceRegistrationBuilder(object implementationInstance) + : base(implementationInstance.GetType(), Lifetime.Singleton) + { + this.implementationInstance = implementationInstance; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override Registration Build() + { + var spawner = new ExistingInstanceProvider(implementationInstance); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, spawner, Key); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs.meta new file mode 100644 index 00000000..72123ca3 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/InstanceRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 097b02c25ccf47df97386afb6a4f0c21 +timeCreated: 1619863604 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs new file mode 100644 index 00000000..344a9ec0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; + +namespace VContainer.Internal +{ + internal static class ListPool + { + const int DefaultCapacity = 32; + + private static readonly Stack> _pool = new Stack>(4); + + /// + /// BufferScope supports releasing a buffer with using clause. + /// + internal readonly struct BufferScope : IDisposable + { + private readonly List _buffer; + + public BufferScope(List buffer) + { + _buffer = buffer; + } + + public void Dispose() + { + Release(_buffer); + } + } + + /// + /// Get a buffer from the pool. + /// + /// + internal static List Get() + { + lock (_pool) + { + if (_pool.Count == 0) + { + return new List(DefaultCapacity); + } + + return _pool.Pop(); + } + } + + /// + /// Get a buffer from the pool. Returning a disposable struct to support recycling via using clause. + /// + /// + /// + internal static BufferScope Get(out List buffer) + { + buffer = Get(); + return new BufferScope(buffer); + } + + /// + /// Declare a buffer won't be used anymore and put it back to the pool. + /// + /// + internal static void Release(List buffer) + { + buffer.Clear(); + lock (_pool) + { + _pool.Push(buffer); + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs.meta new file mode 100644 index 00000000..160c6dea --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ListPool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: da1a74df3eab442e81879a0b2e6fac0e +timeCreated: 1606668004 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs new file mode 100644 index 00000000..2ce2b847 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +namespace VContainer.Internal +{ + public class OpenGenericRegistrationBuilder : RegistrationBuilder + { + public OpenGenericRegistrationBuilder(Type implementationType, Lifetime lifetime) + : base(implementationType, lifetime) + { + if (!implementationType.IsGenericType || implementationType.IsConstructedGenericType) + throw new VContainerException(implementationType, "Type is not open generic type."); + } + + public override Registration Build() + { + var provider = new OpenGenericInstanceProvider(ImplementationType, Lifetime, Parameters); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider, Key); + } + + public override RegistrationBuilder AsImplementedInterfaces() + { + InterfaceTypes = InterfaceTypes ?? new List(); + foreach (var i in ImplementationType.GetInterfaces()) + { + if (!i.IsGenericType) + continue; + + InterfaceTypes.Add(i.GetGenericTypeDefinition()); + } + return this; + } + + protected override void AddInterfaceType(Type interfaceType) + { + if (interfaceType.IsConstructedGenericType) + throw new VContainerException(interfaceType, "Type is not open generic type."); + + foreach (var i in ImplementationType.GetInterfaces()) + { + if (!i.IsGenericType || i.GetGenericTypeDefinition() != interfaceType) + continue; + + if (InterfaceTypes is null) + { + InterfaceTypes = new List(); + } + + if (!InterfaceTypes.Contains(interfaceType)) + InterfaceTypes.Add(interfaceType); + + return; + } + + base.AddInterfaceType(interfaceType); + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs.meta new file mode 100644 index 00000000..254f4caa --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/OpenGenericRegistrationBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41ece9cea6ed8424db8850c90169cb5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs new file mode 100644 index 00000000..62f5db26 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection; + +namespace VContainer.Internal +{ + sealed class ReflectionInjector : IInjector + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReflectionInjector Build(Type type) + { + var injectTypeInfo = TypeAnalyzer.AnalyzeWithCache(type); + return new ReflectionInjector(injectTypeInfo); + } + + readonly InjectTypeInfo injectTypeInfo; + + ReflectionInjector(InjectTypeInfo injectTypeInfo) + { + this.injectTypeInfo = injectTypeInfo; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inject(object instance, IObjectResolver resolver, IReadOnlyList parameters) + { + InjectFields(instance, resolver, parameters); + InjectProperties(instance, resolver, parameters); + InjectMethods(instance, resolver, parameters); + } + + public object CreateInstance(IObjectResolver resolver, IReadOnlyList parameters) + { + var parameterInfos = injectTypeInfo.InjectConstructor.ParameterInfos; + var parameterKeys = injectTypeInfo.InjectConstructor.ParameterKeys; + var parameterValues = CappedArrayPool.Shared8Limit.Rent(parameterInfos.Length); + try + { + for (var i = 0; i < parameterInfos.Length; i++) + { + var parameterInfo = parameterInfos[i]; + var key = parameterKeys[i]; + + parameterValues[i] = resolver.ResolveOrParameter( + parameterInfo.ParameterType, + parameterInfo.Name, + parameters, + key); + } + var instance = injectTypeInfo.InjectConstructor.ConstructorInfo.Invoke(parameterValues); + Inject(instance, resolver, parameters); + return instance; + } + catch (VContainerException ex) + { + throw new VContainerException(ex.InvalidType, $"Failed to resolve {injectTypeInfo.Type} : {ex.Message}"); + } + finally + { + CappedArrayPool.Shared8Limit.Return(parameterValues); + } + } + + void InjectFields(object obj, IObjectResolver resolver, IReadOnlyList parameters) + { + if (injectTypeInfo.InjectFields == null) + return; + + foreach (var x in injectTypeInfo.InjectFields) + { + var fieldValue = resolver.ResolveOrParameter(x.FieldType, x.Name, parameters, x.Key); + x.SetValue(obj, fieldValue); + } + } + + void InjectProperties(object obj, IObjectResolver resolver, IReadOnlyList parameters) + { + if (injectTypeInfo.InjectProperties == null) + return; + + foreach (var x in injectTypeInfo.InjectProperties) + { + var propValue = resolver.ResolveOrParameter(x.PropertyType, x.Name, parameters, x.Key); + x.SetValue(obj, propValue); + } + } + + void InjectMethods(object obj, IObjectResolver resolver, IReadOnlyList parameters) + { + if (injectTypeInfo.InjectMethods == null) + return; + + foreach (var method in injectTypeInfo.InjectMethods) + { + var parameterInfos = method.ParameterInfos; + var parameterKeys = method.ParameterKeys; + var parameterValues = CappedArrayPool.Shared8Limit.Rent(parameterInfos.Length); + try + { + for (var i = 0; i < parameterInfos.Length; i++) + { + var parameterInfo = parameterInfos[i]; + var key = parameterKeys[i]; + + parameterValues[i] = resolver.ResolveOrParameter( + parameterInfo.ParameterType, + parameterInfo.Name, + parameters, + key); + } + method.MethodInfo.Invoke(obj, parameterValues); + } + catch (VContainerException ex) + { + throw new VContainerException(ex.InvalidType, $"Failed to resolve {injectTypeInfo.Type} : {ex.Message}"); + } + finally + { + CappedArrayPool.Shared8Limit.Return(parameterValues); + } + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs.meta new file mode 100644 index 00000000..edb06b32 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/ReflectionInjector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49d591b36bd7b4319a86f326ba19fed0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs new file mode 100644 index 00000000..26107b35 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + static class RuntimeTypeCache + { + static readonly ConcurrentDictionary OpenGenericTypes = new ConcurrentDictionary(); + static readonly ConcurrentDictionary GenericTypeParameters = new ConcurrentDictionary(); + static readonly ConcurrentDictionary ArrayTypes = new ConcurrentDictionary(); + static readonly ConcurrentDictionary EnumerableTypes = new ConcurrentDictionary(); + static readonly ConcurrentDictionary ReadOnlyListTypes = new ConcurrentDictionary(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type OpenGenericTypeOf(Type closedGenericType) + => OpenGenericTypes.GetOrAdd(closedGenericType, key => key.GetGenericTypeDefinition()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type[] GenericTypeParametersOf(Type closedGenericType) + => GenericTypeParameters.GetOrAdd(closedGenericType, key => key.GetGenericArguments()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type ArrayTypeOf(Type elementType) + => ArrayTypes.GetOrAdd(elementType, key => key.MakeArrayType()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type EnumerableTypeOf(Type elementType) + => EnumerableTypes.GetOrAdd(elementType, key => typeof(IEnumerable<>).MakeGenericType(key)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type ReadOnlyListTypeOf(Type elementType) + => ReadOnlyListTypes.GetOrAdd(elementType, key => typeof(IReadOnlyList<>).MakeGenericType(key)); + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs.meta new file mode 100644 index 00000000..d62cd20b --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/RuntimeTypeCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9be1b596e081421dbb84a84fb2ec17fe +timeCreated: 1637461795 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs new file mode 100644 index 00000000..c4eee2e0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs @@ -0,0 +1,469 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace VContainer.Internal +{ + sealed class InjectConstructorInfo + { + public readonly ConstructorInfo ConstructorInfo; + public readonly ParameterInfo[] ParameterInfos; + public readonly object[] ParameterKeys; + + public InjectConstructorInfo(ConstructorInfo constructorInfo) + { + ConstructorInfo = constructorInfo; + ParameterInfos = constructorInfo.GetParameters(); + ParameterKeys = ExtractParameterKeys(ParameterInfos); + } + + public InjectConstructorInfo(ConstructorInfo constructorInfo, ParameterInfo[] parameterInfos) + { + ConstructorInfo = constructorInfo; + ParameterInfos = parameterInfos; + ParameterKeys = ExtractParameterKeys(parameterInfos); + } + + private static object[] ExtractParameterKeys(ParameterInfo[] parameters) + { + var keys = new object[parameters.Length]; + for (var i = 0; i < parameters.Length; i++) + { + var param = parameters[i]; + + var keyAttribute = param.GetCustomAttribute(); + if (keyAttribute != null) + { + keys[i] = keyAttribute.Key; + } + } + return keys; + } + } + + sealed class InjectMethodInfo + { + public readonly MethodInfo MethodInfo; + public readonly ParameterInfo[] ParameterInfos; + public readonly object[] ParameterKeys; + + public InjectMethodInfo(MethodInfo methodInfo) + { + MethodInfo = methodInfo; + ParameterInfos = methodInfo.GetParameters(); + ParameterKeys = ExtractParameterKeys(ParameterInfos); + } + + private static object[] ExtractParameterKeys(ParameterInfo[] parameters) + { + var keys = new object[parameters.Length]; + for (var i = 0; i < parameters.Length; i++) + { + var attr = parameters[i].GetCustomAttribute(); + if (attr != null) + { + keys[i] = attr.Key; + } + } + return keys; + } + } + + sealed class InjectFieldInfo + { + public readonly FieldInfo FieldInfo; + public readonly object Key; + + public InjectFieldInfo(FieldInfo fieldInfo) + { + FieldInfo = fieldInfo; + var attr = fieldInfo.GetCustomAttribute(); + if (attr != null) + { + Key = attr.Key; + } + } + + public Type FieldType => FieldInfo.FieldType; + public string Name => FieldInfo.Name; + + public void SetValue(object obj, object value) => FieldInfo.SetValue(obj, value); + } + + sealed class InjectPropertyInfo + { + public readonly PropertyInfo PropertyInfo; + public readonly object Key; + + public InjectPropertyInfo(PropertyInfo propertyInfo) + { + PropertyInfo = propertyInfo; + var attr = propertyInfo.GetCustomAttribute(); + if (attr != null) + { + Key = attr.Key; + } + } + + public Type PropertyType => PropertyInfo.PropertyType; + public string Name => PropertyInfo.Name; + + public void SetValue(object obj, object value) => PropertyInfo.SetValue(obj, value); + } + + sealed class InjectTypeInfo + { + public readonly Type Type; + public readonly InjectConstructorInfo InjectConstructor; + public readonly IReadOnlyList InjectMethods; + public readonly IReadOnlyList InjectFields; + public readonly IReadOnlyList InjectProperties; + + public InjectTypeInfo( + Type type, + InjectConstructorInfo injectConstructor, + IReadOnlyList injectMethods, + IReadOnlyList injectFields, + IReadOnlyList injectProperties) + { + Type = type; + InjectConstructor = injectConstructor; + InjectFields = injectFields; + InjectProperties = injectProperties; + InjectMethods = injectMethods; + } + } + + readonly struct DependencyInfo + { + public Type ImplementationType => Dependency.ImplementationType; + public IInstanceProvider Provider => Dependency.Provider; + + public readonly Registration Dependency; + readonly Registration owner; + readonly object method; // ctor or method + readonly ParameterInfo param; // param or field or prop + + public DependencyInfo(Registration dependency) + { + Dependency = dependency; + owner = null; + method = null; + param = null; + } + + public DependencyInfo(Registration dependency, Registration owner, ConstructorInfo ctor, ParameterInfo param) + { + Dependency = dependency; + this.owner = owner; + method = ctor; + this.param = param; + } + + public DependencyInfo(Registration dependency, Registration owner, MethodInfo method, ParameterInfo param) + { + Dependency = dependency; + this.owner = owner; + this.method = method; + this.param = param; + } + + public DependencyInfo(Registration dependency, Registration owner, FieldInfo field) + { + Dependency = dependency; + this.owner = owner; + method = field; + param = null; + } + + public DependencyInfo(Registration dependency, Registration owner, PropertyInfo prop) + { + Dependency = dependency; + this.owner = owner; + method = prop; + param = null; + } + + public override string ToString() + { + switch (method) + { + case ConstructorInfo _: + return $"{owner.ImplementationType}..ctor({param.Name})"; + case MethodInfo methodInfo: + return $"{owner.ImplementationType.FullName}.{methodInfo.Name}({param.Name})"; + case FieldInfo field: + return $"{owner.ImplementationType.FullName}.{field.Name}"; + case PropertyInfo prop: + return $"{owner.ImplementationType.FullName}.{prop.Name}"; + default: + return ""; + } + } + } + + static class TypeAnalyzer + { + public static InjectTypeInfo AnalyzeWithCache(Type type) => Cache.GetOrAdd(type, AnalyzeFunc); + + static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); + + [ThreadStatic] + static Stack circularDependencyChecker; + + static readonly Func AnalyzeFunc = Analyze; + + public static InjectTypeInfo Analyze(Type type) + { + var injectConstructor = default(InjectConstructorInfo); + var analyzedType = type; + var typeInfo = type.GetTypeInfo(); + + // Constructor, single [Inject] constructor -> single most parameters constructor + var annotatedConstructorCount = 0; + var maxParameters = -1; + foreach (var constructorInfo in typeInfo.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + { + if (constructorInfo.IsDefined(typeof(InjectAttribute), false)) + { + if (++annotatedConstructorCount > 1) + { + throw new VContainerException(type, $"Type found multiple [Inject] marked constructors, type: {type.Name}"); + } + injectConstructor = new InjectConstructorInfo(constructorInfo); + } + else if (annotatedConstructorCount <= 0) + { + var parameterInfos = constructorInfo.GetParameters(); + if (parameterInfos.Length > maxParameters) + { + injectConstructor = new InjectConstructorInfo(constructorInfo, parameterInfos); + maxParameters = parameterInfos.Length; + } + } + } + + if (injectConstructor == null) + { + var allowNoConstructor = type.IsEnum; +#if UNITY_2018_4_OR_NEWER + // It seems that Unity sometimes strips the constructor of Component at build time. + // In that case, allow null. + allowNoConstructor |= type.IsSubclassOf(typeof(UnityEngine.Component)); +#endif + if (!allowNoConstructor) + throw new VContainerException(type, $"Type does not found injectable constructor, type: {type.Name}"); + } + + var injectMethods = default(List); + var injectFields = default(List); + var injectProperties = default(List); + var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; + + while (type != null && type != typeof(object)) + { + // Methods, [Inject] Only + var methods = type.GetMethods(bindingFlags); + foreach (var methodInfo in methods) + { + if (methodInfo.IsDefined(typeof(InjectAttribute), false)) + { + if (injectMethods == null) + { + injectMethods = new List(); + } + else + { + // Skip if already exists + foreach (var x in injectMethods) + { + if (x.MethodInfo.GetBaseDefinition() == methodInfo.GetBaseDefinition()) + goto EndMethod; + } + } + + injectMethods.Add(new InjectMethodInfo(methodInfo)); + } + } + EndMethod: + + // Fields, [Inject] Only + var fields = type.GetFields(bindingFlags); + foreach (var fieldInfo in fields) + { + if (fieldInfo.IsDefined(typeof(InjectAttribute), false)) + { + if (injectFields == null) + { + injectFields = new List(); + } + else + { + if (Contains(injectFields, fieldInfo)) + { + var message = $"Duplicate injection found for field: {fieldInfo}"; + throw new VContainerException(type, message); + } + + if (injectFields.Any(x => x.Name == fieldInfo.Name)) + { + continue; + } + } + + injectFields.Add(new InjectFieldInfo(fieldInfo)); + } + } + + // Properties, [Inject] only + var props = type.GetProperties(bindingFlags); + foreach (var propertyInfo in props) + { + if (propertyInfo.IsDefined(typeof(InjectAttribute), false)) + { + if (injectProperties == null) + { + injectProperties = new List(); + } + else + { + foreach (var x in injectProperties) + { + if (x.Name == propertyInfo.Name) + goto EndProperty; + } + } + injectProperties.Add(new InjectPropertyInfo(propertyInfo)); + } + } + EndProperty: + + type = type.BaseType; + } + + return new InjectTypeInfo( + analyzedType, + injectConstructor, + injectMethods, + injectFields, + injectProperties); + } + + private static bool Contains(List fields, FieldInfo field) + { + for (var i = 0; i < fields.Count; i++) + { + var x = fields[i]; + if (x.Name == field.Name) + { + return true; + } + } + + return false; + } + + public static void CheckCircularDependency(IReadOnlyList registrations, Registry registry) + { + // ThreadStatic + if (circularDependencyChecker == null) + circularDependencyChecker = new Stack(); + + for (var i = 0; i < registrations.Count; i++) + { + circularDependencyChecker.Clear(); + CheckCircularDependencyRecursive(new DependencyInfo(registrations[i]), registry, circularDependencyChecker); + } + } + + static void CheckCircularDependencyRecursive(DependencyInfo current, Registry registry, Stack stack) + { + var i = 0; + foreach (var dependency in stack) + { + if (current.ImplementationType == dependency.ImplementationType) + { + // When instantiated by Func, the abstract type cycle is user-avoidable. + if (current.Dependency.Provider is FuncInstanceProvider) + { + return; + } + + stack.Push(current); + + var path = string.Join("\n", + stack.Take(i + 1) + .Reverse() + .Select((item, itemIndex) => $" [{itemIndex + 1}] {item} --> {item.ImplementationType.FullName}")); + throw new VContainerException(current.Dependency.ImplementationType, + $"Circular dependency detected!\n{path}"); + } + i++; + } + + stack.Push(current); + + if (Cache.TryGetValue(current.ImplementationType, out var injectTypeInfo)) + { + if (injectTypeInfo.InjectConstructor != null) + { + for (var paramIndex = 0; paramIndex < injectTypeInfo.InjectConstructor.ParameterInfos.Length; paramIndex++) + { + var x = injectTypeInfo.InjectConstructor.ParameterInfos[paramIndex]; + object key = paramIndex < injectTypeInfo.InjectConstructor.ParameterKeys.Length + ? injectTypeInfo.InjectConstructor.ParameterKeys[paramIndex] + : null; + if (registry.TryGet(x.ParameterType, key, out var parameterRegistration)) + { + CheckCircularDependencyRecursive(new DependencyInfo(parameterRegistration, current.Dependency, injectTypeInfo.InjectConstructor.ConstructorInfo, x), registry, stack); + } + } + } + + if (injectTypeInfo.InjectMethods != null) + { + foreach (var methodInfo in injectTypeInfo.InjectMethods) + { + for (var paramIndex = 0; paramIndex < methodInfo.ParameterInfos.Length; paramIndex++) + { + var x = methodInfo.ParameterInfos[paramIndex]; + object key = paramIndex < methodInfo.ParameterKeys.Length + ? methodInfo.ParameterKeys[paramIndex] + : null; + if (registry.TryGet(x.ParameterType, key, out var parameterRegistration)) + { + CheckCircularDependencyRecursive(new DependencyInfo(parameterRegistration, current.Dependency, methodInfo.MethodInfo, x), registry, stack); + } + } + } + } + + if (injectTypeInfo.InjectFields != null) + { + foreach (var x in injectTypeInfo.InjectFields) + { + if (registry.TryGet(x.FieldType, x.Key, out var fieldRegistration)) + { + CheckCircularDependencyRecursive(new DependencyInfo(fieldRegistration, current.Dependency, x.FieldInfo), registry, stack); + } + } + } + + if (injectTypeInfo.InjectProperties != null) + { + foreach (var x in injectTypeInfo.InjectProperties) + { + if (registry.TryGet(x.PropertyType, x.Key, out var propertyRegistration)) + { + CheckCircularDependencyRecursive(new DependencyInfo(propertyRegistration, current.Dependency, x.PropertyInfo), registry, stack); + } + } + } + } + + stack.Pop(); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs.meta new file mode 100644 index 00000000..5858cb65 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeAnalyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1039990f285746d39d3d6656ccdaac3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs new file mode 100644 index 00000000..975d875a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Internal +{ + sealed class TypeKeyHashTable2 + { + struct Bucket + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint DistAndFingerPrintFromHash(int hash) + { + return DistOne | ((uint)hash & FingerPrintMask); + } + + public const uint DistOne = 0x00000100; + public const uint FingerPrintMask = 0x000000FF; + + /// + /// upper 3 bytes: dist (distance of , also known PSL (probe sequence length)) + /// lower 1 bytes: fingerprint (lower 1 byte of hash code) + /// + public uint DistAndFingerPrint; + + /// + /// The index that point to the location where value is actually stored. + /// + public int EntryIndex; + } + + readonly Bucket[] buckets; + readonly KeyValuePair[] entries; + readonly int indexFor; + + int insertedEntryLength; + + public TypeKeyHashTable2(KeyValuePair[] values, float loadFactor = 0.75f) + { + var initialCapacity = (int)(values.Length / loadFactor); + + // make power of 2(and use mask) + // see: Hashing https://en.wikipedia.org/wiki/Hash_table + var capacity = 1; + while (capacity < initialCapacity) + { + capacity <<= 1; + } + + buckets = new Bucket[capacity]; + entries = new KeyValuePair[values.Length]; + indexFor = buckets.Length - 1; + + var entryIndex = 0; + foreach (var x in values) + { + Insert(x, entryIndex++); + } + } + + public bool TryGet(Type key, out TValue value) + { + var hash = RuntimeHelpers.GetHashCode(key); + var distAndFingerPrint = Bucket.DistAndFingerPrintFromHash(hash); + var bucketIndex = hash & indexFor; + var bucket = buckets[bucketIndex]; + + while (true) + { + if (distAndFingerPrint == bucket.DistAndFingerPrint) + { + // compare key + var entry = entries[bucket.EntryIndex]; + if (key == entry.Key) + { + value = entry.Value; + return true; + } + } + else if (distAndFingerPrint > bucket.DistAndFingerPrint) + { + // not found + value = default; + return false; + } + distAndFingerPrint += Bucket.DistOne; + bucketIndex = NextBucketIndex(bucketIndex); + bucket = buckets[bucketIndex]; + } + + } + + void Insert(KeyValuePair entry, int entryIndex) + { + var hash = RuntimeHelpers.GetHashCode(entry.Key); + var distAndFingerPrint = Bucket.DistAndFingerPrintFromHash(hash); + var bucketIndex = hash & indexFor; + + // robin food hashing + while (distAndFingerPrint <= buckets[bucketIndex].DistAndFingerPrint) + { + // key already exists + if (distAndFingerPrint == buckets[bucketIndex].DistAndFingerPrint && + entry.Key == entries[buckets[bucketIndex].EntryIndex].Key) + { + throw new InvalidOperationException($"The key already exists: {entry.Key}"); + } + + // + bucketIndex = NextBucketIndex(bucketIndex); + distAndFingerPrint += Bucket.DistOne; // + } + + entries[entryIndex] = entry; + SetBucketAt(bucketIndex, new Bucket + { + DistAndFingerPrint = distAndFingerPrint, + EntryIndex = entryIndex + }); + } + + /// + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void SetBucketAt(int i, Bucket bucket) + { + while (buckets[i].DistAndFingerPrint != 0) + { + // swap + (buckets[i], bucket) = (bucket, buckets[i]); + bucket.DistAndFingerPrint += Bucket.DistOne; + i = NextBucketIndex(i); + } + buckets[i] = bucket; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + int NextBucketIndex(int i) + { + return i + 1 >= buckets.Length ? 0 : i + 1; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs.meta new file mode 100644 index 00000000..8794f0d0 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Internal/TypeKeyHashTable2.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9c311a30613d4728b2b891e770db92f7 +timeCreated: 1695016383 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs new file mode 100644 index 00000000..ffb1c211 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer +{ + public sealed class Registration + { + public readonly Type ImplementationType; + public readonly IReadOnlyList InterfaceTypes; + public readonly Lifetime Lifetime; + public readonly IInstanceProvider Provider; + public readonly object Key; + + public Registration( + Type implementationType, + Lifetime lifetime, + IReadOnlyList interfaceTypes, + IInstanceProvider provider, + object key = null) + { + ImplementationType = implementationType; + InterfaceTypes = interfaceTypes; + Lifetime = lifetime; + Provider = provider; + Key = key; + } + + public override string ToString() + { + var contractTypes = InterfaceTypes != null ? string.Join(", ", InterfaceTypes) : ""; + var keyStr = Key == null ? "" : $" (Key: {Key})"; + return $"Registration {ImplementationType.Name}{keyStr} ContractTypes=[{contractTypes}] {Lifetime} {Provider}"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) => Provider.SpawnInstance(resolver); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs.meta new file mode 100644 index 00000000..154a57fb --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Registration.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 461e5e326abb400eb12109bba9e795c9 +timeCreated: 1602689096 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs new file mode 100644 index 00000000..fb7558da --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using VContainer.Internal; + +namespace VContainer +{ + public class RegistrationBuilder + { + protected internal readonly Type ImplementationType; + protected internal readonly Lifetime Lifetime; + + protected internal List InterfaceTypes; + protected internal List Parameters; + protected internal object Key; + + public RegistrationBuilder(Type implementationType, Lifetime lifetime) + { + ImplementationType = implementationType; + Lifetime = lifetime; + } + + public virtual Registration Build() + { + var injector = InjectorCache.GetOrBuild(ImplementationType); + var spawner = new InstanceProvider(injector, Parameters); + return new Registration( + ImplementationType, + Lifetime, + InterfaceTypes, + spawner, + Key); + } + + public RegistrationBuilder As() + => As(typeof(TInterface)); + + public RegistrationBuilder As() + => As(typeof(TInterface1), typeof(TInterface2)); + + public RegistrationBuilder As() + => As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3)); + + public RegistrationBuilder As() + => As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3), typeof(TInterface4)); + + public RegistrationBuilder AsSelf() + { + AddInterfaceType(ImplementationType); + return this; + } + + public virtual RegistrationBuilder AsImplementedInterfaces() + { + InterfaceTypes = InterfaceTypes ?? new List(); + InterfaceTypes.AddRange(ImplementationType.GetInterfaces()); + return this; + } + + public RegistrationBuilder As(Type interfaceType) + { + AddInterfaceType(interfaceType); + return this; + } + + public RegistrationBuilder As(Type interfaceType1, Type interfaceType2) + { + AddInterfaceType(interfaceType1); + AddInterfaceType(interfaceType2); + return this; + } + + public RegistrationBuilder As(Type interfaceType1, Type interfaceType2, Type interfaceType3) + { + AddInterfaceType(interfaceType1); + AddInterfaceType(interfaceType2); + AddInterfaceType(interfaceType3); + return this; + } + + public RegistrationBuilder As(params Type[] interfaceTypes) + { + foreach (var interfaceType in interfaceTypes) + { + AddInterfaceType(interfaceType); + } + return this; + } + + public RegistrationBuilder WithParameter(string name, object value) + { + Parameters = Parameters ?? new List(); + Parameters.Add(new NamedParameter(name, value)); + return this; + } + + public RegistrationBuilder WithParameter(string name, Func value) + { + Parameters = Parameters ?? new List(); + Parameters.Add(new FuncNamedParameter(name, value)); + return this; + } + + public RegistrationBuilder WithParameter(Type type, object value) + { + Parameters = Parameters ?? new List(); + Parameters.Add(new TypedParameter(type, value)); + return this; + } + + public RegistrationBuilder WithParameter(Type type, Func value) + { + Parameters = Parameters ?? new List(); + Parameters.Add(new FuncTypedParameter(type, value)); + return this; + } + + public RegistrationBuilder WithParameter(TParam value) + { + return WithParameter(typeof(TParam), value); + } + + public RegistrationBuilder WithParameter(Func value) + { + return WithParameter(typeof(TParam), resolver => value(resolver)); + } + + public RegistrationBuilder WithParameter(Func value) + { + return WithParameter(typeof(TParam), _ => value()); + } + + public RegistrationBuilder Keyed(object key) + { + Key = key; + return this; + } + + protected virtual void AddInterfaceType(Type interfaceType) + { + if (!interfaceType.IsAssignableFrom(ImplementationType)) + { + throw new VContainerException(interfaceType, $"{ImplementationType} is not assignable from {interfaceType}"); + } + InterfaceTypes = InterfaceTypes ?? new List(); + if (!InterfaceTypes.Contains(interfaceType)) + InterfaceTypes.Add(interfaceType); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs.meta new file mode 100644 index 00000000..e127f146 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/RegistrationBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d35bc4ab986c5486e89c9856cfaec07e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs new file mode 100644 index 00000000..8a939354 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace VContainer.Internal +{ + public sealed class Registry + { + private static readonly object AnyKey = new { Any = true }; + + [ThreadStatic] + static IDictionary<(Type, object), Registration> buildBuffer = new Dictionary<(Type, object), Registration>(256); + + readonly FixedTypeObjectKeyHashtable hashTable; + + public static Registry Build(Registration[] registrations) + { + // ThreadStatic + if (buildBuffer == null) + buildBuffer = new Dictionary<(Type, object), Registration>(256); + buildBuffer.Clear(); + + foreach (var registration in registrations) + { + var key = (registration.ImplementationType, key: registration.Key); + + if (registration.InterfaceTypes is IReadOnlyList interfaceTypes) + { + // ReSharper disable once ForCanBeConvertedToForeach + for (var i = 0; i < interfaceTypes.Count; i++) + { + AddToBuildBuffer(buildBuffer, interfaceTypes[i], registration); + } + + // Mark the ImplementationType with a guard because we need to check if it exists later. + if (!buildBuffer.ContainsKey(key)) + { + buildBuffer.Add(key, null); + } + } + else + { + AddToBuildBuffer(buildBuffer, registration.ImplementationType, registration); + } + } + + var hashTable = new FixedTypeObjectKeyHashtable(buildBuffer.ToArray()); + return new Registry(hashTable); + } + + static void AddToBuildBuffer(IDictionary<(Type, object), Registration> buf, Type service, Registration registration) + { + var key = (service, key: registration.Key); + var collectionKey = (service, AnyKey); + + if (buf.TryGetValue(collectionKey, out var exists) && exists != null) + { + CollectionInstanceProvider collection; + if (buf.TryGetValue((RuntimeTypeCache.EnumerableTypeOf(service), null), out var found) && + found.Provider is CollectionInstanceProvider foundCollection) + { + collection = foundCollection; + } + else + { + collection = new CollectionInstanceProvider(service) { exists }; + var newRegistration = new Registration( + RuntimeTypeCache.ArrayTypeOf(service), + Lifetime.Transient, + new List + { + RuntimeTypeCache.EnumerableTypeOf(service), + RuntimeTypeCache.ReadOnlyListTypeOf(service), + }, collection); + AddCollectionToBuildBuffer(buf, newRegistration); + } + collection.Add(registration); + + // Overwritten by the later registration + buf[collectionKey] = registration; + buf[key] = registration; + } + else + { + buf.Add(key, registration); + buf.Add(collectionKey, registration); + } + } + + static void AddCollectionToBuildBuffer(IDictionary<(Type, object), Registration> buf, Registration collectionRegistration) + { + // ReSharper disable once ForCanBeConvertedToForeach + for (var i = 0; i < collectionRegistration.InterfaceTypes.Count; i++) + { + var collectionType = collectionRegistration.InterfaceTypes[i]; + try + { + buf.Add((collectionType, null), collectionRegistration); + } + catch (ArgumentException) + { + throw new VContainerException(collectionType, $"Registration with the same key already exists: {collectionRegistration}"); + } + } + } + + Registry(FixedTypeObjectKeyHashtable hashTable) + { + this.hashTable = hashTable; + } + + public bool TryGet(Type interfaceType, object key, out Registration registration) + { + if (hashTable.TryGet(interfaceType, key, out registration)) + return registration != null; + + if (interfaceType.IsConstructedGenericType) + { + var openGenericType = RuntimeTypeCache.OpenGenericTypeOf(interfaceType); + var typeParameters = RuntimeTypeCache.GenericTypeParametersOf(interfaceType); + return TryGetClosedGenericRegistration(interfaceType, key, openGenericType, typeParameters, out registration) || + TryFallbackToSingleElementCollection(interfaceType, openGenericType, typeParameters, out registration) || + TryFallbackToContainerLocal(interfaceType, openGenericType, key, typeParameters, out registration); + } + return false; + } + + bool TryGetClosedGenericRegistration(Type interfaceType, object key, Type openGenericType, + Type[] typeParameters, + out Registration registration) + { + if (hashTable.TryGet(openGenericType, key, out var openGenericRegistration)) + { + if (openGenericRegistration.Provider is OpenGenericInstanceProvider implementationRegistration) + { + registration = implementationRegistration.GetClosedRegistration(interfaceType, typeParameters); + return true; + } + } + + registration = null; + return false; + } + + public bool Exists(Type type, object key) + { + if (hashTable.TryGet(type, key, out _)) + return true; + + if (type.IsConstructedGenericType) + { + type = RuntimeTypeCache.OpenGenericTypeOf(type); + } + + return hashTable.TryGet(type, key, out _); + } + + bool TryFallbackToContainerLocal( + Type closedGenericType, + Type openGenericType, + object key, + IReadOnlyList typeParameters, + out Registration newRegistration) + { + if (openGenericType == typeof(ContainerLocal<>)) + { + var valueType = typeParameters[0]; + if (TryGet(valueType, key, out var valueRegistration)) + { + var spawner = new ContainerLocalInstanceProvider(closedGenericType, valueRegistration); + newRegistration = new Registration(closedGenericType, Lifetime.Scoped, null, spawner); + return true; + } + } + newRegistration = null; + return false; + } + + bool TryFallbackToSingleElementCollection( + Type closedGenericType, + Type openGenericType, + IReadOnlyList typeParameters, + out Registration newRegistration) + { + if (CollectionInstanceProvider.Match(openGenericType)) + { + var elementType = typeParameters[0]; + var collection = new CollectionInstanceProvider(elementType); + + if (hashTable.TryGet(elementType, AnyKey, out var elementRegistration) && elementRegistration != null) + { + collection.Add(elementRegistration); + } + newRegistration = new Registration( + RuntimeTypeCache.ArrayTypeOf(elementType), + Lifetime.Transient, + new List + { + RuntimeTypeCache.EnumerableTypeOf(elementType), + RuntimeTypeCache.ReadOnlyListTypeOf(elementType), + }, collection); + return true; + } + newRegistration = null; + return false; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs.meta new file mode 100644 index 00000000..5e271f09 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Registry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe300d39071d4c7fb874bbbc4af07e8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity.meta new file mode 100644 index 00000000..795178bc --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6268320f0c31e4403b087ac590e7c106 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs new file mode 100644 index 00000000..519b5c87 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs @@ -0,0 +1,22 @@ +using System; + +namespace VContainer.Unity +{ + public sealed class ActionInstaller : IInstaller + { + public static implicit operator ActionInstaller(Action installation) + => new ActionInstaller(installation); + + readonly Action configuration; + + public ActionInstaller(Action configuration) + { + this.configuration = configuration; + } + + public void Install(IContainerBuilder builder) + { + configuration(builder); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs.meta new file mode 100644 index 00000000..fddd158c --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ActionInstaller.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 48336697de89414886abfd92d2ac9218 +timeCreated: 1593187294 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs new file mode 100644 index 00000000..b75fd7f1 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs @@ -0,0 +1,121 @@ +using System; +using UnityEngine; +using UnityEngine.SceneManagement; +using VContainer.Internal; + +namespace VContainer.Unity +{ + struct ComponentDestination + { + public Transform Parent; + public Func ParentFinder; + public bool DontDestroyOnLoad; + + public Transform GetParent(IObjectResolver resolver) + { + if (Parent != null) + return Parent; + if (ParentFinder != null) + return ParentFinder(resolver); + return null; + } + + public void ApplyDontDestroyOnLoadIfNeeded(Component component) + { + if (DontDestroyOnLoad) + { + UnityEngine.Object.DontDestroyOnLoad(component); + } + } + } + + public sealed class ComponentRegistrationBuilder : RegistrationBuilder + { + readonly object instance; + readonly Func prefabFinder; + readonly string gameObjectName; + + ComponentDestination destination; + Scene scene; + + internal ComponentRegistrationBuilder(object instance) + : base(instance.GetType(), Lifetime.Singleton) + { + this.instance = instance; + } + + internal ComponentRegistrationBuilder(in Scene scene, Type implementationType) + : base(implementationType, Lifetime.Singleton) + { + this.scene = scene; + } + + internal ComponentRegistrationBuilder( + Func prefabFinder, + Type implementationType, + Lifetime lifetime) + : base(implementationType, lifetime) + { + this.prefabFinder = prefabFinder; + } + + internal ComponentRegistrationBuilder( + string gameObjectName, + Type implementationType, + Lifetime lifetime) + : base(implementationType, lifetime) + { + this.gameObjectName = gameObjectName; + } + + public override Registration Build() + { + IInstanceProvider provider; + + if (instance != null) + { + var injector = InjectorCache.GetOrBuild(ImplementationType); + provider = new ExistingComponentProvider(instance, injector, Parameters, destination.DontDestroyOnLoad); + } + else if (scene.IsValid()) + { + provider = new FindComponentProvider(ImplementationType, Parameters, in scene, in destination); + } + else if (prefabFinder != null) + { + var injector = InjectorCache.GetOrBuild(ImplementationType); + provider = new PrefabComponentProvider(prefabFinder, injector, Parameters, in destination); + } + else + { + var injector = InjectorCache.GetOrBuild(ImplementationType); + provider = new NewGameObjectProvider(ImplementationType, injector, Parameters, in destination, gameObjectName); + } + return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider, Key); + } + + public ComponentRegistrationBuilder UnderTransform(Transform parent) + { + destination.Parent = parent; + return this; + } + + public ComponentRegistrationBuilder UnderTransform(Func parentFinder) + { + destination.ParentFinder = _ => parentFinder(); + return this; + } + + public ComponentRegistrationBuilder UnderTransform(Func parentFinder) + { + destination.ParentFinder = parentFinder; + return this; + } + + public ComponentRegistrationBuilder DontDestroyOnLoad() + { + destination.DontDestroyOnLoad = true; + return this; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs.meta new file mode 100644 index 00000000..faafab3e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ComponentRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c8d9071a8a0a4915bc374a6691d9aebd +timeCreated: 1592589484 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs new file mode 100644 index 00000000..136ecd2e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs @@ -0,0 +1,420 @@ +using System; +using UnityEngine; +using VContainer.Internal; + +#if VCONTAINER_ECS_INTEGRATION +using Unity.Entities; +#endif + +namespace VContainer.Unity +{ + public readonly struct EntryPointsBuilder + { + public static void EnsureDispatcherRegistered(IContainerBuilder containerBuilder) + { + if (containerBuilder.Exists(typeof(EntryPointDispatcher), false)) return; + containerBuilder.Register(Lifetime.Scoped); + + containerBuilder.RegisterEntryPointExceptionHandler(UnityEngine.Debug.LogException); + + containerBuilder.RegisterBuildCallback(container => + { + container.Resolve().Dispatch(); + }); + } + + readonly IContainerBuilder containerBuilder; + readonly Lifetime lifetime; + + public EntryPointsBuilder(IContainerBuilder containerBuilder, Lifetime lifetime) + { + this.containerBuilder = containerBuilder; + this.lifetime = lifetime; + } + + public RegistrationBuilder Add() + => containerBuilder.Register(lifetime).AsImplementedInterfaces(); + + public void OnException(Action exceptionHandler) + => containerBuilder.RegisterEntryPointExceptionHandler(exceptionHandler); + } + + public readonly struct ComponentsBuilder + { + readonly IContainerBuilder containerBuilder; + readonly Transform parentTransform; + + public ComponentsBuilder(IContainerBuilder containerBuilder, Transform parentTransform = null) + { + this.containerBuilder = containerBuilder; + this.parentTransform = parentTransform; + } + + public RegistrationBuilder AddInstance(TInterface component) + { + return containerBuilder.RegisterComponent(component); + } + + public ComponentRegistrationBuilder AddInHierarchy() + => containerBuilder.RegisterComponentInHierarchy() + .UnderTransform(parentTransform); + + public ComponentRegistrationBuilder AddOnNewGameObject(Lifetime lifetime, string newGameObjectName = null) + where T : Component + => containerBuilder.RegisterComponentOnNewGameObject(lifetime, newGameObjectName) + .UnderTransform(parentTransform); + + public ComponentRegistrationBuilder AddInNewPrefab(T prefab, Lifetime lifetime) + where T : Component + => containerBuilder.RegisterComponentInNewPrefab(prefab, lifetime) + .UnderTransform(parentTransform); + } + + public static class ContainerBuilderUnityExtensions + { + public static void UseEntryPoints( + this IContainerBuilder builder, + Action configuration) + { + builder.UseEntryPoints(Lifetime.Singleton, configuration); + } + + public static void UseEntryPoints( + this IContainerBuilder builder, + Lifetime lifetime, + Action configuration) + { + EntryPointsBuilder.EnsureDispatcherRegistered(builder); + configuration(new EntryPointsBuilder(builder, lifetime)); + } + + public static void UseComponents(this IContainerBuilder builder, Action configuration) + { + configuration(new ComponentsBuilder(builder)); + } + + public static void UseComponents( + this IContainerBuilder builder, + Transform root, + Action configuration) + { + configuration(new ComponentsBuilder(builder, root)); + } + + public static RegistrationBuilder RegisterEntryPoint( + this IContainerBuilder builder, + Lifetime lifetime = Lifetime.Singleton) + { + EntryPointsBuilder.EnsureDispatcherRegistered(builder); + return builder.Register(lifetime).AsImplementedInterfaces(); + } + + public static RegistrationBuilder RegisterEntryPoint( + this IContainerBuilder builder, + Func implementationConfiguration, + Lifetime lifetime) + { + EntryPointsBuilder.EnsureDispatcherRegistered(builder); + return builder.Register(new FuncRegistrationBuilder(container => implementationConfiguration(container), + typeof(TInterface), lifetime)).AsImplementedInterfaces(); + } + + public static void RegisterEntryPointExceptionHandler( + this IContainerBuilder builder, + Action exceptionHandler) + { + builder.Register(c => new EntryPointExceptionHandler(exceptionHandler), Lifetime.Scoped); + } + + public static RegistrationBuilder RegisterComponent( + this IContainerBuilder builder, + TInterface component) + { + var registrationBuilder = new ComponentRegistrationBuilder(component).As(typeof(TInterface)); + // Force inject execution + builder.RegisterBuildCallback(container => container.Resolve()); + return builder.Register(registrationBuilder); + } + + public static ComponentRegistrationBuilder RegisterComponentInHierarchy( + this IContainerBuilder builder, + Type type) + { + var lifetimeScope = (LifetimeScope)builder.ApplicationOrigin; + var scene = lifetimeScope.gameObject.scene; + + var registrationBuilder = new ComponentRegistrationBuilder(scene, type); + // Force inject execution + builder.RegisterBuildCallback( + container => + { + container.Resolve( + registrationBuilder.InterfaceTypes != null + ? registrationBuilder.InterfaceTypes[0] + : registrationBuilder.ImplementationType + ); + } + ); + return builder.Register(registrationBuilder); + } + + public static ComponentRegistrationBuilder RegisterComponentInHierarchy(this IContainerBuilder builder) + { + return builder.RegisterComponentInHierarchy(typeof(T)); + } + + public static ComponentRegistrationBuilder RegisterComponentOnNewGameObject( + this IContainerBuilder builder, + Type type, + Lifetime lifetime, + string newGameObjectName = null) + { + return builder.Register(new ComponentRegistrationBuilder(newGameObjectName, type, lifetime)); + } + + public static ComponentRegistrationBuilder RegisterComponentOnNewGameObject( + this IContainerBuilder builder, + Lifetime lifetime, + string newGameObjectName = null) + where T : Component + { + return builder.RegisterComponentOnNewGameObject(typeof(T), lifetime, newGameObjectName); + } + + public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( + this IContainerBuilder builder, + Type interfaceType, + Component prefab, + Lifetime lifetime) + { + var componentRegistrationBuilder = builder.Register(new ComponentRegistrationBuilder(_ => prefab, prefab.GetType(), lifetime)); + componentRegistrationBuilder.As(interfaceType); + return componentRegistrationBuilder; + } + + public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( + this IContainerBuilder builder, + T prefab, + Lifetime lifetime) + where T : Component + { + return builder.RegisterComponentInNewPrefab(typeof(T), prefab, lifetime); + } + + public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( + this IContainerBuilder builder, + Func prefab, + Lifetime lifetime) + where T : Component + { + return builder.Register(new ComponentRegistrationBuilder(prefab, typeof(T), lifetime)); + } + + public static ComponentRegistrationBuilder RegisterComponentInNewPrefab( + this IContainerBuilder builder, + Func prefab, + Lifetime lifetime) + where TImplement : Component, TInterface + { + var componentRegistrationBuilder = builder.Register(new ComponentRegistrationBuilder(prefab, typeof(TImplement), lifetime)); + componentRegistrationBuilder.As(); + return componentRegistrationBuilder; + } + +#if VCONTAINER_ECS_INTEGRATION + public readonly struct NewWorldBuilder + { + readonly IContainerBuilder containerBuilder; + readonly string worldName; + readonly Lifetime worldLifetime; + + public NewWorldBuilder(IContainerBuilder containerBuilder, string worldName, Lifetime worldLifetime) + { + this.containerBuilder = containerBuilder; + this.worldName = worldName; + this.worldLifetime = worldLifetime; + + containerBuilder.RegisterNewWorld(worldName, worldLifetime); + } + + public SystemRegistrationBuilder Add() where T : ComponentSystemBase + => containerBuilder.RegisterSystemIntoWorld(worldName); +#if UNITY_2022_2_OR_NEWER + public UnmanagedSystemRegistrationBuilder AddUnmanaged(T system) where T : unmanaged, ISystem + => containerBuilder.RegisterUnmanagedSystemIntoWorld(worldName); +#endif + } + + public readonly struct DefaultWorldBuilder + { + readonly IContainerBuilder containerBuilder; + + public DefaultWorldBuilder(IContainerBuilder containerBuilder) + { + this.containerBuilder = containerBuilder; + } + + public RegistrationBuilder Add() where T : ComponentSystemBase + => containerBuilder.RegisterSystemFromDefaultWorld(); +#if UNITY_2022_2_OR_NEWER + public RegistrationBuilder AddUnmanaged() where T : unmanaged, ISystem + => containerBuilder.RegisterUnmanagedSystemFromDefaultWorld(); +#endif + } + + // Use exisiting world + + public static void UseDefaultWorld(this IContainerBuilder builder, Action configuration) + { + var systems = new DefaultWorldBuilder(builder); + configuration(systems); + } + + public static RegistrationBuilder RegisterSystemFromDefaultWorld(this IContainerBuilder builder) + where T : ComponentSystemBase + => RegisterSystemFromWorld(builder, World.DefaultGameObjectInjectionWorld); +#if UNITY_2022_2_OR_NEWER + public static RegistrationBuilder RegisterUnmanagedSystemFromDefaultWorld(this IContainerBuilder builder) + where T : unmanaged, ISystem + => RegisterUnmanagedSystemFromWorld(builder, World.DefaultGameObjectInjectionWorld); +#endif + public static RegistrationBuilder RegisterSystemFromWorld(this IContainerBuilder builder, World world) + where T : ComponentSystemBase + { +#if UNITY_2022_2_OR_NEWER + var system = world.GetExistingSystemManaged(); +#else + var system = world.GetExistingSystem(); +#endif + if (system is null) + throw new ArgumentException($"{typeof(T).FullName} is not in the world {world}"); + + return builder.RegisterComponent(system) + .As(typeof(ComponentSystemBase), typeof(T)); + } +#if UNITY_2022_2_OR_NEWER + public static RegistrationBuilder RegisterUnmanagedSystemFromWorld(this IContainerBuilder builder, World world) + where T : unmanaged, ISystem + { + var system = world.Unmanaged.GetExistingUnmanagedSystem(); + if (system == SystemHandle.Null) + throw new ArgumentException($"{typeof(T).FullName} is not in the world {world}"); + + Type refType = typeof(UnmanagedSystemReference<>); + Type target = refType.MakeGenericType(typeof(T)); + var reference = (UnmanagedSystemReference)Activator.CreateInstance(target, system, world); + + return builder.RegisterComponent(reference) + .As(target); + } +#endif + // Use custom world + + public static void UseNewWorld( + this IContainerBuilder builder, + string worldName, + Lifetime lifetime, + Action configuration) + { + var systems = new NewWorldBuilder(builder, worldName, lifetime); + configuration(systems); + } + + public static RegistrationBuilder RegisterNewWorld( + this IContainerBuilder builder, + string worldName, + Lifetime lifetime, + Action configuration = null) + { + builder.Register(lifetime) + .WithParameter(typeof(string), worldName); + return builder.Register(new WorldRegistrationBuilder(worldName, lifetime, configuration)); + } + + public static SystemRegistrationBuilder RegisterSystemIntoWorld( + this IContainerBuilder builder, + string worldName) + where T : ComponentSystemBase + { + var registrationBuilder = new SystemRegistrationBuilder(typeof(T), worldName) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } + public static SystemRegistrationBuilder RegisterSystemIntoWorld( + this IContainerBuilder builder, + string worldName) + where T : ComponentSystemBase + where T1 : ComponentSystemGroup + { + var registrationBuilder = new SystemRegistrationBuilder(typeof(T), worldName) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } + +#if UNITY_2022_2_OR_NEWER + public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoWorld( + this IContainerBuilder builder, + string worldName) + where T : unmanaged, ISystem + { + var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), worldName) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } + + public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoWorld( + this IContainerBuilder builder, + string worldName) + where T : unmanaged, ISystem + where T1 : ComponentSystemGroup + { + var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), worldName) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } +#endif + public static SystemRegistrationBuilder RegisterSystemIntoDefaultWorld(this IContainerBuilder builder) + where T : ComponentSystemBase + { + var registrationBuilder = new SystemRegistrationBuilder(typeof(T), null) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } + + public static SystemRegistrationBuilder RegisterSystemIntoDefaultWorld(this IContainerBuilder builder) + where T : ComponentSystemBase + where T1 : ComponentSystemGroup + { + var registrationBuilder = new SystemRegistrationBuilder(typeof(T), null) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } + +#if UNITY_2022_2_OR_NEWER + public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoDefaultWorld(this IContainerBuilder builder) + where T : unmanaged, ISystem + { + var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), null) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } + public static UnmanagedSystemRegistrationBuilder RegisterUnmanagedSystemIntoDefaultWorld(this IContainerBuilder builder) + where T : unmanaged, ISystem + where T1 : ComponentSystemGroup + { + var registrationBuilder = new UnmanagedSystemRegistrationBuilder(typeof(T), null) + .IntoGroup(); + + return builder.Register(registrationBuilder); + } +#endif +#endif + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs.meta new file mode 100644 index 00000000..f516d828 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ContainerBuilderUnityExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5e9b811ca5454c89a0905e0127ea62b8 +timeCreated: 1593020228 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs new file mode 100644 index 00000000..94230a71 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs @@ -0,0 +1,20 @@ +using System; + +namespace VContainer.Unity +{ + sealed class AsyncLoopItem : IPlayerLoopItem + { + readonly Action action; + + public AsyncLoopItem(Action action) + { + this.action = action; + } + + public bool MoveNext() + { + action(); + return false; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs.meta new file mode 100644 index 00000000..93498e5a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/DisposeLoopItem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 792f76fbc1dd409887297cdfe8aa3aa9 +timeCreated: 1653803074 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs new file mode 100644 index 00000000..241efc18 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using VContainer.Internal; +#if VCONTAINER_ECS_INTEGRATION +using Unity.Entities; +#endif + +namespace VContainer.Unity +{ + public sealed class EntryPointDispatcher : IDisposable + { + readonly IObjectResolver container; + readonly CompositeDisposable disposable = new CompositeDisposable(); + + [Inject] + public EntryPointDispatcher(IObjectResolver container) + { + this.container = container; + } + + public void Dispatch() + { + PlayerLoopHelper.EnsureInitialized(); + + var exceptionHandler = container.ResolveOrDefault(); + + var initializables = container.Resolve>>().Value; + for (var i = 0; i < initializables.Count; i++) + { + try + { + initializables[i].Initialize(); + } + catch (Exception ex) + { + if (exceptionHandler != null) + exceptionHandler.Publish(ex); + else + UnityEngine.Debug.LogException(ex); + } + } + + var postInitializables = container.Resolve>>().Value; + for (var i = 0; i < postInitializables.Count; i++) + { + try + { + postInitializables[i].PostInitialize(); + } + catch (Exception ex) + { + if (exceptionHandler != null) + exceptionHandler.Publish(ex); + else + UnityEngine.Debug.LogException(ex); + } + } + + var startables = container.Resolve>>().Value; + if (startables.Count > 0) + { + var loopItem = new StartableLoopItem(startables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Startup, loopItem); + } + + var postStartables = container.Resolve>>().Value; + if (postStartables.Count > 0) + { + var loopItem = new PostStartableLoopItem(postStartables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostStartup, loopItem); + } + + var fixedTickables = container.Resolve>>().Value; + if (fixedTickables.Count > 0) + { + var loopItem = new FixedTickableLoopItem(fixedTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.FixedUpdate, loopItem); + } + + var postFixedTickables = container.Resolve>>().Value; + if (postFixedTickables.Count > 0) + { + var loopItem = new PostFixedTickableLoopItem(postFixedTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostFixedUpdate, loopItem); + } + + var tickables = container.Resolve>>().Value; + if (tickables.Count > 0) + { + var loopItem = new TickableLoopItem(tickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Update, loopItem); + } + + var postTickables = container.Resolve>>().Value; + if (postTickables.Count > 0) + { + var loopItem = new PostTickableLoopItem(postTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostUpdate, loopItem); + } + + var lateTickables = container.Resolve>>().Value; + if (lateTickables.Count > 0) + { + var loopItem = new LateTickableLoopItem(lateTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.LateUpdate, loopItem); + } + + var postLateTickables = container.Resolve>>().Value; + if (postLateTickables.Count > 0) + { + var loopItem = new PostLateTickableLoopItem(postLateTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostLateUpdate, loopItem); + } + +#if VCONTAINER_UNITASK_INTEGRATION || UNITY_2021_3_OR_NEWER + var asyncStartables = container.Resolve>>().Value; + if (asyncStartables.Count > 0) + { + var loopItem = new AsyncStartableLoopItem(asyncStartables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Startup, loopItem); + } +#endif + +#if VCONTAINER_ECS_INTEGRATION + container.Resolve>>(); + + var worldHelpers = container.Resolve>>().Value; + for (var i = 0; i < worldHelpers.Count; i++) + { + worldHelpers[i].SortSystems(); + } +#endif + } + + public void Dispose() => disposable.Dispose(); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs.meta new file mode 100644 index 00000000..3ffec1de --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointDispatcher.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e8b5d8a26f2d409fb1de359daa251bde +timeCreated: 1614397266 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs new file mode 100644 index 00000000..4fb8faaf --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs @@ -0,0 +1,19 @@ +using System; + +namespace VContainer.Unity +{ + sealed class EntryPointExceptionHandler + { + readonly Action handler; + + public EntryPointExceptionHandler(Action handler) + { + this.handler = handler; + } + + public void Publish(Exception ex) + { + handler.Invoke(ex); + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs.meta new file mode 100644 index 00000000..a8310b70 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/EntryPointExceptionHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b60c8fd4709435d82c04a947e096fa3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs new file mode 100644 index 00000000..dd0cb197 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs @@ -0,0 +1,7 @@ +namespace VContainer.Unity +{ + public interface IInstaller + { + void Install(IContainerBuilder builder); + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs.meta new file mode 100644 index 00000000..77e04901 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/IInstaller.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 22d89c28d4364858a331cd5f669e58b7 +timeCreated: 1593187294 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders.meta new file mode 100644 index 00000000..54b63d44 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d7d46367acb94de1966baea5febbf70c +timeCreated: 1637900702 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs new file mode 100644 index 00000000..a304f6a2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace VContainer.Unity +{ + sealed class ExistingComponentProvider : IInstanceProvider + { + readonly object instance; + readonly IInjector injector; + readonly IReadOnlyList customParameters; + readonly bool dontDestroyOnLoad; + + public ExistingComponentProvider( + object instance, + IInjector injector, + IReadOnlyList customParameters, + bool dontDestroyOnLoad = false) + { + this.instance = instance; + this.customParameters = customParameters; + this.injector = injector; + this.dontDestroyOnLoad = dontDestroyOnLoad; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object SpawnInstance(IObjectResolver resolver) + { + injector.Inject(instance, resolver, customParameters); + if (dontDestroyOnLoad) + { + if (instance is UnityEngine.Object component) + { + UnityEngine.Object.DontDestroyOnLoad(component); + } + else + { + throw new VContainerException(instance.GetType(), + $"Cannot apply `DontDestroyOnLoad`. {instance.GetType().Name} is not a UnityEngine.Object"); + } + } + return instance; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs.meta new file mode 100644 index 00000000..b6ba0ad3 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/ExistingComponentProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3eaf1bb53ea4494484290e95207cc8f3 +timeCreated: 1620020530 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs new file mode 100644 index 00000000..bafabe9b --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using VContainer.Internal; + +namespace VContainer.Unity +{ + sealed class FindComponentProvider : IInstanceProvider + { + readonly Type componentType; + readonly IReadOnlyList customParameters; + ComponentDestination destination; + Scene scene; + + public FindComponentProvider( + Type componentType, + IReadOnlyList customParameters, + in Scene scene, + in ComponentDestination destination) + { + this.componentType = componentType; + this.customParameters = customParameters; + this.scene = scene; + this.destination = destination; + } + + public object SpawnInstance(IObjectResolver resolver) + { + var component = default(Component); + + var parent = destination.GetParent(resolver); + if (parent != null) + { + component = parent.GetComponentInChildren(componentType, true); + if (component == null) + { + throw new VContainerException(componentType, $"{componentType} is not in the parent {parent.name} : {this}"); + } + } + else if (scene.IsValid()) + { + using (ListPool.Get(out var gameObjectBuffer)) + { + scene.GetRootGameObjects(gameObjectBuffer); + foreach (var gameObject in gameObjectBuffer) + { + component = gameObject.GetComponentInChildren(componentType, true); + if (component != null) break; + } + } + if (component == null) + { + throw new VContainerException(componentType, $"{componentType} is not in this scene {scene.path} : {this}"); + } + } + else + { + throw new VContainerException(componentType, $"Invalid Component find target {this}"); + } + + if (component is MonoBehaviour monoBehaviour) + { + var injector = InjectorCache.GetOrBuild(monoBehaviour.GetType()); + injector.Inject(monoBehaviour, resolver, customParameters); + } + + destination.ApplyDontDestroyOnLoadIfNeeded(component); + return component; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs.meta new file mode 100644 index 00000000..3ad0915b --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/FindComponentProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8feb76212733400e80e88bc385685993 +timeCreated: 1619932417 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs new file mode 100644 index 00000000..96ca364c --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace VContainer.Unity +{ + sealed class NewGameObjectProvider : IInstanceProvider + { + readonly Type componentType; + readonly IInjector injector; + readonly IReadOnlyList customParameters; + readonly string newGameObjectName; + ComponentDestination destination; + + public NewGameObjectProvider( + Type componentType, + IInjector injector, + IReadOnlyList customParameters, + in ComponentDestination destination, + string newGameObjectName = null) + { + this.componentType = componentType; + this.customParameters = customParameters; + this.injector = injector; + this.destination = destination; + this.newGameObjectName = newGameObjectName; + } + + public object SpawnInstance(IObjectResolver resolver) + { + var name = string.IsNullOrEmpty(newGameObjectName) + ? componentType.Name + : newGameObjectName; + var gameObject = new GameObject(name); + gameObject.SetActive(false); + + var parent = destination.GetParent(resolver); + if (parent != null) + { + gameObject.transform.SetParent(parent); + } + var component = gameObject.AddComponent(componentType); + + injector.Inject(component, resolver, customParameters); + destination.ApplyDontDestroyOnLoadIfNeeded(component); + + component.gameObject.SetActive(true); + return component; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs.meta new file mode 100644 index 00000000..eed69847 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/NewGameObjectProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f18c3e4b45d349ca8f1d388dca5bc8b8 +timeCreated: 1619933144 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs new file mode 100644 index 00000000..b6620316 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace VContainer.Unity +{ + sealed class PrefabComponentProvider : IInstanceProvider + { + readonly IInjector injector; + readonly IReadOnlyList customParameters; + readonly Func prefabFinder; + ComponentDestination destination; + + public PrefabComponentProvider( + Func prefabFinder, + IInjector injector, + IReadOnlyList customParameters, + in ComponentDestination destination) + { + this.injector = injector; + this.customParameters = customParameters; + this.prefabFinder = prefabFinder; + this.destination = destination; + } + + public object SpawnInstance(IObjectResolver resolver) + { + var prefab = prefabFinder(resolver); + var parent = destination.GetParent(resolver); + + var wasActive = prefab.gameObject.activeSelf; + using (new ObjectResolverUnityExtensions.PrefabDirtyScope(prefab.gameObject)) + { + if (wasActive) + { + prefab.gameObject.SetActive(false); + } + + var component = parent != null + ? UnityEngine.Object.Instantiate(prefab, parent) + : UnityEngine.Object.Instantiate(prefab); + + if (VContainerSettings.Instance != null && VContainerSettings.Instance.RemoveClonePostfix) + component.name = prefab.name; + + try + { + injector.Inject(component, resolver, customParameters); + destination.ApplyDontDestroyOnLoadIfNeeded(component); + } + finally + { + if (wasActive) + { + prefab.gameObject.SetActive(true); + component.gameObject.SetActive(true); + } + } + return component; + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs.meta new file mode 100644 index 00000000..216cf108 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/PrefabComponentProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 51a9e9ed532b41898ab9e5f82ea2781f +timeCreated: 1619932830 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs new file mode 100644 index 00000000..e8103bd4 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs @@ -0,0 +1,81 @@ +#if VCONTAINER_ECS_INTEGRATION +using System; +using System.Collections.Generic; +using Unity.Entities; + +namespace VContainer.Unity +{ + public sealed class SystemInstanceProvider : IInstanceProvider where T : ComponentSystemBase + { + readonly Type systemType; + readonly IInjector injector; + readonly IReadOnlyList customParameters; + readonly string worldName; + readonly Type systemGroupType; + + World world; + T instance; + + public SystemInstanceProvider( + Type systemType, + string worldName, + Type systemGroupType, + IInjector injector, + IReadOnlyList customParameters) + { + this.systemType = systemType; + this.worldName = worldName; + this.systemGroupType = systemGroupType; + this.injector = injector; + this.customParameters = customParameters; + } + + public object SpawnInstance(IObjectResolver resolver) + { + if (world is null) + world = GetWorld(resolver); + + if (instance is null) + { + instance = (T) injector.CreateInstance(resolver, customParameters); +#if UNITY_2022_2_OR_NEWER + world.AddSystemManaged(instance); +#else + world.AddSystem(instance); +#endif + + if (systemGroupType != null) + { +#if UNITY_2022_2_OR_NEWER + var systemGroup = (ComponentSystemGroup)world.GetOrCreateSystemManaged(systemGroupType); +#else + var systemGroup = (ComponentSystemGroup)world.GetOrCreateSystem(systemGroupType); +#endif + systemGroup.AddSystemToUpdateList(instance); + } + + return instance; + } +#if UNITY_2022_2_OR_NEWER + return world.GetExistingSystemManaged(systemType); +#else + return world.GetExistingSystem(systemType); +#endif + } + + World GetWorld(IObjectResolver resolver) + { + if (worldName is null && World.DefaultGameObjectInjectionWorld != null) + return World.DefaultGameObjectInjectionWorld; + + var worlds = resolver.Resolve>(); + foreach (var world in worlds) + { + if (world.Name == worldName) + return world; + } + throw new VContainerException(systemType, $"World `{worldName}` is not registered"); + } + } +} +#endif diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs.meta new file mode 100644 index 00000000..f4fee5c2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/SystemInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: baa43b4b3a9a4d1895436d98f4c539e4 +timeCreated: 1595083942 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs new file mode 100644 index 00000000..cad793f7 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs @@ -0,0 +1,71 @@ +#if VCONTAINER_ECS_INTEGRATION && UNITY_2022_2_OR_NEWER +using System; +using System.Collections.Generic; +using Unity.Entities; + +namespace VContainer.Unity +{ + public sealed class UnmanagedSystemInstanceProvider : IInstanceProvider + { + readonly Type systemType; + readonly IInjector injector; + readonly IReadOnlyList customParameters; + readonly string worldName; + readonly Type systemGroupType; + + private World world; + private UnmanagedSystemReference instance; + + public UnmanagedSystemInstanceProvider( + Type systemType, + string worldName, + Type systemGroupType, + IInjector injector, + IReadOnlyList customParameters) + { + this.systemType = systemType; + this.worldName = worldName; + this.systemGroupType = systemGroupType; + this.injector = injector; + this.customParameters = customParameters; + } + + public object SpawnInstance(IObjectResolver resolver) + { + if (world is null) + world = GetWorld(resolver); + + if (instance is null) + { + SystemHandle handle = world.GetOrCreateSystem(systemType); + injector.Inject(handle, resolver, customParameters); + + if (systemGroupType is not null) + { + var systemGroup = (ComponentSystemGroup) world.GetOrCreateSystemManaged(systemGroupType); + systemGroup.AddSystemToUpdateList(handle); + } + Type refType = typeof(UnmanagedSystemReference<>); + Type target = refType.MakeGenericType(systemType); + instance = (UnmanagedSystemReference)Activator.CreateInstance(target, handle, world); + return instance; + } + return instance; + } + + private World GetWorld(IObjectResolver resolver) + { + if (worldName is null && World.DefaultGameObjectInjectionWorld != null) + return World.DefaultGameObjectInjectionWorld; + + var worlds = resolver.Resolve>(); + foreach (World w in worlds) + { + if (w.Name == worldName) + return w; + } + throw new VContainerException(systemType, $"World `{worldName}` is not Created"); + } + } +} +#endif diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs.meta new file mode 100644 index 00000000..7b53808d --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/UnmanagedSystemInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c3e078788acd49f292b76c0a778b1a45 +timeCreated: 1671719884 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs new file mode 100644 index 00000000..0c2e32ec --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs @@ -0,0 +1,48 @@ +#if VCONTAINER_ECS_INTEGRATION +using System; +using Unity.Entities; +using UnityEngine.LowLevel; + +namespace VContainer.Unity +{ + public sealed class WorldInstanceProvider : IInstanceProvider + { + readonly string name; + readonly Action initialization; + + public WorldInstanceProvider(string name, Action initialization = null) + { + this.name = name; + this.initialization = initialization; + } + + public object SpawnInstance(IObjectResolver resolver) + { + var world = new World(name); + if (initialization != null) + { + initialization(world); + } + else + { +#if UNITY_2022_2_OR_NEWER + world.CreateSystemManaged(); + world.CreateSystemManaged(); + world.CreateSystemManaged(); + + ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(world); + ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world); +#else + world.CreateSystem(); + world.CreateSystem(); + world.CreateSystem(); + + ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(world); + ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(world); +#endif + } + return world; + } + } +} +#endif diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs.meta new file mode 100644 index 00000000..a1cfeadd --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/InstanceProviders/WorldInstanceProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c152c2c135444258b6b6bc730bdb1f64 +timeCreated: 1595059160 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs new file mode 100644 index 00000000..f9f9b509 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using VContainer.Internal; + +namespace VContainer.Unity +{ + public sealed class VContainerParentTypeReferenceNotFound : Exception + { + public readonly Type ParentType; + + public VContainerParentTypeReferenceNotFound(Type parentType, string message) + : base(message) + { + ParentType = parentType; + } + } + + partial class LifetimeScope + { + static readonly List WaitingList = new List(); + +#if UNITY_2019_3_OR_NEWER + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] +#else + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] +#endif + static void SubscribeSceneEvents() + { + SceneManager.sceneLoaded -= OnSceneLoaded; + SceneManager.sceneLoaded += OnSceneLoaded; + } + + static void EnqueueAwake(LifetimeScope lifetimeScope) + { + WaitingList.Add(lifetimeScope); + } + + static void CancelAwake(LifetimeScope lifetimeScope) + { + WaitingList.Remove(lifetimeScope); + } + + static void AwakeWaitingChildren(LifetimeScope awakenParent) + { + if (WaitingList.Count <= 0) return; + + using (ListPool.Get(out var buffer)) + { + for (var i = WaitingList.Count - 1; i >= 0; i--) + { + var waitingScope = WaitingList[i]; + if (waitingScope.parentReference.Type == awakenParent.GetType()) + { + waitingScope.parentReference.Object = awakenParent; + WaitingList.RemoveAt(i); + buffer.Add(waitingScope); + } + } + + foreach (var waitingScope in buffer) + { + waitingScope.Awake(); + } + } + } + + static void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (WaitingList.Count <= 0) + return; + + using (ListPool.Get(out var buffer)) + { + for (var i = WaitingList.Count - 1; i >= 0; i--) + { + var waitingScope = WaitingList[i]; + if (waitingScope.gameObject.scene == scene) + { + WaitingList.RemoveAt(i); + buffer.Add(waitingScope); + } + } + + foreach (var waitingScope in buffer) + { + waitingScope.Awake(); // Re-throw if parent not found + } + } + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta new file mode 100644 index 00000000..ae741b5a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69462a1b64a3d4de2bcbd8707dd57ca1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs new file mode 100644 index 00000000..d05a16b3 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs @@ -0,0 +1,369 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using VContainer.Diagnostics; +using VContainer.Internal; + +namespace VContainer.Unity +{ + [DefaultExecutionOrder(-5000)] + public partial class LifetimeScope : MonoBehaviour, IDisposable + { + public readonly struct ParentOverrideScope : IDisposable + { + public ParentOverrideScope(LifetimeScope nextParent) + { + lock (SyncRoot) + { + GlobalOverrideParents.Push(nextParent); + } + } + + public void Dispose() + { + lock (SyncRoot) + { + GlobalOverrideParents.Pop(); + } + } + } + + public readonly struct ExtraInstallationScope : IDisposable + { + public ExtraInstallationScope(IInstaller installer) + { + lock (SyncRoot) + { + GlobalExtraInstallers.Push(installer); + } + } + + void IDisposable.Dispose() + { + lock (SyncRoot) + { + GlobalExtraInstallers.Pop(); + } + } + } + + [SerializeField] + public ParentReference parentReference; + + [SerializeField] + public bool autoRun = true; + + [SerializeField] + protected List autoInjectGameObjects; + + string scopeName; + + static readonly Stack GlobalOverrideParents = new Stack(); + static readonly Stack GlobalExtraInstallers = new Stack(); + static readonly object SyncRoot = new object(); + + public static LifetimeScope Create(IInstaller installer = null, string name = null) + { + var gameObject = new GameObject(name ?? "LifetimeScope"); + gameObject.SetActive(false); + var newScope = gameObject.AddComponent(); + if (installer != null) + { + newScope.localExtraInstallers.Add(installer); + } + gameObject.SetActive(true); + return newScope; + } + + public static LifetimeScope Create(Action configuration, string name = null) + => Create(new ActionInstaller(configuration), name); + + public static ParentOverrideScope EnqueueParent(LifetimeScope parent) + => new ParentOverrideScope(parent); + + public static ExtraInstallationScope Enqueue(Action installing) + => new ExtraInstallationScope(new ActionInstaller(installing)); + + public static ExtraInstallationScope Enqueue(IInstaller installer) + => new ExtraInstallationScope(installer); + + [Obsolete("LifetimeScope.PushParent is obsolete. Use LifetimeScope.EnqueueParent instead.", false)] + public static ParentOverrideScope PushParent(LifetimeScope parent) => new ParentOverrideScope(parent); + + [Obsolete("LifetimeScope.Push is obsolete. Use LifetimeScope.Enqueue instead.", false)] + public static ExtraInstallationScope Push(Action installing) => Enqueue(installing); + + [Obsolete("LifetimeScope.Push is obsolete. Use LifetimeScope.Enqueue instead.", false)] + public static ExtraInstallationScope Push(IInstaller installer) => Enqueue(installer); + + public static LifetimeScope Find(Scene scene) where T : LifetimeScope => Find(typeof(T), scene); + public static LifetimeScope Find() where T : LifetimeScope => Find(typeof(T)); + + static LifetimeScope Find(Type type, Scene scene) + { + using (ListPool.Get(out var buffer)) + { + scene.GetRootGameObjects(buffer); + foreach (var gameObject in buffer) + { + var found = gameObject.GetComponentInChildren(type) as LifetimeScope; + if (found != null) + return found; + } + } + return null; + } + + static LifetimeScope Find(Type type) + { +#if UNITY_2022_1_OR_NEWER + return (LifetimeScope)FindAnyObjectByType(type); +#else + return (LifetimeScope)FindObjectOfType(type); +#endif + } + + public IObjectResolver Container { get; private set; } + public LifetimeScope Parent { get; private set; } + + public bool IsRoot => VContainerSettings.Instance != null && + VContainerSettings.Instance.IsRootLifetimeScopeInstance(this); + + readonly List localExtraInstallers = new List(); + + protected virtual void Awake() + { + if (VContainerSettings.DiagnosticsEnabled && string.IsNullOrEmpty(scopeName)) + { + scopeName = $"{name} ({gameObject.GetInstanceID()})"; + } + try + { + if (autoRun) + { + Build(); + } + } + catch (VContainerParentTypeReferenceNotFound) when(!IsRoot) + { + if (WaitingList.Contains(this)) + { + throw; + } + EnqueueAwake(this); + } + } + + protected virtual void OnDestroy() + { + DisposeCore(); + } + + protected virtual void Configure(IContainerBuilder builder) { } + + public void Dispose() + { + DisposeCore(); + if (this != null) + { + Destroy(gameObject); + } + } + + public void DisposeCore() + { + Container?.Dispose(); + Container = null; + CancelAwake(this); + if (VContainerSettings.DiagnosticsEnabled) + { + DiagnositcsContext.RemoveCollector(scopeName); + } + } + + public void Build() + { + if (Parent == null) + Parent = GetRuntimeParent(); + + if (Parent != null) + { + if (VContainerSettings.Instance != null && Parent.IsRoot) + { + if (Parent.Container == null) + Parent.Build(); + } + + // ReSharper disable once PossibleNullReferenceException + Parent.Container.CreateScope(builder => + { + builder.RegisterBuildCallback(SetContainer); + builder.ApplicationOrigin = this; + builder.Diagnostics = VContainerSettings.DiagnosticsEnabled ? DiagnositcsContext.GetCollector(scopeName) : null; + InstallTo(builder); + }); + } + else + { + var builder = new ContainerBuilder + { + ApplicationOrigin = this, + Diagnostics = VContainerSettings.DiagnosticsEnabled ? DiagnositcsContext.GetCollector(scopeName) : null, + }; + builder.RegisterBuildCallback(SetContainer); + InstallTo(builder); + builder.Build(); + } + + AwakeWaitingChildren(this); + } + + void SetContainer(IObjectResolver container) + { + Container = container; + AutoInjectAll(); + } + + public TScope CreateChild(IInstaller installer = null, string childScopeName = null) + where TScope : LifetimeScope + { + var childGameObject = new GameObject(childScopeName ?? "LifetimeScope (Child)"); + childGameObject.SetActive(false); + childGameObject.transform.SetParent(transform, false); + + var child = childGameObject.AddComponent(); + if (installer != null) + { + child.localExtraInstallers.Add(installer); + } + child.parentReference.Object = this; + childGameObject.SetActive(true); + return child; + } + + public LifetimeScope CreateChild(IInstaller installer = null, string childScopeName = null) + => CreateChild(installer, childScopeName); + + public TScope CreateChild(Action installation, string childScopeName = null) + where TScope : LifetimeScope + => CreateChild(new ActionInstaller(installation), childScopeName); + + public LifetimeScope CreateChild(Action installation, string childScopeName = null) + => CreateChild(new ActionInstaller(installation), childScopeName); + + public TScope CreateChildFromPrefab(TScope prefab, IInstaller installer = null) + where TScope : LifetimeScope + { + var wasActive = prefab.gameObject.activeSelf; + using (new ObjectResolverUnityExtensions.PrefabDirtyScope(prefab.gameObject)) + { + if (wasActive) + { + prefab.gameObject.SetActive(false); + } + var child = Instantiate(prefab, transform, false); + if (installer != null) + { + child.localExtraInstallers.Add(installer); + } + child.parentReference.Object = this; + if (wasActive) + { + prefab.gameObject.SetActive(true); + child.gameObject.SetActive(true); + } + return child; + } + } + + public TScope CreateChildFromPrefab(TScope prefab, Action installation) + where TScope : LifetimeScope + => CreateChildFromPrefab(prefab, new ActionInstaller(installation)); + + void InstallTo(IContainerBuilder builder) + { + Configure(builder); + + foreach (var installer in localExtraInstallers) + { + installer.Install(builder); + } + localExtraInstallers.Clear(); + + lock (SyncRoot) + { + foreach (var installer in GlobalExtraInstallers) + { + installer.Install(builder); + } + } + + builder.RegisterInstance(this).AsSelf(); + EntryPointsBuilder.EnsureDispatcherRegistered(builder); + } + + protected virtual LifetimeScope FindParent() => null; + + LifetimeScope GetRuntimeParent() + { + if (IsRoot) return null; + + if (parentReference.Object != null) + return parentReference.Object; + + // Find via implementation + var implParent = FindParent(); + if (implParent != null) + { + if (parentReference.Type != null && parentReference.Type != implParent.GetType()) { + UnityEngine.Debug.LogWarning($"FindParent returned {implParent.GetType()} but parent reference type is {parentReference.Type}. This may be unintentional."); + } + return implParent; + } + + // Find in scene via type + if (parentReference.Type != null && parentReference.Type != GetType()) + { + var found = Find(parentReference.Type); + if (found != null && found.Container != null) + { + return found; + } + throw new VContainerParentTypeReferenceNotFound( + parentReference.Type, + $"{name} could not found parent reference of type : {parentReference.Type}"); + } + + lock (SyncRoot) + { + if (GlobalOverrideParents.Count > 0) + { + return GlobalOverrideParents.Peek(); + } + } + + // Find root from settings + if (VContainerSettings.Instance != null) + { + return VContainerSettings.Instance.GetOrCreateRootLifetimeScopeInstance(); + } + + return null; + } + + void AutoInjectAll() + { + if (autoInjectGameObjects == null) + return; + + foreach (var target in autoInjectGameObjects) + { + if (target != null) // Check missing reference + { + Container.InjectGameObject(target); + } + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs.meta new file mode 100644 index 00000000..fff49a7a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/LifetimeScope.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b2c62ac3bcd44fb9e09835581422057 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs new file mode 100644 index 00000000..821c0f5b --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs @@ -0,0 +1,292 @@ +using System; +using UnityEngine; +using VContainer.Internal; + +namespace VContainer.Unity +{ + public static class ObjectResolverUnityExtensions + { + public readonly struct PrefabDirtyScope : IDisposable + { + readonly GameObject _prefab; + readonly bool _madeDirty; + + public PrefabDirtyScope(GameObject prefab) + { + _prefab = prefab; + +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + _madeDirty = prefab.activeSelf && !UnityEditor.EditorUtility.IsDirty(_prefab); +#else + _madeDirty = false; +#endif + } + + public void Dispose() + { +#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER + if (_madeDirty) + { + UnityEditor.EditorUtility.ClearDirty(_prefab); + } +#endif + } + } + + public static void InjectGameObject(this IObjectResolver resolver, GameObject gameObject) + { + void InjectGameObjectRecursive(GameObject current) + { + if (current == null) return; + + using (ListPool.Get(out var buffer)) + { + buffer.Clear(); + current.GetComponents(buffer); + foreach (var monoBehaviour in buffer) + { + if (monoBehaviour != null) + { // Can be null if the MonoBehaviour's type wasn't found (e.g. if it was stripped) + resolver.Inject(monoBehaviour); + } + } + } + + var transform = current.transform; + for (var i = 0; i < transform.childCount; i++) + { + var child = transform.GetChild(i); + InjectGameObjectRecursive(child.gameObject); + } + } + + InjectGameObjectRecursive(gameObject); + } + + public static T Instantiate(this IObjectResolver resolver, T prefab) + where T : Component + { + return resolver.Instantiate(prefab, prefab.transform.position, prefab.transform.rotation); + } + + public static T Instantiate(this IObjectResolver resolver, T prefab, Transform parent, bool worldPositionStays = false) + where T : Component + { + var wasActive = prefab.gameObject.activeSelf; + using (new PrefabDirtyScope(prefab.gameObject)) + { + prefab.gameObject.SetActive(false); + + var instance = UnityEngine.Object.Instantiate(prefab, parent, worldPositionStays); + + SetName(instance, prefab); + + try + { + resolver.InjectGameObject(instance.gameObject); + } + finally + { + prefab.gameObject.SetActive(wasActive); + instance.gameObject.SetActive(wasActive); + } + + return instance; + } + } + + public static T Instantiate( + this IObjectResolver resolver, + T prefab, + Vector3 position, + Quaternion rotation) + where T : Component + { + if (resolver.ApplicationOrigin is LifetimeScope scope) + { + return scope.Instantiate(prefab, position, rotation); + } + + return resolver.Instantiate(prefab, position, rotation, null); + } + + public static T Instantiate( + this IObjectResolver resolver, + T prefab, + Vector3 position, + Quaternion rotation, + Transform parent) + where T : Component + { + var wasActive = prefab.gameObject.activeSelf; + using (new PrefabDirtyScope(prefab.gameObject)) + { + prefab.gameObject.SetActive(false); + + var instance = UnityEngine.Object.Instantiate(prefab, position, rotation, parent); + + SetName(instance, prefab); + + try + { + resolver.InjectGameObject(instance.gameObject); + } + finally + { + prefab.gameObject.SetActive(wasActive); + instance.gameObject.SetActive(wasActive); + } + return instance; + } + } + + static T Instantiate(this LifetimeScope scope, T prefab, Vector3 position, Quaternion rotation) + where T : Component + { + var wasActive = prefab.gameObject.activeSelf; + using (new PrefabDirtyScope(prefab.gameObject)) + { + prefab.gameObject.SetActive(false); + + T instance; + if (scope.IsRoot) + { + instance = UnityEngine.Object.Instantiate(prefab, position, rotation); + UnityEngine.Object.DontDestroyOnLoad(instance); + } + else + { + // Into the same scene as LifetimeScope + instance = UnityEngine.Object.Instantiate(prefab, position, rotation, scope.transform); + instance.transform.SetParent(null); + } + + SetName(instance, prefab); + + try + { + scope.Container.InjectGameObject(instance.gameObject); + } + finally + { + prefab.gameObject.SetActive(wasActive); + instance.gameObject.SetActive(wasActive); + } + return instance; + } + } + + static GameObject Instantiate(this LifetimeScope scope, GameObject prefab, Vector3 position, Quaternion rotation) + { + var wasActive = prefab.activeSelf; + using (new PrefabDirtyScope(prefab)) + { + prefab.SetActive(false); + + GameObject instance; + if (scope.IsRoot) + { + instance = UnityEngine.Object.Instantiate(prefab, position, rotation); + UnityEngine.Object.DontDestroyOnLoad(instance); + } + else + { + // Into the same scene as LifetimeScope + instance = UnityEngine.Object.Instantiate(prefab, position, rotation, scope.transform); + instance.transform.SetParent(null); + } + + SetName(instance, prefab); + + try + { + scope.Container.InjectGameObject(instance); + } + finally + { + prefab.SetActive(wasActive); + instance.SetActive(wasActive); + } + return instance; + } + } + + public static GameObject Instantiate(this IObjectResolver resolver, GameObject prefab) + { + return resolver.Instantiate(prefab, prefab.transform.position, prefab.transform.rotation); + } + + public static GameObject Instantiate(this IObjectResolver resolver, GameObject prefab, Transform parent, bool worldPositionStays = false) + { + var wasActive = prefab.activeSelf; + using (new PrefabDirtyScope(prefab)) + { + prefab.SetActive(false); + + GameObject instance = null; + try + { + instance = UnityEngine.Object.Instantiate(prefab, parent, worldPositionStays); + SetName(instance, prefab); + resolver.InjectGameObject(instance); + } + finally + { + prefab.SetActive(wasActive); + instance?.SetActive(wasActive); + } + return instance; + } + } + + public static GameObject Instantiate( + this IObjectResolver resolver, + GameObject prefab, + Vector3 position, + Quaternion rotation) + { + if (resolver.ApplicationOrigin is LifetimeScope scope) + { + return scope.Instantiate(prefab, position, rotation); + } + + return resolver.Instantiate(prefab, position, rotation, null); + } + + public static GameObject Instantiate( + this IObjectResolver resolver, + GameObject prefab, + Vector3 position, + Quaternion rotation, + Transform parent) + { + var wasActive = prefab.activeSelf; + using (new PrefabDirtyScope(prefab)) + { + prefab.SetActive(false); + + var instance = UnityEngine.Object.Instantiate(prefab, position, rotation, parent); + + SetName(instance, prefab); + + try + { + resolver.InjectGameObject(instance); + } + finally + { + prefab.SetActive(wasActive); + instance.SetActive(wasActive); + } + + return instance; + } + } + + static void SetName(UnityEngine.Object instance, UnityEngine.Object prefab) + { + if (VContainerSettings.Instance != null && VContainerSettings.Instance.RemoveClonePostfix) + instance.name = prefab.name; + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs.meta new file mode 100644 index 00000000..280cbcda --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ObjectResolverUnityExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a8a82741a10a46aa94d4ce08e372dc34 +timeCreated: 1606404902 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs new file mode 100644 index 00000000..5b123004 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs @@ -0,0 +1,47 @@ +using System; +using UnityEngine; + +namespace VContainer.Unity +{ + [Serializable] + public struct ParentReference : ISerializationCallbackReceiver + { + [SerializeField] + public string TypeName; + + [NonSerialized] + public LifetimeScope Object; + + public Type Type { get; private set; } + + ParentReference(Type type) + { + Type = type; + TypeName = type.FullName; + Object = null; + } + + void ISerializationCallbackReceiver.OnBeforeSerialize() + { + TypeName = Type?.FullName; + } + + void ISerializationCallbackReceiver.OnAfterDeserialize() + { + if (!string.IsNullOrEmpty(TypeName)) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + Type = assembly.GetType(TypeName); + if (Type != null) + break; + } + } + } + + public static ParentReference Create() where T : LifetimeScope + { + return new ParentReference(typeof(T)); + } + } +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs.meta new file mode 100644 index 00000000..b66ef13a --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/ParentReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 99eaede4c7734beba2b3abd51cd79b1b +timeCreated: 1594621389 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs new file mode 100644 index 00000000..0125b1f7 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs @@ -0,0 +1,212 @@ +using System; +using System.Threading; +#if UNITY_2019_3_OR_NEWER +using UnityEngine.LowLevel; +using UnityEngine.PlayerLoop; +#else +using UnityEngine.Experimental.LowLevel; +using UnityEngine.Experimental.PlayerLoop; +#endif + +namespace VContainer.Unity +{ + public struct VContainerInitialization {} + public struct VContainerPostInitialization {} + public struct VContainerStartup {} + public struct VContainerPostStartup {} + public struct VContainerFixedUpdate {} + public struct VContainerPostFixedUpdate {} + public struct VContainerUpdate {} + public struct VContainerPostUpdate {} + public struct VContainerLateUpdate {} + public struct VContainerPostLateUpdate {} + + enum PlayerLoopTiming + { + Initialization = 0, + PostInitialization = 1, + + Startup = 2, + PostStartup = 3, + + FixedUpdate = 4, + PostFixedUpdate = 5, + + Update = 6, + PostUpdate = 7, + + LateUpdate = 8, + PostLateUpdate = 9, + } + + static class PlayerLoopHelper + { + static readonly PlayerLoopRunner[] Runners = new PlayerLoopRunner[10]; + static long initialized; + + // [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + public static void EnsureInitialized() + { + if (Interlocked.CompareExchange(ref initialized, 1, 0) != 0) + return; + + for (var i = 0; i < Runners.Length; i++) + { + Runners[i] = new PlayerLoopRunner(); + } + + var playerLoop = +#if UNITY_2019_3_OR_NEWER + PlayerLoop.GetCurrentPlayerLoop(); +#else + PlayerLoop.GetDefaultPlayerLoop(); +#endif + + var copyList = playerLoop.subSystemList; + + ref var initializeSystem = ref FindSubSystem(typeof(Initialization), copyList); + InsertSubsystem( + ref initializeSystem, + null, + new PlayerLoopSystem + { + type = typeof(VContainerInitialization), + updateDelegate = Runners[(int)PlayerLoopTiming.Initialization].Run + }, + new PlayerLoopSystem + { + type = typeof(VContainerPostInitialization), + updateDelegate = Runners[(int)PlayerLoopTiming.PostInitialization].Run + }); + + + ref var earlyUpdateSystem = ref FindSubSystem(typeof(EarlyUpdate), copyList); + InsertSubsystem( + ref earlyUpdateSystem, + typeof(EarlyUpdate.ScriptRunDelayedStartupFrame), + new PlayerLoopSystem + { + type = typeof(VContainerStartup), + updateDelegate = Runners[(int)PlayerLoopTiming.Startup].Run + }, + new PlayerLoopSystem + { + type = typeof(VContainerPostStartup), + updateDelegate = Runners[(int)PlayerLoopTiming.PostStartup].Run + }); + + ref var fixedUpdateSystem = ref FindSubSystem(typeof(FixedUpdate), copyList); + InsertSubsystem( + ref fixedUpdateSystem, + typeof(FixedUpdate.ScriptRunBehaviourFixedUpdate), + new PlayerLoopSystem + { + type = typeof(VContainerFixedUpdate), + updateDelegate = Runners[(int)PlayerLoopTiming.FixedUpdate].Run + }, + new PlayerLoopSystem + { + type = typeof(VContainerPostFixedUpdate), + updateDelegate = Runners[(int)PlayerLoopTiming.PostFixedUpdate].Run + }); + + ref var updateSystem = ref FindSubSystem(typeof(Update), copyList); + InsertSubsystem( + ref updateSystem, + typeof(Update.ScriptRunBehaviourUpdate), + new PlayerLoopSystem + { + type = typeof(VContainerUpdate), + updateDelegate = Runners[(int)PlayerLoopTiming.Update].Run + }, + new PlayerLoopSystem + { + type = typeof(VContainerPostUpdate), + updateDelegate = Runners[(int)PlayerLoopTiming.PostUpdate].Run + }); + + ref var lateUpdateSystem = ref FindSubSystem(typeof(PreLateUpdate), copyList); + InsertSubsystem( + ref lateUpdateSystem, + typeof(PreLateUpdate.ScriptRunBehaviourLateUpdate), + new PlayerLoopSystem + { + type = typeof(VContainerLateUpdate), + updateDelegate = Runners[(int)PlayerLoopTiming.LateUpdate].Run + }, + new PlayerLoopSystem + { + type = typeof(VContainerPostLateUpdate), + updateDelegate = Runners[(int)PlayerLoopTiming.PostLateUpdate].Run + }); + + playerLoop.subSystemList = copyList; + PlayerLoop.SetPlayerLoop(playerLoop); + } + + public static void Dispatch(PlayerLoopTiming timing, IPlayerLoopItem item) + { + EnsureInitialized(); + Runners[(int)timing].Dispatch(item); + } + + static ref PlayerLoopSystem FindSubSystem(Type targetType, PlayerLoopSystem[] systems) + { + for (var i = 0; i < systems.Length; i++) + { + if (systems[i].type == targetType) + return ref systems[i]; + } + throw new InvalidOperationException($"{targetType.FullName} not in systems"); + } + + static void InsertSubsystem( + ref PlayerLoopSystem parentSystem, + Type beforeType, + PlayerLoopSystem newSystem, + PlayerLoopSystem newPostSystem) + { + var source = parentSystem.subSystemList; + var insertIndex = -1; + if (beforeType == null) + { + insertIndex = 0; + } + for (var i = 0; i < source.Length; i++) + { + if (source[i].type == beforeType) + { + insertIndex = i; + } + } + + if (insertIndex < 0) + { + throw new ArgumentException($"{beforeType.FullName} not in system {parentSystem} {parentSystem.type.FullName}"); + } + + var dest = new PlayerLoopSystem[source.Length + 2]; + for (var i = 0; i < dest.Length; i++) + { + if (i == insertIndex) + { + dest[i] = newSystem; + } + else if (i == dest.Length - 1) + { + dest[i] = newPostSystem; + } + else if (i < insertIndex) + { + dest[i] = source[i]; + } + else + { + dest[i] = source[i - 1]; + } + } + + parentSystem.subSystemList = dest; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs.meta new file mode 100644 index 00000000..1d3d6c83 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: efc97ba6489541959a28b1356de70b97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs new file mode 100644 index 00000000..c8527e28 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs @@ -0,0 +1,349 @@ +using System; +using System.Collections.Generic; +using System.Threading; +#if VCONTAINER_UNITASK_INTEGRATION +using Cysharp.Threading.Tasks; +#endif + +namespace VContainer.Unity +{ + sealed class StartableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IEnumerable entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public StartableLoopItem( + IEnumerable entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + foreach (var x in entries) + { + try + { + x.Start(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return false; + } + + public void Dispose() => disposed = true; + } + + sealed class PostStartableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IEnumerable entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public PostStartableLoopItem( + IEnumerable entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + foreach (var x in entries) + { + try + { + x.PostStart(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return false; + } + + public void Dispose() => disposed = true; + } + + sealed class FixedTickableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IReadOnlyList entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public FixedTickableLoopItem( + IReadOnlyList entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + for (var i = 0; i < entries.Count; i++) + { + try + { + entries[i].FixedTick(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return !disposed; + } + + public void Dispose() => disposed = true; + } + + sealed class PostFixedTickableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IReadOnlyList entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public PostFixedTickableLoopItem( + IReadOnlyList entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + for (var i = 0; i < entries.Count; i++) + { + try + { + entries[i].PostFixedTick(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return !disposed; + } + + public void Dispose() => disposed = true; + } + + sealed class TickableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IReadOnlyList entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public TickableLoopItem( + IReadOnlyList entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + for (var i = 0; i < entries.Count; i++) + { + try + { + entries[i].Tick(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return !disposed; + } + + public void Dispose() => disposed = true; + } + + sealed class PostTickableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IReadOnlyList entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public PostTickableLoopItem( + IReadOnlyList entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + for (var i = 0; i < entries.Count; i++) + { + try + { + entries[i].PostTick(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return !disposed; + } + + public void Dispose() => disposed = true; + } + + sealed class LateTickableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IReadOnlyList entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public LateTickableLoopItem( + IReadOnlyList entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + for (var i = 0; i < entries.Count; i++) + { + try + { + entries[i].LateTick(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return !disposed; + } + + public void Dispose() => disposed = true; + } + + sealed class PostLateTickableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IReadOnlyList entries; + readonly EntryPointExceptionHandler exceptionHandler; + bool disposed; + + public PostLateTickableLoopItem( + IReadOnlyList entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + for (var i = 0; i < entries.Count; i++) + { + try + { + entries[i].PostLateTick(); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } + } + return !disposed; + } + + public void Dispose() => disposed = true; + } + +#if VCONTAINER_UNITASK_INTEGRATION || UNITY_2021_3_OR_NEWER + sealed class AsyncStartableLoopItem : IPlayerLoopItem, IDisposable + { + readonly IEnumerable entries; + readonly EntryPointExceptionHandler exceptionHandler; + readonly CancellationTokenSource cts = new CancellationTokenSource(); + bool disposed; + + public AsyncStartableLoopItem( + IEnumerable entries, + EntryPointExceptionHandler exceptionHandler) + { + this.entries = entries; + this.exceptionHandler = exceptionHandler; + } + + public bool MoveNext() + { + if (disposed) return false; + foreach (var x in entries) + { +#if VCONTAINER_UNITASK_INTEGRATION + var task = x.StartAsync(cts.Token); + if (exceptionHandler != null) + { + task.Forget(ex => exceptionHandler.Publish(ex)); + } + else + { + task.Forget(); + } +#else + try + { + var task = x.StartAsync(cts.Token); + _ = task.Forget(exceptionHandler); + } + catch (Exception ex) + { + if (exceptionHandler == null) throw; + exceptionHandler.Publish(ex); + } +#endif + } + return false; + } + + public void Dispose() + { + lock (entries) + { + if (disposed) return; + disposed = true; + } + cts.Cancel(); + cts.Dispose(); + } + } +#endif +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs.meta new file mode 100644 index 00000000..be73b835 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8278b0aa962c47c59217aa3127037554 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs new file mode 100644 index 00000000..da4837dd --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs @@ -0,0 +1,43 @@ +using VContainer.Internal; + +namespace VContainer.Unity +{ + interface IPlayerLoopItem + { + bool MoveNext(); + } + + sealed class PlayerLoopRunner + { + readonly FreeList runners = new FreeList(16); + + int running; + + public void Dispatch(IPlayerLoopItem item) + { + runners.Add(item); + } + + public void Run() + { + var span = +#if NETSTANDARD2_1 + runners.AsSpan(); +#else + runners; +#endif + for (var i = 0; i < span.Length; i++) + { + var item = span[i]; + if (item != null) + { + var continued = item.MoveNext(); + if (!continued) + { + runners.RemoveAt(i); + } + } + } + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs.meta new file mode 100644 index 00000000..9676b787 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/PlayerLoopRunner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15c8b175a3194a3cb81a63541b402fe5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs new file mode 100644 index 00000000..4ab53765 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs @@ -0,0 +1,43 @@ +#if VCONTAINER_ECS_INTEGRATION +using System; +using System.Collections.Generic; +using Unity.Entities; +using VContainer.Internal; + +namespace VContainer.Unity { + public sealed class SystemRegistrationBuilder : RegistrationBuilder { + readonly string worldName; + Type systemGroupType; + + internal SystemRegistrationBuilder(Type implementationType, string worldName) + : base(implementationType, default) { + this.worldName = worldName; + InterfaceTypes = new List { + typeof(ComponentSystemBase), + implementationType + }; + } + + public override Registration Build() { + var injector = InjectorCache.GetOrBuild(ImplementationType); + + var parameters = new object[] { + ImplementationType, + worldName, + systemGroupType, + injector, + Parameters + }; + + Type type = typeof(SystemInstanceProvider<>).MakeGenericType(ImplementationType); + var provider = (IInstanceProvider)Activator.CreateInstance(type, parameters); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider, Key); + } + + public SystemRegistrationBuilder IntoGroup() where T : ComponentSystemGroup { + systemGroupType = typeof(T); + return this; + } + } +} +#endif \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs.meta new file mode 100644 index 00000000..192e4811 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/SystemRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4040dd1c878f4e029f835a9f41a8df2d +timeCreated: 1595004027 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs new file mode 100644 index 00000000..f3ac1948 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs @@ -0,0 +1,70 @@ +#if VCONTAINER_ECS_INTEGRATION && UNITY_2022_2_OR_NEWER +using System; +using Unity.Entities; + +namespace VContainer.Unity +{ + public class UnmanagedSystemReference : IDisposable + { + private World world; + + public SystemHandle Id { get; private set; } + + public Type SystemType { get; private set; } + + public UnmanagedSystemReference(Type systemType, SystemHandle id, World world) + { + this.world = world; + SystemType = systemType; + Id = id; + } + + public ref T GetSystem() where T : unmanaged, ISystem + { + if (typeof(T) != SystemType) + { + throw new ArgumentException($"System type mismatch. Expected: {SystemType}, Actual: {typeof(T)}"); + } + + if (!world.Unmanaged.IsSystemValid(Id)) + { + throw new InvalidOperationException($"System is not valid. SystemType: {SystemType}"); + } + + return ref world.Unmanaged.GetUnsafeSystemRef(Id); + } + + public ref T GetSystemFromDefaultWorld() where T : unmanaged, ISystem + { + return ref World.DefaultGameObjectInjectionWorld.Unmanaged.GetUnsafeSystemRef(Id); + } + + public void Dispose() + { + if (world.Unmanaged.IsSystemValid(Id)) + { + world.DestroySystem(Id); + } + else if(world is null && World.DefaultGameObjectInjectionWorld.Unmanaged.IsSystemValid(Id)) + { + World.DefaultGameObjectInjectionWorld.DestroySystem(Id); + } + } + } + + public class UnmanagedSystemReference : UnmanagedSystemReference where T : unmanaged, ISystem + { + public UnmanagedSystemReference(SystemHandle id, World world) : base(typeof(T), id, world) { } + + public ref T GetSystem(WorldUnmanaged world) + { + return ref world.GetUnsafeSystemRef(Id); + } + + public ref T GetSystemFromDefaultWorld() + { + return ref World.DefaultGameObjectInjectionWorld.Unmanaged.GetUnsafeSystemRef(Id); + } + } +} +#endif diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs.meta new file mode 100644 index 00000000..b6dec5d1 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 44d895de5bf04a3586323e6d798df1df +timeCreated: 1674376930 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs new file mode 100644 index 00000000..9b48137e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs @@ -0,0 +1,47 @@ +#if VCONTAINER_ECS_INTEGRATION && UNITY_2022_2_OR_NEWER +using System; +using System.Collections.Generic; +using Unity.Entities; +using VContainer.Internal; + +namespace VContainer.Unity +{ + public sealed class UnmanagedSystemRegistrationBuilder : RegistrationBuilder + { + readonly string worldName; + Type systemGroupType; + + internal UnmanagedSystemRegistrationBuilder(Type implementationType, string worldName) + : base(implementationType, default) + { + this.worldName = worldName; + Type refType = typeof(UnmanagedSystemReference<>); + Type[] typeArgs = { implementationType }; + Type refImplType = refType.MakeGenericType(typeArgs); + InterfaceTypes = new List + { + typeof(UnmanagedSystemReference), + refImplType + }; + } + + public override Registration Build() + { + IInjector injector = InjectorCache.GetOrBuild(ImplementationType); + var provider = new UnmanagedSystemInstanceProvider( + ImplementationType, + worldName, + systemGroupType, + injector, + Parameters); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider, Key); + } + + public UnmanagedSystemRegistrationBuilder IntoGroup() where T : ComponentSystemGroup + { + systemGroupType = typeof(T); + return this; + } + } +} +#endif diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs.meta new file mode 100644 index 00000000..93eb2a2d --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/UnmanagedSystemRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a773d39e6f5c40ffa1c54b017f69de04 +timeCreated: 1671719145 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs new file mode 100644 index 00000000..6c11353e --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs @@ -0,0 +1,131 @@ +using System.Linq; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace VContainer.Unity +{ + public sealed class VContainerSettings : ScriptableObject + { + public static VContainerSettings Instance { get; private set; } + public static bool DiagnosticsEnabled => Instance != null && Instance.EnableDiagnostics; + + static LifetimeScope rootLifetimeScopeInstance; + + [SerializeField] + [Tooltip("Set the Prefab to be the parent of the entire Project.")] + public LifetimeScope RootLifetimeScope; + + [SerializeField] + [Tooltip("Enables the collection of information that can be viewed in the VContainerDiagnosticsWindow. Note: Performance degradation")] + public bool EnableDiagnostics; + + [SerializeField] + [Tooltip("Disables script modification for LifetimeScope scripts.")] + public bool DisableScriptModifier; + + [SerializeField] + [Tooltip("Removes (Clone) postfix in IObjectResolver.Instantiate() and IContainerBuilder.RegisterComponentInNewPrefab().")] + public bool RemoveClonePostfix; + +#if UNITY_EDITOR + [UnityEditor.MenuItem("Assets/Create/VContainer/VContainer Settings")] + public static void CreateAsset() + { + var path = UnityEditor.EditorUtility.SaveFilePanelInProject( + "Save VContainerSettings", + "VContainerSettings", + "asset", + string.Empty); + + if (string.IsNullOrEmpty(path)) + return; + + var newSettings = CreateInstance(); + UnityEditor.AssetDatabase.CreateAsset(newSettings, path); + + var preloadedAssets = UnityEditor.PlayerSettings.GetPreloadedAssets().ToList(); + preloadedAssets.RemoveAll(x => x is VContainerSettings); + preloadedAssets.Add(newSettings); + UnityEditor.PlayerSettings.SetPreloadedAssets(preloadedAssets.ToArray()); + } + + public static void LoadInstanceFromPreloadAssets() + { + var preloadAsset = UnityEditor.PlayerSettings.GetPreloadedAssets().FirstOrDefault(x => x is VContainerSettings); + if (preloadAsset is VContainerSettings instance) + { + instance.OnDisable(); + instance.OnEnable(); + } + } + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void RuntimeInitialize() + { + // For editor, we need to load the Preload asset manually. + LoadInstanceFromPreloadAssets(); + } +#endif + + public LifetimeScope GetOrCreateRootLifetimeScopeInstance() + { + if (RootLifetimeScope != null && rootLifetimeScopeInstance == null) + { + var activeBefore = RootLifetimeScope.gameObject.activeSelf; + RootLifetimeScope.gameObject.SetActive(false); + + rootLifetimeScopeInstance = Instantiate(RootLifetimeScope); + SetName(rootLifetimeScopeInstance, RootLifetimeScope); + DontDestroyOnLoad(rootLifetimeScopeInstance); + rootLifetimeScopeInstance.gameObject.SetActive(true); + + RootLifetimeScope.gameObject.SetActive(activeBefore); + } + return rootLifetimeScopeInstance; + } + + public bool IsRootLifetimeScopeInstance(LifetimeScope lifetimeScope) => + RootLifetimeScope == lifetimeScope || rootLifetimeScopeInstance == lifetimeScope; + + void OnEnable() + { + if (Application.isPlaying) + { + Instance = this; + + var activeScene = SceneManager.GetActiveScene(); + if (activeScene.isLoaded) + { + OnFirstSceneLoaded(activeScene, default); + } + else + { + SceneManager.sceneLoaded -= OnFirstSceneLoaded; + SceneManager.sceneLoaded += OnFirstSceneLoaded; + } + } + } + + void OnDisable() + { + Instance = null; + } + + void OnFirstSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (RootLifetimeScope != null && + RootLifetimeScope.autoRun && + (rootLifetimeScopeInstance == null || rootLifetimeScopeInstance.Container == null)) + { + GetOrCreateRootLifetimeScopeInstance(); + } + SceneManager.sceneLoaded -= OnFirstSceneLoaded; + } + + static void SetName(Object instance, Object prefab) + { + if (Instance != null && Instance.RemoveClonePostfix) + instance.name = prefab.name; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs.meta new file mode 100644 index 00000000..e3c02889 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/VContainerSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9cf4d8df1d704d5689f3c69bd70d1cb9 +timeCreated: 1595759041 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs new file mode 100644 index 00000000..41ded05b --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs @@ -0,0 +1,48 @@ +#if VCONTAINER_ECS_INTEGRATION +using System; +using System.Collections.Generic; +using Unity.Entities; + +namespace VContainer.Unity +{ + public sealed class WorldConfigurationHelper : IDisposable + { + public readonly World World; + + public WorldConfigurationHelper(IEnumerable worlds, string targetWorldName) + { + foreach (var world in worlds) + { + if (world.Name == targetWorldName) + { + World = world; + break; + } + } + + if (World is null) + throw new VContainerException(typeof(WorldConfigurationHelper), $"World {targetWorldName} is not registered"); + } + + public void SortSystems() + { + foreach (var system in World.Systems) + { + if (system is ComponentSystemGroup group) + group.SortSystems(); + } + } + + public void Dispose() + { + foreach (var system in World.Systems) + { + if (system is IDisposable disposableSystem) + { + disposableSystem.Dispose(); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs.meta new file mode 100644 index 00000000..2af6d307 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldConfigurationHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e747717446364b59ae6fc331aa779b27 +timeCreated: 1595137806 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs new file mode 100644 index 00000000..37f2f23f --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs @@ -0,0 +1,26 @@ +#if VCONTAINER_ECS_INTEGRATION +using System; +using Unity.Entities; + +namespace VContainer.Unity +{ + public sealed class WorldRegistrationBuilder : RegistrationBuilder + { + readonly string name; + readonly Action initialization; + + public WorldRegistrationBuilder(string name, Lifetime lifetime, Action initialization) + : base(typeof(World), lifetime) + { + this.name = name; + this.initialization = initialization; + } + + public override Registration Build() + { + var provider = new WorldInstanceProvider(name, initialization); + return new Registration(typeof(World), Lifetime, null, provider, Key); + } + } +} +#endif \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs.meta new file mode 100644 index 00000000..70927267 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/Unity/WorldRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b731692c99e34ff984d1e308b27415ff +timeCreated: 1595060757 \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef b/Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef new file mode 100644 index 00000000..8bd5dcc4 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef @@ -0,0 +1,29 @@ +{ + "name": "VContainer", + "rootNamespace": "VContainer", + "references": [ + "Unity.Collections", + "Unity.Entities", + "UniTask" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.entities", + "expression": "", + "define": "VCONTAINER_ECS_INTEGRATION" + }, + { + "name": "com.cysharp.unitask", + "expression": "", + "define": "VCONTAINER_UNITASK_INTEGRATION" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef.meta b/Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef.meta new file mode 100644 index 00000000..656b2461 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/VContainer.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b0214a6008ed146ff8f122a6a9c2f6cc +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs b/Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs new file mode 100644 index 00000000..42285af6 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs @@ -0,0 +1,14 @@ +using System; + +namespace VContainer +{ + public sealed class VContainerException : Exception + { + public readonly Type InvalidType; + + public VContainerException(Type invalidType, string message) : base(message) + { + InvalidType = invalidType; + } + } +} diff --git a/Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs.meta b/Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs.meta new file mode 100644 index 00000000..68e42da2 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/Runtime/VContainerException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b5e8798acfde432c832f44cdb44b377 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/jp.hadashikick.vcontainer/package.json b/Packages/jp.hadashikick.vcontainer/package.json new file mode 100644 index 00000000..38706dd6 --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/package.json @@ -0,0 +1,17 @@ +{ + "name": "jp.hadashikick.vcontainer", + "displayName": "VContainer", + "version": "1.17.0", + "unity": "2018.4", + "description": "The extra fast DI (Dependency Injection) for Unity", + "keywords": [ + "DI", + "Dependency Injection", + "DI Container" + ], + "license": "MIT", + "documentationUrl": "https://vcontainer.hadashikick.jp/", + "changelogUrl": "https://github.com/hadashiA/VContainer/releases", + "licensesUrl": "https://github.com/hadashiA/VContainer/blob/master/LICENSE", + "_fingerprint": "8c70d0b94b301d746b1efe68b98cc3475bab177c" +} diff --git a/Packages/jp.hadashikick.vcontainer/package.json.meta b/Packages/jp.hadashikick.vcontainer/package.json.meta new file mode 100644 index 00000000..be96742f --- /dev/null +++ b/Packages/jp.hadashikick.vcontainer/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0ec4861db50774d2c92fb7b73afa846f +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json index efa56557..98469b3c 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -17,6 +17,7 @@ "com.unity.timeline": "1.8.10", "com.unity.ugui": "2.0.0", "com.unity.visualscripting": "1.9.9", + "jp.hadashikick.vcontainer": "https://github.com/hadashiA/VContainer.git?path=VContainer/Assets/VContainer#1.17.0", "com.unity.modules.accessibility": "1.0.0", "com.unity.modules.adaptiveperformance": "1.0.0", "com.unity.modules.ai": "1.0.0", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 5b3da38d..7fae4ec3 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -204,6 +204,12 @@ }, "url": "https://packages.unity.com" }, + "jp.hadashikick.vcontainer": { + "version": "file:jp.hadashikick.vcontainer", + "depth": 0, + "source": "embedded", + "dependencies": {} + }, "com.unity.modules.accessibility": { "version": "1.0.0", "depth": 0,