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)
interface ComponentContainer {
val containerId: String
fun createResolveContext(requestingDescriptor: ValueDescriptor): ValueResolveContext
}
@@ -108,7 +110,7 @@ class StorageComponentContainer(
}
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()
return runWithUnwrappingInvocationException {
@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 {
@@ -31,11 +31,12 @@ interface ValueResolveContext {
}
class ComponentResolveContext(
val container: StorageComponentContainer,
val requestingDescriptor: ValueDescriptor,
val parentContext: ValueResolveContext? = null
val container: StorageComponentContainer,
val requestingDescriptor: ValueDescriptor,
val parentContext: ValueResolveContext? = null
) : 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"
}
@@ -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 Class<*>.bindToConstructor(context: ValueResolveContext): ConstructorBinding {
val constructorInfo = getInfo().constructorInfo ?: error("No constructor for $this: ${getInfo()}")
fun Class<*>.bindToConstructor(containerId: String, context: ValueResolveContext): ConstructorBinding {
val constructorInfo = getInfo().constructorInfo ?: error("No constructor for $this: ${getInfo()} in $containerId")
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 {
return MethodBinding(this, bindArguments(genericParameterTypes.toList(), context))
fun Method.bindToMethod(containerId: String, context: ValueResolveContext): MethodBinding {
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)
var unsatisfied: MutableList<Type>? = null
@@ -71,13 +76,12 @@ private fun Member.bindArguments(parameters: List<Type>, context: ValueResolveCo
if (unsatisfied == null)
unsatisfied = ArrayList<Type>()
unsatisfied.add(parameter)
}
else {
} else {
bound.add(descriptor)
}
}
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
}
@@ -121,7 +121,7 @@ open class SingletonTypeComponentDescriptor(container: ComponentContainer, val k
override fun getRegistrations(): Iterable<Type> = klass.getInfo().registrations
private fun createInstanceOf(klass: Class<*>, context: ValueResolveContext): Any {
val binding = klass.bindToConstructor(context)
val binding = klass.bindToConstructor(container.containerId, context)
state = ComponentState.Initializing
for (argumentDescriptor in binding.argumentDescriptors) {
if (argumentDescriptor is Closeable && argumentDescriptor !is SingletonDescriptor) {
@@ -69,7 +69,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
return nonDefault.singleOrNull()
?: 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()}"
)
}
@@ -86,7 +86,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
}
fun dump(printer: PrintStream): Unit = with(printer) {
val heading = "Container: $myId"
val heading = containerId
println(heading)
println("=".repeat(heading.length))
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> {
registerDependency(request, context)
@@ -132,7 +134,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
fun compose(context: ComponentResolveContext) {
if (state != ComponentStorageState.Initial)
throw ContainerConsistencyException("Container $myId was already composed.")
throw ContainerConsistencyException("$containerId $myId was already composed.")
state = ComponentStorageState.Initialized
composeDescriptors(context, descriptors)
@@ -216,7 +218,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
val classInfo = instance::class.java.getInfo()
classInfo.setterInfos.forEach { (method) ->
val methodBinding = method.bindToMethod(context)
val methodBinding = method.bindToMethod(containerId, context)
methodBinding.invoke(instance)
}
}