Improve container error reporting with containerId

Merge-request: KT-MR-8965
Merged-by: Vladimir Dolzhenko <Vladimir.Dolzhenko@jetbrains.com>
This commit is contained in:
Vladimir Dolzhenko
2023-03-22 16:34:22 +00:00
committed by Space Team
parent eb80ba9cf7
commit 8528519d96
4 changed files with 31 additions and 20 deletions
@@ -25,6 +25,8 @@ import java.lang.reflect.WildcardType
class ContainerConsistencyException(message: String) : Exception(message) class ContainerConsistencyException(message: String) : Exception(message)
interface ComponentContainer { interface ComponentContainer {
val containerId: String
fun createResolveContext(requestingDescriptor: ValueDescriptor): ValueResolveContext fun createResolveContext(requestingDescriptor: ValueDescriptor): ValueResolveContext
} }
@@ -108,7 +110,7 @@ class StorageComponentContainer(
} }
override fun <T> create(request: Class<T>): T { override fun <T> create(request: Class<T>): T {
val constructorBinding = request.bindToConstructor(unknownContext) val constructorBinding = request.bindToConstructor(containerId, unknownContext)
val args = constructorBinding.argumentDescriptors.map { it.getValue() }.toTypedArray() val args = constructorBinding.argumentDescriptors.map { it.getValue() }.toTypedArray()
return runWithUnwrappingInvocationException { return runWithUnwrappingInvocationException {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -116,7 +118,10 @@ class StorageComponentContainer(
} }
} }
override fun toString() = "Container $id" override val containerId
get() = "Container: $id"
override fun toString() = containerId
} }
fun StorageComponentContainer.registerSingleton(klass: Class<*>): StorageComponentContainer { fun StorageComponentContainer.registerSingleton(klass: Class<*>): StorageComponentContainer {
@@ -35,7 +35,8 @@ class ComponentResolveContext(
val requestingDescriptor: ValueDescriptor, val requestingDescriptor: ValueDescriptor,
val parentContext: ValueResolveContext? = null val parentContext: ValueResolveContext? = null
) : ValueResolveContext { ) : ValueResolveContext {
override fun resolve(registration: Type): ValueDescriptor? = container.resolve(registration, this) ?: parentContext?.resolve(registration) override fun resolve(registration: Type): ValueDescriptor? =
container.resolve(registration, this) ?: parentContext?.resolve(registration)
override fun toString(): String = "for $requestingDescriptor in $container" override fun toString(): String = "for $requestingDescriptor in $container"
} }
@@ -51,17 +52,21 @@ class MethodBinding(val method: Method, private val argumentDescriptors: List<Va
fun computeArguments(argumentDescriptors: List<ValueDescriptor>): List<Any> = argumentDescriptors.map { it.getValue() } fun computeArguments(argumentDescriptors: List<ValueDescriptor>): List<Any> = argumentDescriptors.map { it.getValue() }
fun Class<*>.bindToConstructor(context: ValueResolveContext): ConstructorBinding { fun Class<*>.bindToConstructor(containerId: String, context: ValueResolveContext): ConstructorBinding {
val constructorInfo = getInfo().constructorInfo ?: error("No constructor for $this: ${getInfo()}") val constructorInfo = getInfo().constructorInfo ?: error("No constructor for $this: ${getInfo()} in $containerId")
val candidate = constructorInfo.constructor val candidate = constructorInfo.constructor
return ConstructorBinding(candidate, candidate.bindArguments(constructorInfo.parameters, context)) return ConstructorBinding(candidate, candidate.bindArguments(containerId, constructorInfo.parameters, context))
} }
fun Method.bindToMethod(context: ValueResolveContext): MethodBinding { fun Method.bindToMethod(containerId: String, context: ValueResolveContext): MethodBinding {
return MethodBinding(this, bindArguments(genericParameterTypes.toList(), context)) return MethodBinding(this, bindArguments(containerId, genericParameterTypes.toList(), context))
} }
private fun Member.bindArguments(parameters: List<Type>, context: ValueResolveContext): List<ValueDescriptor> { private fun Member.bindArguments(
containerId: String,
parameters: List<Type>,
context: ValueResolveContext
): List<ValueDescriptor> {
val bound = ArrayList<ValueDescriptor>(parameters.size) val bound = ArrayList<ValueDescriptor>(parameters.size)
var unsatisfied: MutableList<Type>? = null var unsatisfied: MutableList<Type>? = null
@@ -71,13 +76,12 @@ private fun Member.bindArguments(parameters: List<Type>, context: ValueResolveCo
if (unsatisfied == null) if (unsatisfied == null)
unsatisfied = ArrayList<Type>() unsatisfied = ArrayList<Type>()
unsatisfied.add(parameter) unsatisfied.add(parameter)
} } else {
else {
bound.add(descriptor) bound.add(descriptor)
} }
} }
if (unsatisfied != null) { if (unsatisfied != null) {
throw UnresolvedDependenciesException("Dependencies for `$this` cannot be satisfied:\n $unsatisfied") throw UnresolvedDependenciesException("$containerId: Dependencies for `$this` cannot be satisfied:\n $unsatisfied")
} }
return bound return bound
} }
@@ -121,7 +121,7 @@ open class SingletonTypeComponentDescriptor(container: ComponentContainer, val k
override fun getRegistrations(): Iterable<Type> = klass.getInfo().registrations override fun getRegistrations(): Iterable<Type> = klass.getInfo().registrations
private fun createInstanceOf(klass: Class<*>, context: ValueResolveContext): Any { private fun createInstanceOf(klass: Class<*>, context: ValueResolveContext): Any {
val binding = klass.bindToConstructor(context) val binding = klass.bindToConstructor(container.containerId, context)
state = ComponentState.Initializing state = ComponentState.Initializing
for (argumentDescriptor in binding.argumentDescriptors) { for (argumentDescriptor in binding.argumentDescriptors) {
if (argumentDescriptor is Closeable && argumentDescriptor !is SingletonDescriptor) { if (argumentDescriptor is Closeable && argumentDescriptor !is SingletonDescriptor) {
@@ -69,7 +69,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
return nonDefault.singleOrNull() return nonDefault.singleOrNull()
?: throw InvalidCardinalityException( ?: throw InvalidCardinalityException(
"Request $request cannot be satisfied because there is more than one type registered\n" + "$containerId: Request $request cannot be satisfied because there is more than one type registered\n" +
"Clashed registrations: ${entry.joinToString()}" "Clashed registrations: ${entry.joinToString()}"
) )
} }
@@ -86,7 +86,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
} }
fun dump(printer: PrintStream): Unit = with(printer) { fun dump(printer: PrintStream): Unit = with(printer) {
val heading = "Container: $myId" val heading = containerId
println(heading) println(heading)
println("=".repeat(heading.length)) println("=".repeat(heading.length))
println() println()
@@ -107,6 +107,8 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
} }
} }
val containerId
get() = "Container: $myId"
fun resolveMultiple(request: Type, context: ValueResolveContext): Iterable<ValueDescriptor> { fun resolveMultiple(request: Type, context: ValueResolveContext): Iterable<ValueDescriptor> {
registerDependency(request, context) registerDependency(request, context)
@@ -132,7 +134,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
fun compose(context: ComponentResolveContext) { fun compose(context: ComponentResolveContext) {
if (state != ComponentStorageState.Initial) if (state != ComponentStorageState.Initial)
throw ContainerConsistencyException("Container $myId was already composed.") throw ContainerConsistencyException("$containerId $myId was already composed.")
state = ComponentStorageState.Initialized state = ComponentStorageState.Initialized
composeDescriptors(context, descriptors) composeDescriptors(context, descriptors)
@@ -216,7 +218,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
val classInfo = instance::class.java.getInfo() val classInfo = instance::class.java.getInfo()
classInfo.setterInfos.forEach { (method) -> classInfo.setterInfos.forEach { (method) ->
val methodBinding = method.bindToMethod(context) val methodBinding = method.bindToMethod(containerId, context)
methodBinding.invoke(instance) methodBinding.invoke(instance)
} }
} }