001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2023–2024 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.bean; 015 016import java.lang.System.Logger; 017 018import java.lang.constant.ClassDesc; 019import java.lang.constant.Constable; 020import java.lang.constant.ConstantDesc; 021import java.lang.constant.DynamicConstantDesc; 022import java.lang.constant.MethodHandleDesc; 023 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.List; 027import java.util.Objects; 028import java.util.Optional; 029 030import java.util.function.Predicate; 031 032import javax.lang.model.element.Element; 033import javax.lang.model.element.ElementKind; 034import javax.lang.model.element.Parameterizable; 035import javax.lang.model.element.QualifiedNameable; 036 037import javax.lang.model.type.ArrayType; 038import javax.lang.model.type.DeclaredType; 039import javax.lang.model.type.IntersectionType; 040import javax.lang.model.type.ReferenceType; 041import javax.lang.model.type.TypeKind; 042import javax.lang.model.type.TypeMirror; 043import javax.lang.model.type.TypeVariable; 044import javax.lang.model.type.WildcardType; 045 046import org.microbean.lang.TypeAndElementSource; 047 048import static java.lang.constant.ConstantDescs.BSM_INVOKE; 049 050import static org.microbean.lang.ConstantDescs.CD_TypeAndElementSource; 051 052/** 053 * A {@link Matcher} encapsulating <a 054 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">CDI-compatible 055 * type matching rules</a>. 056 * 057 * @author <a href="https://about.me/lairdnelson/" target="_top">Laird Nelson</a> 058 * 059 * @see #test(TypeMirror, TypeMirror) 060 */ 061public final class TypeMatcher implements Constable, Matcher<TypeMirror, TypeMirror> { 062 063 064 /* 065 * Static fields. 066 */ 067 068 069 private static final Logger LOGGER = System.getLogger(TypeMatcher.class.getName()); 070 071 072 /* 073 * Instance fields. 074 */ 075 076 077 private final TypeAndElementSource tes; 078 079 080 /* 081 * Constructors. 082 */ 083 084 085 /** 086 * Creates a new {@link TypeMatcher}. 087 * 088 * @param tes a {@link TypeAndElementSource}; must not be {@code null} 089 * 090 * @exception NullPointerException if {@code tes} is {@code null} 091 * 092 * @see TypeAndElementSource 093 */ 094 public TypeMatcher(final TypeAndElementSource tes) { 095 super(); 096 this.tes = Objects.requireNonNull(tes, "tes"); 097 } 098 099 100 /* 101 * Instance methods. 102 */ 103 104 105 /* 106 * Public methods. 107 */ 108 109 /** 110 * Returns an {@link Optional} housing a {@link ConstantDesc} that represents this {@link TypeMatcher}. 111 * 112 * <p>This method never returns {@code null}.</p> 113 * 114 * <p>The default implementation of this method relies on the presence of a {@code public} constructor that accepts a 115 * single {@link TypeAndElementSource}-typed argument.</p> 116 * 117 * <p>The {@link Optional} returned by an invocation of this method may be, and often will be, {@linkplain 118 * Optional#isEmpty() empty}.</p> 119 * 120 * @return an {@link Optional} housing a {@link ConstantDesc} that represents this {@link TypeMatcher}; never {@code 121 * null} 122 * 123 * @see Constable#describeConstable() 124 */ 125 @Override // Constable 126 public final Optional<? extends ConstantDesc> describeConstable() { 127 return (this.tes instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty()) 128 .map(tesDesc -> DynamicConstantDesc.of(BSM_INVOKE, 129 MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), 130 CD_TypeAndElementSource), 131 tesDesc)); 132 } 133 134 /** 135 * Returns {@code true} if and only if the supplied {@code payload} argument <a 136 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution"><em>matches</em></a> 137 * the supplied {@code receiver} argument, according to the rules defined by <a 138 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">section 139 * 2.4.2.1 of the CDI specification</a>. 140 * 141 * @param receiver the left hand side of a type assignment; must not be {@code null} 142 * 143 * @param payload the right hand side of a type assignment; must not be {@code null} 144 * 145 * @return {@code true} if and only if the supplied {@code payload} argument <a 146 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">matches</a> 147 * the supplied {@code receiver} argument, according to the rules defined by <a 148 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">section 149 * 2.4.2.1 of the CDI specification</a>; {@code false} otherwise 150 * 151 * @exception NullPointerException if either argument is {@code null} 152 * 153 * @exception IllegalArgumentException if either type is any type other than an {@linkplain TypeKind#ARRAY array 154 * type}, a {@link TypeKind#isPrimitive() primitive type}, or a {@linkplain TypeKind#DECLARED declared type} 155 */ 156 // Is the payload assignable to the receiver? That is, does the payload "match the receiver", in CDI parlance? 157 @Override 158 public final boolean test(final TypeMirror receiver, final TypeMirror payload) { 159 // "A bean is assignable to a given injection point if: 160 // 161 // "The bean has a bean type [payload] that matches the required type [receiver]. For this purpose..." 162 return receiver == Objects.requireNonNull(payload, "payload") || switch (receiver.getKind()) { 163 // "...primitive types are considered to match their corresponding wrapper types in java.lang..." 164 case BOOLEAN -> payload.getKind() == TypeKind.BOOLEAN || declaredTypeNamed(payload, "java.lang.Boolean"); 165 case BYTE -> payload.getKind() == TypeKind.BYTE || declaredTypeNamed(payload, "java.lang.Byte"); 166 case CHAR -> payload.getKind() == TypeKind.CHAR || declaredTypeNamed(payload, "java.lang.Character"); 167 case DOUBLE -> payload.getKind() == TypeKind.DOUBLE || declaredTypeNamed(payload, "java.lang.Double"); 168 case FLOAT -> payload.getKind() == TypeKind.FLOAT || declaredTypeNamed(payload, "java.lang.Float"); 169 case INT -> payload.getKind() == TypeKind.INT || declaredTypeNamed(payload, "java.lang.Integer"); 170 case LONG -> payload.getKind() == TypeKind.LONG || declaredTypeNamed(payload, "java.lang.Long"); 171 case SHORT -> payload.getKind() == TypeKind.SHORT || declaredTypeNamed(payload, "java.lang.Short"); 172 // "...and array types are considered to match only if their element types [note: not component types] are 173 // identical ['identical' is actually undefined in the specification]..." 174 case ARRAY -> payload.getKind() == TypeKind.ARRAY && this.identical(elementType(receiver), elementType(payload)); 175 case DECLARED -> switch (payload.getKind()) { 176 // "...primitive types are considered to match their corresponding wrapper types in java.lang..." 177 case BOOLEAN -> named((DeclaredType)receiver, "java.lang.Boolean"); 178 case BYTE -> named((DeclaredType)receiver, "java.lang.Byte"); 179 case CHAR -> named((DeclaredType)receiver, "java.lang.Character"); 180 case DOUBLE -> named((DeclaredType)receiver, "java.lang.Double"); 181 case FLOAT -> named((DeclaredType)receiver, "java.lang.Float"); 182 case INT -> named((DeclaredType)receiver, "java.lang.Integer"); 183 case LONG -> named((DeclaredType)receiver, "java.lang.Long"); 184 case SHORT -> named((DeclaredType)receiver, "java.lang.Short"); 185 // "Parameterized and raw types [and non-generic classes, and non-array types]..." 186 case DECLARED -> 187 // "...are considered to match if they are identical [undefined]..." 188 this.identical(receiver, payload) || 189 // "...or if the bean type [payload] is assignable to [see #assignable(TypeMirror, TypeMirror)] the required type 190 // [receiver]...." 191 this.assignable((DeclaredType)receiver, (DeclaredType)payload); 192 default -> throw new IllegalArgumentException("Illegal payload kind: " + payload.getKind() + "; payload: " + payload); 193 }; 194 default -> throw new IllegalArgumentException("Illegal receiver kind: " + receiver.getKind() + "; receiver: " + receiver); 195 }; 196 } 197 198 /** 199 * Returns the {@link TypeAndElementSource} used by this {@link TypeMatcher}. 200 * 201 * @return the {@link TypeAndElementSource} used by this {@link TypeMatcher}; never {@code null} 202 * 203 * @see #TypeMatcher(TypeAndElementSource) 204 * 205 * @see TypeAndElementSource 206 */ 207 public final TypeAndElementSource typeAndElementSource() { 208 return this.tes; 209 } 210 211 /* 212 * Private methods. 213 */ 214 215 private final boolean assignable(final DeclaredType receiver, final DeclaredType payload) { 216 assert receiver.getKind() == TypeKind.DECLARED; 217 assert payload.getKind() == TypeKind.DECLARED; 218 return switch (payload) { 219 // "A parameterized bean type [payload] is considered assignable..." 220 case DeclaredType parameterizedPayload when parameterized(payload) -> switch (receiver) { 221 // "...to a [non-generic class or] raw required type [receiver]..." 222 case TypeMirror rawReceiver when !generic(receiver) || raw(receiver) -> { 223 // "...if the [non-generic class or] raw types are identical [undefined] and all type parameters [type 224 // arguments] of the bean type [payload] are either unbounded type variables [undefined] or java.lang.Object." 225 yield 226 this.identical(this.nonGenericClassOrRawType(rawReceiver), this.nonGenericClassOrRawType(parameterizedPayload)) && 227 allTypeArgumentsAre(parameterizedPayload.getTypeArguments(), 228 ((Predicate<TypeMirror>)this::unboundedTypeVariable).or(TypeMatcher::isJavaLangObject)); 229 } 230 // "...to a parameterized required type [receiver]..." 231 case DeclaredType parameterizedReceiver when parameterized(receiver) -> { 232 // "...if they have identical raw type [really if their declarations/elements are 'identical']..." 233 if (this.identical(this.rawType(parameterizedReceiver), this.rawType(parameterizedPayload))) { 234 // "...and for each parameter [type argument pair]..." 235 final List<? extends TypeMirror> rtas = parameterizedReceiver.getTypeArguments(); 236 final List<? extends TypeMirror> ptas = parameterizedPayload.getTypeArguments(); 237 assert rtas.size() == ptas.size(); 238 for (int i = 0; i < rtas.size(); i++) { 239 final TypeMirror rta = rtas.get(i); 240 final TypeMirror pta = ptas.get(i); 241 // "...the required [receiver] type parameter [type argument] and the bean [payload] type parameter [type 242 // argument] are actual types [non-type variable, non-wildcard reference types]..." 243 if (actual(rta)) { 244 if (actual(pta)) { 245 // "...with identical [non-generic classes or] raw type[s]..." 246 if (this.identical(this.nonGenericClassOrRawType(rta), this.nonGenericClassOrRawType(pta))) { 247 // "...and, if the type [?] is parameterized [?]..." 248 // 249 // Let rta and pta be array types with parameterized element types, such as List<Number>[] and 250 // List<String>[]. Then their raw types are List[] and List[]. Their parameterized element types are 251 // List<Number> and List<String>. According to the JLS, neither List<Number>[] nor List<String>[] is 252 // parameterized. 253 // 254 // In this example, if we get here, we have only proven that List[] is "identical to" List[]. 255 // 256 // The "if the type is parameterized" clause is tough. Which type is under discussion? "the type" 257 // seems to refer to the "identical raw type". But a raw type by definition is not parameterized, so 258 // this clause would seem to be superfluous and thus never apply, and so List<Number>[] := 259 // List<String>[] is OK. Oops. So not that interpretation. 260 // 261 // What if instead the "if the type is parameterized" clause means the receiver type itself and 262 // somehow loosely the payload type as well? Well, clearly it cannot correctly do this, since an 263 // array type is never a parameterized type. Same bad result. Oops. So not that interpretation. 264 // 265 // Or what if "identical raw type" really means "identical raw type (or identical component type if 266 // they are array types)"? That would be most efficient, since it would rule out List<Number>[] := 267 // List<String>[] right off the bat: we wouldn't even get here. But that really doesn't seem to be 268 // what is meant. So probably not that interpretation. 269 // 270 // What if the "if the type is parameterized" clause really means "if the element type declaration 271 // used by both the receiver and payload types is generic"? That would work. That's also 272 // semantically equivalent to something like: "...if at least one of the two arguments is a 273 // parameterized type [e.g. List<Number>, not List<Number>[], not String, not List], or at least one 274 // of the two types is an array type with a parameterized element type [e.g. List<Number>[], not 275 // List[], not String[]]..." 276 // 277 // That is the interpretation we apply here. So: 278 // 279 // "...and, if the type [?] is parameterized [?]..." 280 // 281 // becomes: 282 // 283 // "...and, if at least one of the two type arguments is a parameterized type, or if at least one 284 // of the two types is an array type with a parameterized element type..." 285 // 286 // That, in turn, designates any type capable of properly yielding a raw type, while ruling out 287 // those that can't! That means it is exactly equal to our yieldsRawType(TypeMirror) method, and so 288 // that's what cdiParameterized(TypeMirror) returns (go see for yourself). 289 if (cdiParameterized(rta)) { // really just yieldsRawType(rta) 290 assert cdiParameterized(pta); // ...because otherwise their raw types would not have been "identical" 291 // "...the bean type parameter [type argument] is assignable to the required type parameter [type 292 // argument] according to [all of] these rules [including 'matching']..." 293 if (test(rta, pta)) { // note recursion 294 continue; 295 } 296 yield false; 297 } else { 298 assert !cdiParameterized(pta); 299 continue; // yes, trust me; vetted 300 } 301 } 302 yield false; 303 } else if (pta.getKind() == TypeKind.TYPEVAR) { 304 // "...the required [receiver] type parameter [type argument] is an actual type [a non-type variable, 305 // non-wildcard reference type], the bean [payload] type parameter [type argument] is a type variable and 306 // the actual type [non-type variable, non-wildcard reference type] [required type argument, receiver] is 307 // assignable to the upper bound, if any, of the type variable [bean type argument, payload] [type 308 // variables have multiple bounds]..." 309 // 310 // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to the 311 // *payload* type argument.) 312 if (assignableToCondensedTypeVariableBounds((TypeVariable)pta, (ReferenceType)rta)) { 313 continue; 314 } 315 yield false; 316 } 317 throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind()); 318 } else if (rta.getKind() == TypeKind.WILDCARD) { 319 // "...the required type parameter [type argument] is a wildcard, the bean type parameter [type 320 // argument] is an actual type [a non-type variable, non-wildcard reference type]..." 321 if (actual(pta)) { 322 // "...and the actual type [non-type variable, non-wildcard reference type] is assignable to the upper 323 // bound, if any, of the wildcard and assignable from the lower bound, if any of the wildcard" 324 if (assignableToCondensedExtendsBound((WildcardType)rta, (ReferenceType)pta) && 325 assignableFromCondensedSuperBound((ReferenceType)pta, (WildcardType)rta)) { 326 continue; 327 } 328 yield false; 329 } else if (pta.getKind() == TypeKind.TYPEVAR) { 330 // "...the required [receiver] type parameter [type argument] is a wildcard, the bean [payload] type 331 // parameter [type argument] is a type variable and the upper bound of the type variable [a type 332 // variable has many bounds?] is assignable to or assignable from the upper bound, if any, of the 333 // wildcard and assignable from the lower bound, if any, of the wildcard" 334 if ((condensedTypeVariableBoundsAssignableToCondensedExtendsBound((WildcardType)rta, (TypeVariable)pta) || 335 condensedTypeVariableBoundsAssignableFromCondensedExtendsBound((TypeVariable)pta, (WildcardType)rta)) && 336 condensedTypeVariableBoundsAssignableFromCondensedSuperBound((TypeVariable)pta, (WildcardType)rta)) { 337 continue; 338 } 339 yield false; 340 } 341 throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind()); 342 } else if (rta.getKind() == TypeKind.TYPEVAR) { 343 if (pta.getKind() == TypeKind.TYPEVAR) { 344 // "...the required [receiver] type parameter [type argument] and the bean [payload] type parameter 345 // [type argument] are both type variables and the upper bound of the required [receiver] type 346 // parameter [type argument] [a type variable has many bounds?] is assignable to the upper bound [a 347 // type variable has many bounds?], if any, of the bean [payload] type parameter [type argument]" 348 // 349 // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to the 350 // *payload* type argument.) 351 if (condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds((TypeVariable)pta, (TypeVariable)rta)) { 352 continue; 353 } 354 yield false; 355 } 356 throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind()); 357 } 358 throw new AssertionError("Unexpected receiver type argument kind: " + rta.getKind()); 359 } 360 yield true; // we passed all the type argument assignability tests 361 } 362 yield false; // the type arguments' non-generic classes or raw types were not identical 363 } 364 default -> throw new AssertionError("Unexpected receiver kind: " + receiver.getKind()); 365 }; 366 // "A [non-generic or] raw bean type [payload] is considered assignable..." 367 case TypeMirror nonGenericOrRawPayload -> switch (receiver) { 368 // "...to a parameterized required type [receiver]..." 369 case DeclaredType parameterizedReceiver when parameterized(receiver) -> { 370 // "...if the[ir] [non-generic classes or] raw types are identical and all type parameters [type arguments] of 371 // the required type [receiver] are either unbounded type variables or java.lang.Object." 372 yield 373 this.identical(this.nonGenericClassOrRawType(parameterizedReceiver), nonGenericOrRawPayload) && 374 allTypeArgumentsAre(parameterizedReceiver.getTypeArguments(), 375 ((Predicate<TypeMirror>)this::unboundedTypeVariable).or(TypeMatcher::isJavaLangObject)); 376 } 377 // [Otherwise the payload is not assignable to the receiver; identity checking should have already happened in 378 // matches(), not here.] 379 case DeclaredType nonGenericOrRawReceiver when receiver.getKind() == TypeKind.DECLARED -> { 380 yield false; // or yield this.identical(nonGenericOrRawReceiver, nonGenericOrRawPayload); 381 } 382 default -> throw new AssertionError("Unexpected payload kind: " + payload.getKind() + "; receiver: " + receiver + "; payload: " + payload); 383 }; 384 }; 385 } 386 387 // Is payload "identical to" receiver, following the intent of CDI? The relation "identical to" is not defined in the 388 // specification. Does it mean ==? Does it mean equals()? Does it mean 389 // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)? Something else? 390 // 391 // This implementation chooses TypeAndElementSource#sameType(TypeMirror, TypeMirror), but with one 392 // change. TypeAndElementSource#sameType(TypeMirror, TypeMirror) is usually backed by the 393 // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror) method. That method will return false if either 394 // argument is a wildcard type. This method first checks to see if the arguments are the same Java references (==), 395 // regardless of type. 396 private final boolean identical(final TypeMirror receiver, final TypeMirror payload) { 397 // CDI has an undefined notion of "identical to". This method attempts to divine and implement the intent. Recall 398 // that javax.lang.model.* compares types with "sameType" semantics. 399 return receiver == payload || this.tes.sameType(receiver, payload); 400 } 401 402 // Is the "actual" type argument represented by payload assignable to all of the receiver's condensed bounds? 403 // 404 // Pay close attention to how this is called, i.e. what is payload and what is receiver is often "backwards". For 405 // example: 406 // 407 // @Inject Foo<String> foo; <-- Bean<T extends CharSequence> // is the String "actual type" (payload) assignable to T's (receiver's) CharSequence bound? 408 // // ^ payload! ^ receiver! 409 // 410 // Recall that after condensing "T extends CharSequence" you get CharSequence. 411 // 412 // Recall that if, instead, you had T extends S and S extends CharSequence, condensing T still yields CharSequence. 413 private final boolean assignableToCondensedTypeVariableBounds(final TypeVariable receiver, final ReferenceType payload) { 414 assert receiver.getKind() == TypeKind.TYPEVAR; 415 assert actual(payload); // We can assert this only because of where the method is called. 416 return covariantlyAssignable(List.of(receiver), List.of(payload)); // deliberately List.of(payload) and not condense(payload) 417 } 418 419 // Is candidate covariantly assignable to w's condensed extends (upper) bound? 420 private final boolean assignableToCondensedExtendsBound(final WildcardType w, final ReferenceType candidate) { 421 assert w.getKind() == TypeKind.WILDCARD; 422 assert actual(candidate); // We can assert this only because of where this method is called from. 423 final ReferenceType extendsBound = (ReferenceType)w.getExtendsBound(); 424 return extendsBound == null || switch (extendsBound.getKind()) { 425 case ARRAY, DECLARED -> covariantlyAssignable(extendsBound, candidate); 426 case TYPEVAR -> covariantlyAssignable((ReferenceType)this.condense(extendsBound), List.of(candidate)); 427 default -> throw new AssertionError(); 428 }; 429 } 430 431 // It's not immediately clear what CDI means by a type variable's upper bound. In javax.lang.model.type parlance, the 432 // upper bound of a TypeVariable could be an IntersectionType, which java.lang.reflect.TypeVariable represents as a 433 // collection of bounds. CDI blundered into this earlier: https://issues.redhat.com/browse/CDI-440 and 434 // https://github.com/jakartaee/cdi/issues/682 435 // 436 // "the upper bound of the required type parameter [receiver type argument] is assignable to the upper bound, if any, 437 // of the bean type parameter [payload type argument]" (when both arguments are type variables) should *actually* 438 // read: 439 // 440 // "for each bound, PTA, of the bean type parameter [payload type argument], there is at least one bound, RTA, of the 441 // required type parameter [receiver type argument], which is assignable to PTA." 442 // 443 // The TCK enforces this, even though it's not in the specification (!). 444 // 445 // Weld's implementation confuses type parameters with arguments, just like the specification. They have a series of 446 // methods implemented as part of PR 614 (https://github.com/weld/core/pull/614) named "parametersMatch" [arguments 447 // match]. 448 // 449 // Weld also has methods named things like "getUppermostTypeVariableBounds" and "getUppermostBounds". These appear to 450 // "condense" useless type variable extensions to "get to" the "real" types involved. So T extends S extends String 451 // becomes String. 452 // 453 // Digging deeper, you (I) might think getUppermostTypeVariableBounds(tv) is just erase(tv) applied recursively. But I 454 // think you would be wrong. 455 // 456 // Start with: 457 // https://github.com/openjdk/jdk/blob/181845ae46157a9bb3bf8e2a328fa59eddc0273a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2450 458 // 459 // Compare vs.: 460 // https://github.com/weld/core/blob/e894d1699ff1c91332605f5ecae5f53410effb81/impl/src/main/java/org/jboss/weld/resolution/AbstractAssignabilityRules.java#L57-L62 461 // 462 // To illustrate the difference between the two operations, recursive erasure (according to the Java Language 463 // Specification) of the pseudo-declaration T extends S extends List<String> & Serializable would yield, simply, List 464 // (T would erease to "the erasure of its leftmost bound", which would be the erasure of S, which would be "the 465 // erasure of its leftmost bound", which would be the erasure of List<String>, which would be List. Serializable just 466 // gets dropped.) 467 // 468 // By contrast, Weld's getUppermostTypeVariableBounds(T) operation would yield [List<String>, Serializable] (have not 469 // tested this with code, just reading). 470 // 471 // I think this is what the javac compiler calls, somewhat confusingly, "classBound": 472 // https://github.com/openjdk/jdk/blob/jdk-24%2B7/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2760-L2796 473 // 474 // So then: 475 // 476 // For every bound in (condensed) receiverBounds, is there a bound in (condensed) payloadBounds that is covariantly 477 // assignable to it? 478 // 479 // (Is there one bound in (condensed) payloadBounds that matches all bounds in (condensed) receiver bounds?) 480 // 481 // Throws ClassCastException if, after condensing, any encountered bound is not either an ArrayType or a DeclaredType. 482 private final boolean covariantlyAssignable(final List<? extends TypeMirror> receiverBounds, List<? extends TypeMirror> payloadBounds) { 483 payloadBounds = this.condense(payloadBounds); 484 for (final TypeMirror condensedReceiverBound : this.condense(receiverBounds)) { 485 if (!covariantlyAssignable((ReferenceType)condensedReceiverBound, payloadBounds)) { 486 return false; 487 } 488 } 489 return true; 490 } 491 492 // Is there a DeclaredType-or-ArrayType bound in condensedPayloadBounds that is assignable to classOrArrayTypeReceiver 493 // using Java, not CDI, assignability semantics? 494 // 495 // It is assumed condensedPayloadBounds is the result of a condense() call. 496 // 497 // Throws ClassCastException or IllegalArgumentException if any encountered type is neither an ArrayType nor a 498 // DeclaredType. 499 private final boolean covariantlyAssignable(final ReferenceType classOrArrayTypeReceiver, final List<? extends TypeMirror> condensedPayloadBounds) { 500 assert actual(classOrArrayTypeReceiver); // We can assert this only because of where this method is called. 501 return switch (classOrArrayTypeReceiver.getKind()) { 502 case ARRAY, DECLARED -> { 503 for (final TypeMirror condensedPayloadBound : condensedPayloadBounds) { 504 assert actual(condensedPayloadBound); 505 if (covariantlyAssignable(classOrArrayTypeReceiver, (ReferenceType)condensedPayloadBound)) { 506 yield true; 507 } 508 } 509 yield false; 510 } 511 default -> throw new IllegalArgumentException("classOrArrayTypeReceiver: " + classOrArrayTypeReceiver + "; kind: " + classOrArrayTypeReceiver.getKind()); 512 }; 513 } 514 515 // Is classOrArrayTypePayload assignable to classOrArrayTypeReceiver following the rules of Java assignability 516 // (i.e. covariance)? 517 // 518 // The types are ReferenceTypes because this is only ever invoked in the context of type arguments. 519 private final boolean covariantlyAssignable(final ReferenceType classOrArrayTypeReceiver, final ReferenceType classOrArrayTypePayload) { 520 assert actual(classOrArrayTypeReceiver); // Based on where and how this method is called. 521 assert actual(classOrArrayTypePayload); // Based on where and how this method is called. 522 return 523 classOrArrayTypeReceiver == classOrArrayTypePayload || // Optimization 524 // Note that TypeAndElementSource#assignable(TypeMirror, TypeMirror) follows the lead of 525 // javax.lang.model.util.Types#isAssignable(TypeMirror, TypeMirror) where the "payload" is the *first* parameter 526 // and the "receiver" is the *second* argument. 527 this.tes.assignable(classOrArrayTypePayload, classOrArrayTypeReceiver); // yes, "backwards" 528 } 529 530 // Are payload's condensed bounds assignable to receiver's condensed extends bound (upper bound)? 531 private final boolean condensedTypeVariableBoundsAssignableToCondensedExtendsBound(final WildcardType receiver, final TypeVariable payload) { 532 assert receiver.getKind() == TypeKind.WILDCARD; 533 assert payload.getKind() == TypeKind.TYPEVAR; 534 // "...the upper bound of the type variable [a type variable has many bounds?] is assignable TO [...] the upper 535 // bound, if any, of the wildcard..." 536 final TypeMirror extendsBound = receiver.getExtendsBound(); 537 // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant 538 // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. 539 return extendsBound == null || covariantlyAssignable(List.of((ReferenceType)extendsBound), List.of(payload)); 540 } 541 542 // Is payload's condensed extends bound (upper bound) covariantly assignable to receiver's condensed bounds? 543 private final boolean condensedTypeVariableBoundsAssignableFromCondensedExtendsBound(final TypeVariable receiver, final WildcardType payload) { 544 assert receiver.getKind() == TypeKind.TYPEVAR; 545 assert payload.getKind() == TypeKind.WILDCARD; 546 // "...the upper bound of the type variable [a type variable has many bounds?] is assignable [...] FROM the upper 547 // bound, if any, of the wildcard..." 548 final TypeMirror extendsBound = payload.getExtendsBound(); 549 // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant 550 // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. 551 return extendsBound == null || covariantlyAssignable(List.of(receiver), List.of((ReferenceType)extendsBound)); 552 } 553 554 // Is payload's super bound (lower bound) covariantly assignable to receiver's condensed bounds? 555 private final boolean condensedTypeVariableBoundsAssignableFromCondensedSuperBound(final TypeVariable receiver, final WildcardType payload) { 556 assert receiver.getKind() == TypeKind.TYPEVAR; 557 assert payload.getKind() == TypeKind.WILDCARD; 558 final TypeMirror superBound = payload.getSuperBound(); 559 // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant 560 // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. 561 return superBound == null || covariantlyAssignable(List.of(receiver), List.of(superBound)); 562 } 563 564 // Are payload's condensed bounds covariantly assignable to receiver's condensed bounds? 565 private final boolean condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds(final TypeVariable receiver, final TypeVariable payload) { 566 assert receiver.getKind() == TypeKind.TYPEVAR; 567 assert payload.getKind() == TypeKind.TYPEVAR; 568 // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant 569 // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. 570 return covariantlyAssignable(List.of(receiver), List.of(payload)); 571 } 572 573 // Is payload's condensed super bound (lower bound) covariantly assignable to receiver? 574 // 575 // Since either of a wildcard type's bounds may be a type variable, and since a type variable's bound may be an 576 // intersection type, it follows that after condensation either of a wildcard type's bounds may be a list of actual 577 // types. 578 // 579 // Therefore this is really: Is there one bound among payload's actual super bounds that is assignable to receiver? 580 private final boolean assignableFromCondensedSuperBound(final ReferenceType receiver, final WildcardType payload) { 581 assert payload.getKind() == TypeKind.WILDCARD; 582 assert actual(receiver); // We can assert this only because of where this method is called from. 583 final ReferenceType superBound = (ReferenceType)payload.getSuperBound(); 584 return superBound == null || switch (superBound.getKind()) { 585 case ARRAY, DECLARED -> covariantlyAssignable(receiver, superBound); 586 case TYPEVAR -> covariantlyAssignable(receiver, (ReferenceType)this.condense(superBound)); 587 default -> throw new AssertionError(); 588 }; 589 } 590 591 // Get the raw type yielded by t, assuming t is the sort of type that can yield a raw type. 592 // 593 // An array type with a parameterized element type can yield a raw type. 594 // 595 // A declared type that is parameterized can yield a raw type. 596 // 597 // No other type yields a raw type. 598 // 599 // t cannot be null. 600 private final TypeMirror rawType(final TypeMirror t) { 601 return switch (t.getKind()) { 602 case ARRAY -> { 603 final TypeMirror et = elementType(t); 604 if (!parameterized(et)) { 605 throw new IllegalArgumentException("t is an array type whose element type is not parameterized so cannot yield a raw type"); 606 } 607 yield this.arrayTypeOf(this.erasure(et)); 608 } 609 case DECLARED -> { 610 if (!parameterized(t)) { 611 throw new IllegalArgumentException("t is a declared type that is not parameterized so cannot yield a raw type"); 612 } 613 yield this.erasure(t); 614 } 615 default -> throw new IllegalArgumentException("t is a " + t.getKind() + " type and so cannot yield a raw type"); 616 }; 617 } 618 619 // Return t if its element declares a non-generic class, or if it is the raw type usage of a generic class. 620 private final TypeMirror nonGenericClassOrRawType(final TypeMirror t) { 621 return yieldsRawType(t) ? this.rawType(t) : t; 622 } 623 624 // Erase t and return its erasure. Erasing is a complex business that can involve the creation of new types. 625 private final TypeMirror erasure(final TypeMirror t) { 626 return this.tes.erasure(t); 627 } 628 629 // Return a possibly new ArrayType whose component type is the supplied componentType. 630 private final ArrayType arrayTypeOf(final TypeMirror componentType) { 631 return this.tes.arrayTypeOf(componentType); 632 } 633 634 // "Condenses" t, according to the following rules. 635 // 636 // If t is null, returns List.of(). 637 // 638 // If t is a type variable, returns the result of condensing its upper bound (thus eliminating the type variable). 639 // 640 // If t is an intersection type, returns the result of condensing its bounds (thus eliminating the intersection type). 641 // 642 // If t is a wildcard type, returns the result of condensing either its super or extends bound (thus eliminating the 643 // wildcard type). The result of condensing a wildcard type that declares neither bound (?) is the result of 644 // condensing the type declared by java.lang.Object. 645 // 646 // In all other cases returns List.of(t). 647 // 648 // Note especially this is not type erasure, though it has some similarities. 649 // 650 // See #condense(List) below. 651 private final List<? extends TypeMirror> condense(final TypeMirror t) { 652 return t == null ? List.of() : switch (t.getKind()) { 653 case INTERSECTION -> this.condense(((IntersectionType)t).getBounds()); 654 case TYPEVAR -> this.condense(((TypeVariable)t).getUpperBound()); 655 case WILDCARD -> { 656 final WildcardType w = (WildcardType)t; 657 final TypeMirror s = w.getSuperBound(); 658 final TypeMirror e = w.getExtendsBound(); 659 if (s == null) { 660 yield e == null ? List.of(this.tes.declaredType("java.lang.Object")) : this.condense(e); 661 } else if (e == null) { 662 yield this.condense(s); 663 } else { 664 throw new AssertionError(); 665 } 666 } 667 default -> List.of(t); // t's bounds are defined solely by itself, or it's a wildcard and we didn't say which bounds 668 }; 669 } 670 671 // If ts is empty, returns an empty unmodifiable List. 672 // 673 // If ts consists of a single element, returns the result of condensing t (see #condense(TypeMirror) above). 674 // 675 // Otherwise creates a new list, condenses each element of ts, and adds each element of the condensation to the new 676 // list, and returns an unmodifiable view of the new list. 677 // 678 // Note that deliberately duplicates are not eliminated from the new list. 679 private final List<? extends TypeMirror> condense(final List<? extends TypeMirror> ts) { 680 final int size = ts.isEmpty() ? 0 : ts.size(); 681 if (size == 0) { 682 return List.of(); 683 } 684 // We take care not to allocate/copy new lists needlessly. 685 ArrayList<TypeMirror> newBounds = null; 686 for (int i = 0; i < size; i++) { 687 final TypeMirror t = ts.get(i); 688 switch (t.getKind()) { 689 case INTERSECTION: 690 case TYPEVAR: 691 case WILDCARD: 692 // These are the only types where condensing does anything. In these cases alone we need to make a new list. 693 if (newBounds == null) { 694 newBounds = new ArrayList<>(size * 2); 695 if (i > 0) { 696 // All ts up to this point have been non-condensable types, so catch up and add them as-is. 697 newBounds.addAll(ts.subList(0, i)); 698 } 699 } 700 newBounds.addAll(condense(t)); 701 break; 702 default: 703 if (newBounds != null) { 704 newBounds.add(t); 705 } 706 break; 707 } 708 } 709 if (newBounds == null) { 710 return ts; 711 } 712 newBounds.trimToSize(); 713 return Collections.unmodifiableList(newBounds); 714 } 715 716 // Is t an unbounded type variable? 717 // 718 // CDI does not define what an "unbounded type variable" is. This method attempts to divine and implement the intent. 719 // 720 // Since according to the Java Language Specification, all type variables have an upper bound, which in the 721 // pathological case is java.lang.Object, it would seem that "unbounded" means "has java.lang.Object as its upper 722 // bound". 723 // 724 // It is not clear in the case of T extends S whether T is an unbounded TypeVariable or not. This interpretation 725 // behaves as if it is. 726 // 727 // Weld seems to take the position that an unbounded type variable is one that has java.lang.Object as its sole upper 728 // bound; see 729 // https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L258. Under this 730 // interpretation T extends S would not be considered an unbounded type variable. Type variable bounds are erased in 731 // every other situation in CDI. 732 private final boolean unboundedTypeVariable(final TypeMirror t) { 733 return switch (t.getKind()) { 734 case TYPEVAR -> { 735 final List<? extends TypeMirror> condensedBounds = this.condense(((TypeVariable)t).getUpperBound()); 736 yield condensedBounds.size() == 1 && isJavaLangObject(condensedBounds.get(0)); 737 } 738 default -> false; 739 }; 740 } 741 742 743 /* 744 * Static methods. 745 */ 746 747 748 // Does e represent a generic declaration? 749 // 750 // A declaration is generic if it declares one or more type parameters. 751 // 752 // Since an annotation interface cannot declare type parameters, it follows that TypeElements representing annotation 753 // instances are never generic. 754 private static final boolean generic(final Element e) { 755 return switch (e.getKind()) { 756 case CLASS, CONSTRUCTOR, ENUM, INTERFACE, METHOD, RECORD -> !((Parameterizable)e).getTypeParameters().isEmpty(); 757 default -> false; 758 }; 759 } 760 761 // Is t the usage of a generic class, i.e. a usage (whether raw or parameterized) of a generic class declaration? 762 // 763 // t is deemed to be generic if it is a declared type whose defining element (its type declaration) is generic. 764 // 765 // Array types are never generic. 766 private static final boolean generic(final TypeMirror t) { 767 return 768 t.getKind() == TypeKind.DECLARED && 769 generic(((DeclaredType)t).asElement()); 770 } 771 772 // Is t a parameterized type (and not a raw type) strictly according to the rules of the Java Language Specification? 773 // 774 // A type is parameterized if it is a declared type with a non-empty list of type arguments. No other type is 775 // parameterized. 776 private static final boolean parameterized(final TypeMirror t) { 777 return 778 t.getKind() == TypeKind.DECLARED && 779 !((DeclaredType)t).getTypeArguments().isEmpty(); 780 } 781 782 // Is t a type that CDI considers to be "parameterized"? 783 // 784 // There are some cases, but not all, where CDI (incorrectly) considers an array type to be something that can be 785 // parameterized, or else "bean type parameter" resolution would never work. See 786 // https://stackoverflow.com/questions/76493672/when-cdi-speaks-of-a-parameterized-type-does-it-also-incorrectly-mean-array-typ. 787 // 788 // The semantics CDI wants to express are really: can t *yield* a raw type, for a certain definition of "yield"? See 789 // #yieldsRawType(TypeMirror) below. 790 private static final boolean cdiParameterized(final TypeMirror t) { 791 return yieldsRawType(t); 792 } 793 794 // Can t *yield* a raw type? 795 // 796 // We say that to yield a raw type, t must be either: 797 // 798 // * an array type with a parameterized element type 799 // * a declared type with at least one type argument 800 private static final boolean yieldsRawType(final TypeMirror t) { 801 return parameterized(t) || t.getKind() == TypeKind.ARRAY && parameterized(elementType(t)); 802 } 803 804 // Is t a raw type, following the rules of the Java Language Specification, not CDI? 805 // 806 // A raw type is either "the erasure of a parameterized type" (so List<String>'s raw type is List, clearly not 807 // List<?>, and not List<E>) or "an array type whose element type is a raw type" (so List<String>[]'s raw type is 808 // List[]). (String is not a raw type; its element defines no type parameters.) 809 // 810 // No other type is a raw type. 811 // 812 // CDI gets confused and uses "raw" in different senses. The sense used here is that of the Java Language 813 // Specification. 814 private static final boolean raw(final TypeMirror t) { 815 return switch (t.getKind()) { 816 case ARRAY -> raw(elementType((ArrayType)t)); 817 case DECLARED -> generic(t) && ((DeclaredType)t).getTypeArguments().isEmpty(); 818 case TYPEVAR -> false; // called out explicitly just so you realize it 819 default -> false; 820 }; 821 } 822 823 // Is t a declared type that bears the supplied fully qualified name? 824 private static final boolean declaredTypeNamed(final TypeMirror t, final CharSequence n) { 825 return 826 t.getKind() == TypeKind.DECLARED && 827 named(((DeclaredType)t), n); 828 } 829 830 // Regardless of its reported TypeKind, does t's declaring TypeElement bear the supplied fully qualified name? 831 // 832 // Throws ClassCastException if the return value of t.asElement() is not a TypeElement. 833 private static final boolean named(final DeclaredType t, final CharSequence n) { 834 // (No getKind() check on purpose.) 835 return ((QualifiedNameable)t.asElement()).getQualifiedName().contentEquals(n); 836 } 837 838 // Do all supplied TypeMirrors, when used as type arguments, pass the test represented by the supplied Predicate? 839 private static final boolean allTypeArgumentsAre(final Iterable<? extends TypeMirror> typeArguments, final Predicate<? super TypeMirror> p) { 840 for (final TypeMirror t : typeArguments) { 841 if (!p.test(t)) { 842 return false; 843 } 844 } 845 return true; 846 } 847 848 // Is e a TypeElement representing java.lang.Object? 849 private static final boolean isJavaLangObject(final Element e) { 850 return 851 e.getKind() == ElementKind.CLASS && 852 ((QualifiedNameable)e).getQualifiedName().contentEquals("java.lang.Object"); 853 } 854 855 // Is t a DeclaredType whose asElement() method returns an Element representing java.lang.Object? 856 private static final boolean isJavaLangObject(final TypeMirror t) { 857 return 858 t.getKind() == TypeKind.DECLARED && 859 isJavaLangObject(((DeclaredType)t).asElement()); 860 } 861 862 // Is t an "actual type"? 863 // 864 // CDI mentions actual types but does not define what they are. This method attempts to divine and implement the 865 // intent. 866 // 867 // A comment in a closed bug report (CDI-502) 868 // (https://issues.redhat.com/browse/CDI-502?focusedId=13036118&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13036118) 869 // by one of the reference implementation's authors (Jozef Hartinger) provides the only definition: 870 // 871 // "An actual type is a type that is not a wildcard nor [sic] an unresolved [sic] type variable." 872 // 873 // The Java Language Specification does not mention anything about "actual types" or type variable "resolution". CDI 874 // does not mention anything about type variable "resolution". 875 // 876 // (Clearly type variable "resolution" must simply be the process of supplying a type argument for a type parameter.) 877 // 878 // Presumably the null type is not intended to be an actual type either. 879 // 880 // More strictly, therefore, the intent seems to be that an actual type is an array, declared or primitive type, and 881 // none other. 882 // 883 // See also: https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L213 884 private static final boolean actual(final TypeMirror t) { 885 return switch (t.getKind()) { 886 case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT -> true; 887 default -> false; 888 }; 889 } 890 891 // Returns the element type of t. 892 // 893 // The element type of an array type is the element type of its component type. 894 // 895 // The element type of every other kind of type is the type itself. 896 private static final TypeMirror elementType(final TypeMirror t) { 897 return t.getKind() == TypeKind.ARRAY ? elementType(((ArrayType)t).getComponentType()) : t; 898 } 899 900}