@@ -16,8 +16,7 @@ import org.apache.commons.lang3.reflect.TypeUtils
1616import org.reactivestreams.Publisher
1717import org.slf4j.LoggerFactory
1818import java.lang.reflect.*
19- import kotlin.reflect.full.valueParameters
20- import kotlin.reflect.jvm.javaType
19+ import kotlin.reflect.full.extensionReceiverParameter
2120import kotlin.reflect.jvm.kotlinFunction
2221
2322/* *
@@ -29,6 +28,8 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
2928
3029 private val allowedLastArgumentTypes = listOfNotNull(DataFetchingEnvironment ::class .java, GraphQLContext ::class .java, options.contextClass)
3130
31+ private val methodsByNameCache = mutableMapOf<Pair <Class <out Any >, Boolean > , Map <String , List <Method >>>()
32+
3233 fun findFieldResolver (field : FieldDefinition , resolverInfo : ResolverInfo ): FieldResolver {
3334 val searches = resolverInfo.getFieldSearches()
3435
@@ -86,29 +87,34 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
8687 }
8788
8889 private fun findResolverMethod (field : FieldDefinition , search : Search ): Method ? {
89- val methods = getAllMethods (search)
90+ val methodsByName = getMethodsByName (search)
9091 val argumentCount = field.inputValueDefinitions.size + if (search.requiredFirstParameterType != null ) 1 else 0
9192 val name = field.name
93+ val capitalizedName = name.replaceFirstChar(Char ::titlecase)
9294
9395 // Check for the following one by one:
9496 // 1. Method with exact field name
9597 // 2. Method that returns a boolean with "is" style getter
9698 // 3. Method with "get" style getter
9799 // 4. Method with "getField" style getter
98100 // 5. Method with "get" style getter with the field name converted from snake_case to camelCased. ex: key_ops -> getKeyOps()
99- return methods.find {
100- it.name == name && verifyMethodArguments(it, argumentCount, search)
101- } ? : methods.find {
102- (isBoolean(field.type) && it.name == " is${name.replaceFirstChar(Char ::titlecase)} " ) && verifyMethodArguments(it, argumentCount, search)
103- } ? : methods.find {
104- it.name == " get${name.replaceFirstChar(Char ::titlecase)} " && verifyMethodArguments(it, argumentCount, search)
105- } ? : methods.find {
106- it.name == " getField${name.replaceFirstChar(Char ::titlecase)} " && verifyMethodArguments(it, argumentCount, search)
107- } ? : methods.find {
108- it.name == " get${name.snakeToCamelCase()} " && verifyMethodArguments(it, argumentCount, search)
101+ return listOfNotNull(
102+ name,
103+ if (isBoolean(field.type)) " is$capitalizedName " else null ,
104+ " get$capitalizedName " ,
105+ " getField$capitalizedName " ,
106+ " get${name.snakeToCamelCase()} "
107+ ).firstNotNullOfOrNull { methodName ->
108+ methodsByName[methodName]?.find { verifyMethodArguments(it, argumentCount, search) }
109109 }
110110 }
111111
112+ // root fields are searched on every root resolver, so each class's methods are only indexed once
113+ private fun getMethodsByName (search : Search ): Map <String , List <Method >> =
114+ methodsByNameCache.getOrPut(search.type.unwrap() to search.isSubscription) {
115+ getAllMethods(search).groupBy { it.name }
116+ }
117+
112118 private fun getAllMethods (search : Search ): List <Method > {
113119 val type = search.type.unwrap()
114120 val declaredMethods = type.declaredNonProxyMethods
@@ -160,31 +166,23 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
160166 it.eraseUnboundedWildcards() == search.requiredFirstParameterType || method.declaringClass.typeParameters.contains(it)
161167 } ? : false
162168 } else {
163- true
169+ // an extension receiver can only take the source object
170+ ! isExtensionFunction(method)
164171 }
165172
166- val methodParameterCount = getMethodParameterCount( method)
167- val methodLastParameter = getMethodLastParameter( method)
173+ val methodParameterCount = method.parameterCountWithoutContinuation( )
174+ val methodLastParameter = method.parameterTypes.getOrNull(methodParameterCount - 1 )
168175
169176 val correctParameterCount = methodParameterCount == requiredCount ||
170177 (methodParameterCount == (requiredCount + 1 ) && allowedLastArgumentTypes.contains(methodLastParameter))
171178 return correctParameterCount && appropriateFirstParameter
172179 }
173180
174- private fun getMethodParameterCount (method : Method ): Int {
175- return try {
176- method.kotlinFunction?.valueParameters?.size ? : method.parameterCount
177- } catch (e: InternalError ) {
178- method.parameterCount
179- }
180- }
181-
182- private fun getMethodLastParameter (method : Method ): Type ? {
181+ private fun isExtensionFunction (method : Method ): Boolean {
183182 return try {
184- method.kotlinFunction?.valueParameters?.lastOrNull()?.type?.javaType
185- ? : method.parameterTypes.lastOrNull()
183+ method.kotlinFunction?.extensionReceiverParameter != null
186184 } catch (e: InternalError ) {
187- method.parameterTypes.lastOrNull()
185+ false
188186 }
189187 }
190188
0 commit comments