1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.model.text 18 19 import com.android.tools.metalava.model.AnnotationItem 20 import com.android.tools.metalava.model.ArrayTypeItem 21 import com.android.tools.metalava.model.BaseTypeVisitor 22 import com.android.tools.metalava.model.ClassTypeItem 23 import com.android.tools.metalava.model.Codebase 24 import com.android.tools.metalava.model.DefaultAnnotationItem 25 import com.android.tools.metalava.model.JAVA_LANG_OBJECT 26 import com.android.tools.metalava.model.PrimitiveTypeItem 27 import com.android.tools.metalava.model.ReferenceTypeItem 28 import com.android.tools.metalava.model.TypeArgumentTypeItem 29 import com.android.tools.metalava.model.TypeItem 30 import com.android.tools.metalava.model.TypeModifiers 31 import com.android.tools.metalava.model.TypeNullability 32 import com.android.tools.metalava.model.TypeParameterScope 33 import com.android.tools.metalava.model.TypeVisitor 34 import com.android.tools.metalava.model.VariableTypeItem 35 import com.android.tools.metalava.model.WildcardTypeItem 36 import com.android.tools.metalava.model.type.ContextNullability 37 import com.android.tools.metalava.model.type.DefaultArrayTypeItem 38 import com.android.tools.metalava.model.type.DefaultClassTypeItem 39 import com.android.tools.metalava.model.type.DefaultPrimitiveTypeItem 40 import com.android.tools.metalava.model.type.DefaultTypeModifiers 41 import com.android.tools.metalava.model.type.DefaultVariableTypeItem 42 import com.android.tools.metalava.model.type.DefaultWildcardTypeItem 43 import kotlin.collections.HashMap 44 45 /** Parses and caches types for a [codebase]. */ 46 internal class TextTypeParser(val codebase: Codebase, val kotlinStyleNulls: Boolean = false) { 47 48 /** 49 * The cache key, incorporates some information from [ContextNullability] and [kotlinStyleNulls] 50 * as well as the type string as they can all affect the created [TypeItem]. 51 * 52 * e.g. [ContextNullability.forceNonNull] will cause the type to always be 53 * [TypeNullability.NONNULL] even if [kotlinStyleNulls] is `false` which would normally cause it 54 * to be [TypeNullability.PLATFORM]. However, when [kotlinStyleNulls] is `true` then there is no 55 * difference between [ContextNullability.forceNonNull] and [ContextNullability.none] as they 56 * will both cause a class type with no nullability suffix to be treated as 57 * [TypeNullability.NONNULL]. 58 * 59 * That information is encapsulated in the [forceClassToBeNonNull] property. 60 */ 61 private data class Key(val forceClassToBeNonNull: Boolean, val type: String) 62 63 /** The cache from [Key] to [CacheEntry]. */ 64 private val typeCache = HashMap<Key, CacheEntry>() 65 66 internal var requests = 0 67 internal var cacheSkip = 0 68 internal var cacheHit = 0 69 internal var cacheSize = 0 70 71 /** A [TypeItem] representing `java.lang.Object`, suitable for general use. */ 72 private val objectType: ReferenceTypeItem 73 get() = cachedParseType(JAVA_LANG_OBJECT, TypeParameterScope.empty) as ReferenceTypeItem 74 75 /** 76 * Creates or retrieves from the cache a [TypeItem] representing [type], in the context of the 77 * type parameters from [typeParameterScope], if applicable. 78 */ 79 fun obtainTypeFromString( 80 type: String, 81 typeParameterScope: TypeParameterScope, 82 contextNullability: ContextNullability = ContextNullability.none, 83 ): TypeItem = cachedParseType(type, typeParameterScope, emptyList(), contextNullability) 84 85 /** 86 * Creates or retrieves from the cache a [TypeItem] representing [type], in the context of the 87 * type parameters from [typeParameterScope], if applicable. 88 * 89 * Used internally, as it has an extra [annotations] parameter that allows the annotations on 90 * array components to be correctly associated with the correct component. They are optional 91 * leading type-use annotations that have already been removed from the arrays type string. 92 */ 93 private fun cachedParseType( 94 type: String, 95 typeParameterScope: TypeParameterScope, 96 annotations: List<AnnotationItem> = emptyList(), 97 contextNullability: ContextNullability = ContextNullability.none, 98 ): TypeItem { 99 requests++ 100 101 // Class types used as super types, i.e. in an extends or implements list are forced to be 102 // [TypeNullability.NONNULL], just as they would be if kotlinStyleNulls was true. Use the 103 // same cache key for both so that they reuse cached types where possible. 104 val forceClassToBeNonNull = 105 contextNullability.forcedNullability == TypeNullability.NONNULL || kotlinStyleNulls 106 107 // Don't use the cache when there are type-use annotations not contained in the string. 108 return if (annotations.isEmpty()) { 109 val key = Key(forceClassToBeNonNull, type) 110 111 // Get the cache entry for the supplied type and forceClassToBeNonNull. 112 val result = 113 typeCache.computeIfAbsent(key) { CacheEntry(it.type, it.forceClassToBeNonNull) } 114 115 // Get the appropriate [TypeItem], creating one if necessary. 116 result.getTypeItem(typeParameterScope) 117 } else { 118 cacheSkip++ 119 parseType(type, typeParameterScope, annotations, forceClassToBeNonNull) 120 } 121 } 122 123 /** Converts the [type] to a [TypeItem] in the context of the [typeParameterScope]. */ 124 private fun parseType( 125 type: String, 126 typeParameterScope: TypeParameterScope, 127 annotations: List<AnnotationItem>, 128 // Forces a [ClassTypeItem] to have [TypeNullability.NONNULL] 129 forceClassToBeNonNull: Boolean = false, 130 ): TypeItem { 131 val (unannotated, annotationsFromString) = trimLeadingAnnotations(type) 132 val allAnnotations = annotations + annotationsFromString 133 val (withoutNullability, nullability) = 134 splitNullabilitySuffix( 135 unannotated, 136 // If forceClassToBeNonNull is true then a plain class type without any nullability 137 // suffix must be treated as if it was not null, which is just how it would be 138 // treated when kotlinStyleNulls is true. So, pretend that kotlinStyleNulls is true. 139 kotlinStyleNulls || forceClassToBeNonNull 140 ) 141 val trimmed = withoutNullability.trim() 142 143 // Figure out what kind of type this is. 144 // 145 // Start with variable as the type parameter scope allows us to determine whether something 146 // is a type parameter or not. Also, if a type parameter has the same name as a primitive 147 // type (possible in Kotlin, but not Java) then it will be treated as a type parameter not a 148 // primitive. 149 // 150 // Then try parsing as a primitive as while Kotlin classes can shadow primitive types 151 // they would need to be fully qualified. 152 return asVariable(trimmed, typeParameterScope, allAnnotations, nullability) 153 ?: asPrimitive(type, trimmed, allAnnotations, nullability) 154 // Try parsing as a wildcard before trying to parse as an array. 155 // `? extends java.lang.String[]` should be parsed as a wildcard with an array bound, 156 // not as an array of wildcards, for consistency with how this would be compiled. 157 ?: asWildcard(trimmed, typeParameterScope, allAnnotations, nullability) 158 // Try parsing as an array. 159 ?: asArray(trimmed, allAnnotations, nullability, typeParameterScope) 160 // If it isn't anything else, parse the type as a class. 161 ?: asClass(trimmed, typeParameterScope, allAnnotations, nullability) 162 } 163 164 /** 165 * Try parsing [type] as a primitive. This will return a non-null [PrimitiveTypeItem] if [type] 166 * exactly matches a primitive name. 167 * 168 * [type] should have annotations and nullability markers stripped, with [original] as the 169 * complete annotated type. Once annotations are properly handled (b/300081840), preserving 170 * [original] won't be necessary. 171 */ 172 private fun asPrimitive( 173 original: String, 174 type: String, 175 annotations: List<AnnotationItem>, 176 nullability: TypeNullability? 177 ): PrimitiveTypeItem? { 178 val kind = 179 when (type) { 180 "byte" -> PrimitiveTypeItem.Primitive.BYTE 181 "char" -> PrimitiveTypeItem.Primitive.CHAR 182 "double" -> PrimitiveTypeItem.Primitive.DOUBLE 183 "float" -> PrimitiveTypeItem.Primitive.FLOAT 184 "int" -> PrimitiveTypeItem.Primitive.INT 185 "long" -> PrimitiveTypeItem.Primitive.LONG 186 "short" -> PrimitiveTypeItem.Primitive.SHORT 187 "boolean" -> PrimitiveTypeItem.Primitive.BOOLEAN 188 "void" -> PrimitiveTypeItem.Primitive.VOID 189 else -> return null 190 } 191 if (nullability != null && nullability != TypeNullability.NONNULL) { 192 throw ApiParseException("Invalid nullability suffix on primitive: $original") 193 } 194 return DefaultPrimitiveTypeItem(modifiers(annotations, TypeNullability.NONNULL), kind) 195 } 196 197 /** 198 * Try parsing [type] as an array. This will return a non-null [ArrayTypeItem] if [type] ends 199 * with `[]` or `...`. 200 * 201 * The context [typeParameterScope] are used to parse the component type of the array. 202 */ 203 private fun asArray( 204 type: String, 205 componentAnnotations: List<AnnotationItem>, 206 nullability: TypeNullability?, 207 typeParameterScope: TypeParameterScope 208 ): ArrayTypeItem? { 209 // Check if this is a regular array or varargs. 210 val (inner, varargs) = 211 if (type.endsWith("...")) { 212 Pair(type.dropLast(3), true) 213 } else if (type.endsWith("[]")) { 214 Pair(type.dropLast(2), false) 215 } else { 216 return null 217 } 218 219 // Create lists of the annotations and nullability markers for each dimension of the array. 220 // These are in separate lists because annotations appear in the type string in order from 221 // outermost array annotations to innermost array annotations (for `T @A [] @B [] @ C[]`, 222 // `@A` applies to the three-dimensional array, `@B` applies to the inner two-dimensional 223 // arrays, and `@C` applies to the inner one-dimensional arrays), while nullability markers 224 // appear in order from the innermost array nullability to the outermost array nullability 225 // (for `T[]![]?[]`, the three-dimensional array has no nullability marker, the inner 226 // two-dimensional arrays have `?` as the nullability marker, and the innermost arrays have 227 // `!` as a nullability marker. 228 val allAnnotations = mutableListOf<List<AnnotationItem>>() 229 // The nullability marker for the outer array is already known, include it in the list. 230 val allNullability = mutableListOf(nullability) 231 232 // Remove annotations from the end of the string, add them to the list. 233 var annotationsResult = trimTrailingAnnotations(inner) 234 var componentString = annotationsResult.first 235 allAnnotations.add(annotationsResult.second) 236 237 // Remove nullability marker from the component type, but don't add it to the list yet, as 238 // it might not be an array. 239 var nullabilityResult = splitNullabilitySuffix(componentString, kotlinStyleNulls) 240 componentString = nullabilityResult.first 241 var componentNullability = nullabilityResult.second 242 243 // Work through all layers of arrays to get to the inner component type. 244 // Inner arrays can't be varargs. 245 while (componentString.endsWith("[]")) { 246 // The component is an array, add the nullability to the list. 247 allNullability.add(componentNullability) 248 249 // Remove annotations from the end of the string, add them to the list. 250 annotationsResult = trimTrailingAnnotations(componentString.removeSuffix("[]")) 251 componentString = annotationsResult.first 252 allAnnotations.add(annotationsResult.second) 253 254 // Remove nullability marker from the new component type, but don't add it to the list 255 // yet, as the next component type might not be an array. 256 nullabilityResult = splitNullabilitySuffix(componentString, kotlinStyleNulls) 257 componentString = nullabilityResult.first 258 componentNullability = nullabilityResult.second 259 } 260 261 // Re-add the component's nullability suffix when parsing the component type, and include 262 // the leading annotations already removed from the type string. 263 componentString += componentNullability?.suffix.orEmpty() 264 val deepComponentType = 265 cachedParseType(componentString, typeParameterScope, componentAnnotations) 266 267 // Join the annotations and nullability markers -- as described in the comment above, these 268 // appear in the string in reverse order of each other. The modifiers list will be ordered 269 // from innermost array modifiers to outermost array modifiers. 270 val allModifiers = 271 allAnnotations.zip(allNullability.reversed()).map { (annotations, nullability) -> 272 modifiers(annotations, nullability) 273 } 274 // The final modifiers are in the list apply to the outermost array. 275 val componentModifiers = allModifiers.dropLast(1) 276 val arrayModifiers = allModifiers.last() 277 // Create the component type of the outermost array by building up the inner component type. 278 val componentType = 279 componentModifiers.fold(deepComponentType) { component, modifiers -> 280 DefaultArrayTypeItem(modifiers, component, false) 281 } 282 283 // Create the outer array. 284 return DefaultArrayTypeItem(arrayModifiers, componentType, varargs) 285 } 286 287 /** 288 * Try parsing [type] as a wildcard. This will return a non-null [WildcardTypeItem] if [type] 289 * begins with `?`. 290 * 291 * The context [typeParameterScope] are needed to parse the bounds of the wildcard. 292 * 293 * [type] should have annotations and nullability markers stripped. 294 */ 295 private fun asWildcard( 296 type: String, 297 typeParameterScope: TypeParameterScope, 298 annotations: List<AnnotationItem>, 299 nullability: TypeNullability? 300 ): WildcardTypeItem? { 301 // See if this is a wildcard 302 if (!type.startsWith("?")) return null 303 304 // Unbounded wildcard type: there is an implicit Object extends bound 305 if (type == "?") 306 return DefaultWildcardTypeItem( 307 modifiers(annotations, TypeNullability.UNDEFINED), 308 objectType, 309 null, 310 ) 311 312 // If there's a bound, the nullability suffix applies there instead. 313 val bound = type.substring(2) + nullability?.suffix.orEmpty() 314 return if (bound.startsWith("extends")) { 315 val extendsBound = bound.substring(8) 316 DefaultWildcardTypeItem( 317 modifiers(annotations, TypeNullability.UNDEFINED), 318 getWildcardBound(extendsBound, typeParameterScope), 319 null, 320 ) 321 } else if (bound.startsWith("super")) { 322 val superBound = bound.substring(6) 323 DefaultWildcardTypeItem( 324 modifiers(annotations, TypeNullability.UNDEFINED), 325 // All wildcards have an implicit Object extends bound 326 objectType, 327 getWildcardBound(superBound, typeParameterScope), 328 ) 329 } else { 330 throw ApiParseException( 331 "Type starts with \"?\" but doesn't appear to be wildcard: $type" 332 ) 333 } 334 } 335 336 private fun getWildcardBound(bound: String, typeParameterScope: TypeParameterScope) = 337 cachedParseType(bound, typeParameterScope) as ReferenceTypeItem 338 339 /** 340 * Try parsing [type] as a type variable. This will return a non-null [VariableTypeItem] if 341 * [type] matches a parameter from [typeParameterScope]. 342 * 343 * [type] should have annotations and nullability markers stripped. 344 */ 345 private fun asVariable( 346 type: String, 347 typeParameterScope: TypeParameterScope, 348 annotations: List<AnnotationItem>, 349 nullability: TypeNullability? 350 ): VariableTypeItem? { 351 val param = typeParameterScope.findTypeParameter(type) ?: return null 352 return DefaultVariableTypeItem(modifiers(annotations, nullability), param) 353 } 354 355 /** 356 * Parse the [type] as a class. This function will always return a non-null [ClassTypeItem], so 357 * it should only be used when it is certain that [type] is not a different kind of type. 358 * 359 * The context [typeParameterScope] are used to parse the parameters of the class type. 360 * 361 * [type] should have annotations and nullability markers stripped. 362 */ 363 private fun asClass( 364 type: String, 365 typeParameterScope: TypeParameterScope, 366 annotations: List<AnnotationItem>, 367 nullability: TypeNullability? 368 ): ClassTypeItem { 369 return createClassType(type, null, typeParameterScope, annotations, nullability) 370 } 371 372 /** 373 * Creates a class name for the class represented by [type] with optional [outerClassType]. 374 * 375 * For instance, `test.pkg.Outer<P1>` would be the [outerClassType] when parsing `Inner<P2>` 376 * from the [original] type `test.pkg.Outer<P1>.Inner<P2>`. 377 */ 378 private fun createClassType( 379 type: String, 380 outerClassType: ClassTypeItem?, 381 typeParameterScope: TypeParameterScope, 382 annotations: List<AnnotationItem>, 383 nullability: TypeNullability? 384 ): ClassTypeItem { 385 val (name, afterName, classAnnotations) = splitClassType(type) 386 387 val qualifiedName = 388 if (outerClassType != null) { 389 // This is an inner type, add the prefix of the outer name 390 "${outerClassType.qualifiedName}.$name" 391 } else if (!name.contains('.')) { 392 // Reverse the effect of [TypeItem.stripJavaLangPrefix]. 393 "java.lang.$name" 394 } else { 395 name 396 } 397 398 val (argumentStrings, remainder) = typeParameterStringsWithRemainder(afterName) 399 val arguments = 400 argumentStrings.map { cachedParseType(it, typeParameterScope) as TypeArgumentTypeItem } 401 // If this is an outer class type (there's a remainder), call it non-null and don't apply 402 // the leading annotations (they belong to the inner class type). 403 val classModifiers = 404 if (remainder != null) { 405 modifiers(classAnnotations, TypeNullability.NONNULL) 406 } else { 407 modifiers(classAnnotations + annotations, nullability) 408 } 409 val classType = 410 DefaultClassTypeItem(codebase, classModifiers, qualifiedName, arguments, outerClassType) 411 412 if (remainder != null) { 413 if (!remainder.startsWith('.')) { 414 throw ApiParseException( 415 "Could not parse type `$type`. Found unexpected string after type parameters: $remainder" 416 ) 417 } 418 // This is an inner class type, recur with the new outer class 419 return createClassType( 420 remainder.substring(1), 421 classType, 422 typeParameterScope, 423 annotations, 424 nullability 425 ) 426 } 427 428 return classType 429 } 430 431 private fun modifiers( 432 annotations: List<AnnotationItem>, 433 nullability: TypeNullability? 434 ): TypeModifiers { 435 return DefaultTypeModifiers.create( 436 annotations, 437 nullability, 438 "Type modifiers created by the text model are immutable because they are cached", 439 ) 440 } 441 442 /** 443 * Removes all annotations at the beginning of the type, returning the trimmed type and list of 444 * annotations. 445 */ 446 fun trimLeadingAnnotations(type: String): Pair<String, List<AnnotationItem>> { 447 val annotations = mutableListOf<AnnotationItem>() 448 var trimmed = type.trim() 449 while (trimmed.startsWith('@')) { 450 val end = findAnnotationEnd(trimmed, 1) 451 val annotationSource = trimmed.substring(0, end).trim() 452 annotations.add(DefaultAnnotationItem.create(codebase, annotationSource)) 453 trimmed = trimmed.substring(end).trim() 454 } 455 return Pair(trimmed, annotations) 456 } 457 458 /** 459 * Removes all annotations at the end of the [type], returning the trimmed type and list of 460 * annotations. This is for use with arrays where annotations applying to the array type go 461 * after the component type, for instance `String @A []`. The input [type] should **not** 462 * include the array suffix (`[]` or `...`). 463 */ 464 fun trimTrailingAnnotations(type: String): Pair<String, List<AnnotationItem>> { 465 // The simple way to implement this would be to work from the end of the string, finding 466 // `@` and removing annotations from the end. However, it is possible for an annotation 467 // string to contain an `@`, so this is not a safe way to remove the annotations. 468 // Instead, this finds all annotations starting from the beginning of the string, then 469 // works backwards to find which ones are the trailing annotations. 470 val allAnnotationIndices = mutableListOf<Pair<Int, Int>>() 471 var trimmed = type.trim() 472 473 // First find all annotations, saving the first and last index. 474 var currIndex = 0 475 while (currIndex < trimmed.length) { 476 if (trimmed[currIndex] == '@') { 477 val endIndex = findAnnotationEnd(trimmed, currIndex + 1) 478 allAnnotationIndices.add(Pair(currIndex, endIndex)) 479 currIndex = endIndex + 1 480 } else { 481 currIndex++ 482 } 483 } 484 485 val annotations = mutableListOf<AnnotationItem>() 486 // Go through all annotations from the back, seeing if they're at the end of the string. 487 for ((start, end) in allAnnotationIndices.reversed()) { 488 // This annotation isn't at the end, so we've hit the last trailing annotation 489 if (end < trimmed.length) { 490 break 491 } 492 val annotationSource = trimmed.substring(start) 493 annotations.add(DefaultAnnotationItem.create(codebase, annotationSource)) 494 // Cut this annotation off, so now the next one can end at the last index. 495 trimmed = trimmed.substring(0, start).trim() 496 } 497 return Pair(trimmed, annotations.reversed()) 498 } 499 500 /** 501 * Given [type] which represents a class, splits the string into the qualified name of the 502 * class, the remainder of the type string, and a list of type-use annotations. The remainder of 503 * the type string might be the type parameter list, inner class names, or a combination 504 * 505 * For `java.util.@A @B List<java.lang.@C String>`, returns the triple ("java.util.List", 506 * "<java.lang.@C String", listOf("@A", "@B")). 507 * 508 * For `test.pkg.Outer.Inner`, returns the triple ("test.pkg.Outer", ".Inner", emptyList()). 509 * 510 * For `test.pkg.@test.pkg.A Outer<P1>.@test.pkg.B Inner<P2>`, returns the triple 511 * ("test.pkg.Outer", "<P1>.@test.pkg.B Inner<P2>", listOf("@test.pkg.A")). 512 */ 513 fun splitClassType(type: String): Triple<String, String?, List<AnnotationItem>> { 514 // The constructed qualified type name 515 var name = "" 516 // The part of the type which still needs to be parsed 517 var remaining = type.trim() 518 // The annotations of the type, may be set later 519 var annotations = emptyList<AnnotationItem>() 520 521 var dotIndex = remaining.indexOf('.') 522 var paramIndex = remaining.indexOf('<') 523 var annotationIndex = remaining.indexOf('@') 524 525 // Find which of '.', '<', or '@' comes first, if any 526 var minIndex = minIndex(dotIndex, paramIndex, annotationIndex) 527 while (minIndex != null) { 528 when (minIndex) { 529 // '.' is first, the next part is part of the qualified class name. 530 dotIndex -> { 531 val nextNameChunk = remaining.substring(0, dotIndex) 532 name += nextNameChunk 533 remaining = remaining.substring(dotIndex) 534 // Assumes that package names are all lower case and class names will have 535 // an upper class character (the [START_WITH_UPPER] API lint check should 536 // make this a safe assumption). If the name is a class name, we've found 537 // the complete class name, return. 538 if (nextNameChunk.any { it.isUpperCase() }) { 539 return Triple(name, remaining, annotations) 540 } 541 } 542 // '<' is first, the end of the class name has been reached. 543 paramIndex -> { 544 name += remaining.substring(0, paramIndex) 545 remaining = remaining.substring(paramIndex) 546 return Triple(name, remaining, annotations) 547 } 548 // '@' is first, trim all annotations. 549 annotationIndex -> { 550 name += remaining.substring(0, annotationIndex) 551 trimLeadingAnnotations(remaining.substring(annotationIndex)).let { 552 (first, second) -> 553 remaining = first 554 annotations = second 555 } 556 } 557 } 558 // Reset indices -- the string may now start with '.' for the next chunk of the name 559 // but this should find the end of the next chunk. 560 dotIndex = remaining.indexOf('.', 1) 561 paramIndex = remaining.indexOf('<') 562 annotationIndex = remaining.indexOf('@') 563 minIndex = minIndex(dotIndex, paramIndex, annotationIndex) 564 } 565 // End of the name reached with no leftover string. 566 name += remaining 567 return Triple(name, null, annotations) 568 } 569 570 companion object { 571 /** 572 * Splits the Kotlin-style nullability marker off the type string, returning a pair of the 573 * cleaned type string and the nullability suffix. 574 */ 575 fun splitNullabilitySuffix( 576 type: String, 577 kotlinStyleNulls: Boolean 578 ): Pair<String, TypeNullability?> { 579 return if (kotlinStyleNulls) { 580 // Don't interpret the wildcard type `?` as a nullability marker. 581 if (type == "?") { 582 Pair(type, TypeNullability.UNDEFINED) 583 } else if (type.endsWith("?")) { 584 Pair(type.dropLast(1), TypeNullability.NULLABLE) 585 } else if (type.endsWith("!")) { 586 Pair(type.dropLast(1), TypeNullability.PLATFORM) 587 } else { 588 Pair(type, TypeNullability.NONNULL) 589 } 590 } else if (type.length > 1 && type.endsWith("?") || type.endsWith("!")) { 591 throw ApiParseException( 592 "Format does not support Kotlin-style null type syntax: $type" 593 ) 594 } else { 595 Pair(type, null) 596 } 597 } 598 599 /** 600 * Returns the minimum valid list index from the input, or null if there isn't one. -1 is 601 * not a valid index. 602 */ 603 private fun minIndex(vararg index: Int): Int? = index.filter { it != -1 }.minOrNull() 604 605 /** 606 * Given a string and the index in that string which is the start of an annotation (the 607 * character _after_ the `@`), returns the index of the end of the annotation. 608 */ 609 fun findAnnotationEnd(type: String, start: Int): Int { 610 var index = start 611 val length = type.length 612 var balance = 0 613 while (index < length) { 614 val c = type[index] 615 if (c == '(') { 616 balance++ 617 } else if (c == ')') { 618 balance-- 619 if (balance == 0) { 620 return index + 1 621 } 622 } else if (c != '.' && !Character.isJavaIdentifierPart(c) && balance == 0) { 623 break 624 } 625 index++ 626 } 627 return index 628 } 629 630 /** 631 * Breaks a string representing type parameters into a list of the type parameter strings. 632 * 633 * E.g. `"<A, B, C>"` -> `["A", "B", "C"]` and `"<List<A>, B>"` -> `["List<A>", "B"]`. 634 */ 635 fun typeParameterStrings(typeString: String?): List<String> { 636 return typeParameterStringsWithRemainder(typeString).first 637 } 638 639 /** 640 * Breaks a string representing type parameters into a list of the type parameter strings, 641 * and also returns the remainder of the string after the closing ">". 642 * 643 * E.g. `"<A, B, C>.Inner"` -> `Pair(["A", "B", "C"], ".Inner")` 644 */ 645 fun typeParameterStringsWithRemainder(typeString: String?): Pair<List<String>, String?> { 646 val s = typeString ?: return Pair(emptyList(), null) 647 if (!s.startsWith("<")) return Pair(emptyList(), s) 648 val list = mutableListOf<String>() 649 var balance = 0 650 var expect = false 651 var start = 0 652 var i = 0 653 while (i < s.length) { 654 val c = s[i] 655 if (c == '<') { 656 balance++ 657 expect = balance == 1 658 } else if (c == '>') { 659 balance-- 660 if (balance == 0) { 661 add(list, s, start, i) 662 return if (i == s.length - 1) { 663 Pair(list, null) 664 } else { 665 Pair(list, s.substring(i + 1)) 666 } 667 } 668 } else if (c == ',') { 669 expect = 670 if (balance == 1) { 671 add(list, s, start, i) 672 true 673 } else { 674 false 675 } 676 } else { 677 // This is the start of a parameter 678 if (expect && balance == 1) { 679 start = i 680 expect = false 681 } 682 683 if (c == '@') { 684 // Skip the entire text of the annotation 685 i = findAnnotationEnd(typeString, i + 1) 686 continue 687 } 688 } 689 i++ 690 } 691 return Pair(list, null) 692 } 693 694 /** 695 * Adds the substring of [s] from [from] to [to] to the [list], trimming whitespace from the 696 * front. 697 */ 698 private fun add(list: MutableList<String>, s: String, from: Int, to: Int) { 699 for (i in from until to) { 700 if (!Character.isWhitespace(s[i])) { 701 list.add(s.substring(i, to)) 702 return 703 } 704 } 705 } 706 } 707 708 /** 709 * The cache entry, that contains the [TypeItem] that has been produced from the [type] and 710 * [forceClassToBeNonNull] properties. 711 */ 712 internal inner class CacheEntry( 713 /** The string type from which the [TypeItem] will be parsed. */ 714 private val type: String, 715 716 /** 717 * Indicates whether an outermost [ClassTypeItem] is forced to be [TypeNullability.NONNULL]. 718 * 719 * It is passed into [parseType] and if `true` it will cause the top level class type to be 720 * treated as if it was being parsed when [kotlinStyleNulls] is `true` as that sets 721 * [TypeNullability.NONNULL] by default. 722 */ 723 private val forceClassToBeNonNull: Boolean, 724 ) { 725 /** 726 * Map from [TypeParameterScope] to the [TypeItem] created for it. 727 * 728 * The [TypeParameterScope] that will be used to cache a type depends on the unqualified 729 * names used in the type. It will use the closest enclosing scope of the one supplied that 730 * adds at least one type parameter whose name is used in the type. 731 * 732 * See [TypeParameterScope.findSignificantScope]. 733 */ 734 private val scopeToItem = mutableMapOf<TypeParameterScope, TypeItem>() 735 736 /** 737 * The set of unqualified names used by [type]. 738 * 739 * This is determined solely by the contents of the [type] string and so will be the same 740 * for all [TypeItem]s cached in this entry. 741 * 742 * If this has not been set then no type items have been cached in this entry. It is set the 743 * first time that a [TypeItem] is cached. 744 */ 745 private lateinit var unqualifiedNamesInType: Set<String> 746 747 /** Get the [TypeItem] for this type depending on the setting of [forceClassToBeNonNull]. */ 748 fun getTypeItem(typeParameterScope: TypeParameterScope): TypeItem { 749 // If this is not the first time through then check to see if anything suitable has been 750 // cached. 751 val scopeForCachingOrNull = 752 if (::unqualifiedNamesInType.isInitialized) { 753 // Find the scope to use for caching this type and then check to see if a 754 // [TypeItem] 755 // has been cached for that scope and if so return it. Otherwise, drop out. 756 typeParameterScope.findSignificantScope(unqualifiedNamesInType).also { 757 scopeForCaching -> 758 scopeToItem[scopeForCaching]?.let { 759 cacheHit++ 760 return it 761 } 762 } 763 } else { 764 // This is the first time through, so [unqualifiedNamesInType] is not available 765 // so drop through and initialize later. 766 null 767 } 768 769 // Parse the [type] to produce a [TypeItem]. 770 val typeItem = createTypeItem(typeParameterScope) 771 cacheSize++ 772 773 // Find the scope for caching if it was not found above. 774 val scopeForCaching = 775 scopeForCachingOrNull 776 ?: let { 777 // This will only happen if [unqualifiedNamesInType] is uninitialized so 778 // make sure to initialize it. 779 unqualifiedNamesInType = unqualifiedNameGatherer.gatherFrom(typeItem) 780 781 // Find the scope for caching. It could not be found before because 782 // [unqualifiedNamesInType] was not initialized. 783 typeParameterScope.findSignificantScope(unqualifiedNamesInType) 784 } 785 786 // Store the type item in the scope selected for caching. 787 scopeToItem[scopeForCaching] = typeItem 788 789 // Return it. 790 return typeItem 791 } 792 793 /** 794 * Create a new [TypeItem] for [type] with the given [forceClassToBeNonNull] setting and for 795 * the requested [typeParameterScope]. 796 */ 797 private fun createTypeItem(typeParameterScope: TypeParameterScope): TypeItem { 798 return parseType(type, typeParameterScope, emptyList(), forceClassToBeNonNull) 799 } 800 } 801 802 /** 803 * A [TypeVisitor] that will extract all unqualified names from the type. 804 * 805 * These are the names that could be used as a type parameter name and so whose meaning could 806 * change depending on the [TypeParameterScope], i.e. the set of type parameters currently in 807 * scope. 808 */ 809 private class UnqualifiedNameGatherer : BaseTypeVisitor() { 810 811 private val unqualifiedNames = mutableSetOf<String>() 812 813 override fun visit(primitiveType: PrimitiveTypeItem) { 814 // Primitive type names are added because Kotlin allows them to be shadowed by a type 815 // parameter. 816 unqualifiedNames.add(primitiveType.kind.primitiveName) 817 } 818 819 override fun visitClassType(classType: ClassTypeItem) { 820 // Classes in java.lang package can be represented in the type without the leading 821 // package, all other types must be fully qualified. At this point it is not clear 822 // whether the type used in the input type string was qualified or not as the package 823 // has been prepended so this assumes that they all are just to be on the safe side. 824 // It is only for legacy reasons that all `java.lang` package prefixes are stripped 825 // when generating the API signature files. See b/324047248. 826 val name = classType.qualifiedName 827 if (!name.contains('.')) { 828 unqualifiedNames.add(name) 829 } else { 830 val trimmed = TypeItem.stripJavaLangPrefix(name) 831 if (trimmed != name) { 832 unqualifiedNames.add(trimmed) 833 } 834 } 835 } 836 837 override fun visitVariableType(variableType: VariableTypeItem) { 838 unqualifiedNames.add(variableType.name) 839 } 840 841 /** Gather the names from [typeItem] returning an immutable set of the unqualified names. */ 842 fun gatherFrom(typeItem: TypeItem): Set<String> { 843 unqualifiedNames.clear() 844 typeItem.accept(this) 845 return unqualifiedNames.toSet() 846 } 847 } 848 849 /** 850 * An instance of [UnqualifiedNameGatherer] used for gathering all the unqualified names from 851 * all the [TypeItem]s cached by this. 852 */ 853 private val unqualifiedNameGatherer = UnqualifiedNameGatherer() 854 } 855