001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2026 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.construct.constant; 015 016import java.lang.constant.ClassDesc; 017import java.lang.constant.Constable; 018import java.lang.constant.ConstantDesc; 019import java.lang.constant.DynamicConstantDesc; 020import java.lang.constant.MethodTypeDesc; 021 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Optional; 026 027import java.util.function.Function; 028 029import javax.lang.model.AnnotatedConstruct; 030 031import javax.lang.model.element.Element; 032import javax.lang.model.element.ExecutableElement; 033import javax.lang.model.element.ModuleElement; 034import javax.lang.model.element.PackageElement; 035import javax.lang.model.element.Name; 036import javax.lang.model.element.RecordComponentElement; 037import javax.lang.model.element.TypeElement; 038import javax.lang.model.element.TypeParameterElement; 039import javax.lang.model.element.VariableElement; 040 041import javax.lang.model.type.ArrayType; 042import javax.lang.model.type.DeclaredType; 043import javax.lang.model.type.NoType; 044import javax.lang.model.type.NullType; 045import javax.lang.model.type.PrimitiveType; 046import javax.lang.model.type.TypeKind; 047import javax.lang.model.type.TypeMirror; 048import javax.lang.model.type.TypeVariable; 049import javax.lang.model.type.WildcardType; 050 051import org.microbean.construct.Domain; 052 053import static java.lang.constant.ConstantDescs.BSM_INVOKE; 054import static java.lang.constant.ConstantDescs.CD_List; 055import static java.lang.constant.ConstantDescs.CD_Map; 056import static java.lang.constant.ConstantDescs.CD_Object; 057import static java.lang.constant.ConstantDescs.NULL; 058 059import static java.lang.constant.DirectMethodHandleDesc.Kind.INTERFACE_STATIC; 060import static java.lang.constant.DirectMethodHandleDesc.Kind.VIRTUAL; 061 062import static java.lang.constant.MethodHandleDesc.ofMethod; 063 064import static java.util.Arrays.fill; 065 066import static org.microbean.construct.constant.ConstantDescs.CD_ArrayType; 067import static org.microbean.construct.constant.ConstantDescs.CD_CharSequence; 068import static org.microbean.construct.constant.ConstantDescs.CD_DeclaredType; 069import static org.microbean.construct.constant.ConstantDescs.CD_Element; 070import static org.microbean.construct.constant.ConstantDescs.CD_ExecutableElement; 071import static org.microbean.construct.constant.ConstantDescs.CD_ModuleElement; 072import static org.microbean.construct.constant.ConstantDescs.CD_Name; 073import static org.microbean.construct.constant.ConstantDescs.CD_NoType; 074import static org.microbean.construct.constant.ConstantDescs.CD_NullType; 075import static org.microbean.construct.constant.ConstantDescs.CD_PackageElement; 076import static org.microbean.construct.constant.ConstantDescs.CD_Parameterizable; 077import static org.microbean.construct.constant.ConstantDescs.CD_PrimitiveType; 078import static org.microbean.construct.constant.ConstantDescs.CD_RecordComponentElement; 079import static org.microbean.construct.constant.ConstantDescs.CD_TypeElement; 080import static org.microbean.construct.constant.ConstantDescs.CD_TypeKind; 081import static org.microbean.construct.constant.ConstantDescs.CD_TypeParameterElement; 082import static org.microbean.construct.constant.ConstantDescs.CD_TypeMirror; 083import static org.microbean.construct.constant.ConstantDescs.CD_TypeVariable; 084import static org.microbean.construct.constant.ConstantDescs.CD_VariableElement; 085import static org.microbean.construct.constant.ConstantDescs.CD_WildcardType; 086 087/** 088 * A utility class that returns nominal descriptors for constructs. 089 * 090 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 091 * 092 * @see #describe(Element, Domain) 093 * 094 * @see #describe(TypeMirror, Domain) 095 */ 096@SuppressWarnings("try") 097public final class Constables { 098 099 private Constables() { 100 super(); 101 } 102 103 /** 104 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 105 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 106 * 107 * @param n the argument; may be {@code null} 108 * 109 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 110 * 111 * @return a non-{@code null} {@link Optional} 112 * 113 * @exception NullPointerException if {@code d} is {@code null} 114 */ 115 public static final Optional<? extends ConstantDesc> describe(final Name n, final Domain d) { 116 return switch (n) { 117 case null -> Optional.of(NULL); 118 case Constable c -> c.describeConstable(); 119 case ConstantDesc cd -> Optional.of(cd); // future proofing? 120 default -> (d instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty()) 121 .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, 122 ofMethod(VIRTUAL, 123 Domain.class.describeConstable().orElseThrow(), 124 "name", 125 MethodTypeDesc.of(CD_Name, 126 CD_CharSequence)), 127 domainDesc, 128 d.toString(n))); 129 }; 130 } 131 132 /** 133 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 134 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 135 * 136 * @param ac the argument; may be {@code null} 137 * 138 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 139 * 140 * @return a non-{@code null} {@link Optional} 141 * 142 * @exception NullPointerException if {@code d} is {@code null} 143 */ 144 public static final Optional<? extends ConstantDesc> describe(final AnnotatedConstruct ac, final Domain d) { 145 return switch (ac) { 146 case null -> Optional.of(NULL); 147 case Constable c -> c.describeConstable(); 148 case ConstantDesc cd -> Optional.of(cd); // future proofing? 149 case Element e -> describe(e, d); 150 case TypeMirror t -> describe(t, d); 151 default -> Optional.empty(); 152 }; 153 } 154 155 /** 156 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 157 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 158 * 159 * @param e the argument; may be {@code null} 160 * 161 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 162 * 163 * @return a non-{@code null} {@link Optional} 164 * 165 * @exception NullPointerException if {@code d} is {@code null} 166 */ 167 public static final Optional<? extends ConstantDesc> describe(final Element e, final Domain d) { 168 return switch (e) { 169 case null -> Optional.of(NULL); 170 case Constable c -> c.describeConstable(); 171 case ConstantDesc cd -> Optional.of(cd); // future proofing? 172 default -> { 173 try (var lock = d.lock()) { 174 yield switch (e.getKind()) { 175 case ANNOTATION_TYPE, CLASS, ENUM, INTERFACE, RECORD -> describe((TypeElement)e, d); 176 case BINDING_VARIABLE, EXCEPTION_PARAMETER, LOCAL_VARIABLE, OTHER, RESOURCE_VARIABLE -> 177 // No way to get these from javax.lang.model.Elements 178 Optional.empty(); 179 case CONSTRUCTOR, INSTANCE_INIT, METHOD, STATIC_INIT -> describe((ExecutableElement)e, d); 180 case ENUM_CONSTANT, FIELD, PARAMETER -> describe((VariableElement)e, d); 181 case MODULE -> describe((ModuleElement)e, d); 182 case PACKAGE -> describe((PackageElement)e, d); 183 case RECORD_COMPONENT -> describe((RecordComponentElement)e, d); 184 case TYPE_PARAMETER -> describe((TypeParameterElement)e, d); 185 }; 186 } 187 } 188 }; 189 } 190 191 /** 192 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 193 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 194 * 195 * @param e the argument; may be {@code null} 196 * 197 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 198 * 199 * @return a non-{@code null} {@link Optional} 200 * 201 * @exception NullPointerException if {@code d} is {@code null} 202 */ 203 public static final Optional<? extends ConstantDesc> describe(final ExecutableElement e, final Domain d) { 204 return switch (e) { 205 case null -> Optional.of(NULL); 206 case Constable c -> c.describeConstable(); 207 case ConstantDesc cd -> Optional.of(cd); // future proofing? 208 default -> { 209 final ConstantDesc domainDesc = d instanceof Constable c ? c.describeConstable().orElse(null) : null; 210 if (domainDesc == null) { 211 yield Optional.empty(); 212 } 213 try (var lock = d.lock()) { 214 // Trying to do this via flatMap etc. simply will not work because type inference does not work properly, even 215 // with hints/coercion. Feel free to try again, but make sure you keep this implementation around to revert 216 // back to. 217 final List<? extends VariableElement> parameters = e.getParameters(); 218 final int parameterCount = parameters.size(); 219 final ConstantDesc[] args = new ConstantDesc[5 + parameterCount]; 220 args[0] = 221 ofMethod(VIRTUAL, 222 Domain.class.describeConstable().orElseThrow(), 223 "executableElement", 224 MethodTypeDesc.of(CD_ExecutableElement, 225 CD_TypeElement, 226 CD_TypeMirror, 227 CD_CharSequence, 228 CD_TypeMirror.arrayType())); 229 args[1] = domainDesc; 230 args[2] = describe(e.getEnclosingElement(), d).orElse(null); 231 if (args[2] == null) { 232 yield Optional.empty(); 233 } 234 args[3] = describe(e.getReturnType(), d).orElse(null); 235 if (args[3] == null) { 236 yield Optional.empty(); 237 } 238 args[4] = describe(e.getSimpleName(), d).orElse(null); 239 if (args[4] == null) { 240 yield Optional.empty(); 241 } 242 for (int i = 0; i < parameterCount; i++) { 243 int index = i + 5; 244 args[index] = describe(parameters.get(i).asType(), d).orElse(null); 245 if (args[index] == null) { 246 yield Optional.empty(); 247 } 248 } 249 yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE, args)); 250 } 251 } 252 }; 253 } 254 255 /** 256 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 257 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 258 * 259 * @param e the argument; may be {@code null} 260 * 261 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 262 * 263 * @return a non-{@code null} {@link Optional} 264 * 265 * @exception NullPointerException if {@code d} is {@code null} 266 */ 267 public static final Optional<? extends ConstantDesc> describe(final ModuleElement e, final Domain d) { 268 return switch (e) { 269 case null -> Optional.of(NULL); 270 case Constable c -> c.describeConstable(); 271 case ConstantDesc cd -> Optional.of(cd); // future proofing? 272 default -> describe(e.getQualifiedName(), d) // getQualifiedName() does not cause symbol completion 273 .map(nameDesc -> DynamicConstantDesc.of(BSM_INVOKE, 274 ofMethod(VIRTUAL, 275 Domain.class.describeConstable().orElseThrow(), 276 "moduleElement", 277 MethodTypeDesc.of(CD_ModuleElement, 278 CD_CharSequence)), 279 ((Constable)d).describeConstable().orElseThrow(), 280 nameDesc)); 281 }; 282 } 283 284 /** 285 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 286 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 287 * 288 * @param e the argument; may be {@code null} 289 * 290 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 291 * 292 * @return a non-{@code null} {@link Optional} 293 * 294 * @exception NullPointerException if {@code d} is {@code null} 295 */ 296 public static final Optional<? extends ConstantDesc> describe(final PackageElement e, final Domain d) { 297 return switch (e) { 298 case null -> Optional.of(NULL); 299 case Constable c -> c.describeConstable(); 300 case ConstantDesc cd -> Optional.of(cd); // future proofing? 301 default -> describe(e.getQualifiedName(), d) // getQualifiedName() does not cause symbol completion 302 .map(nameDesc -> DynamicConstantDesc.of(BSM_INVOKE, 303 ofMethod(VIRTUAL, 304 Domain.class.describeConstable().orElseThrow(), 305 "packageElement", 306 MethodTypeDesc.of(CD_PackageElement, 307 CD_CharSequence)), 308 ((Constable)d).describeConstable().orElseThrow(), 309 nameDesc)); 310 }; 311 } 312 313 /** 314 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 315 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 316 * 317 * @param e the argument; may be {@code null} 318 * 319 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 320 * 321 * @return a non-{@code null} {@link Optional} 322 * 323 * @exception NullPointerException if {@code d} is {@code null} 324 */ 325 public static final Optional<? extends ConstantDesc> describe(final TypeElement e, final Domain d) { 326 return switch (e) { 327 case null -> Optional.of(NULL); 328 case Constable c -> c.describeConstable(); 329 case ConstantDesc cd -> Optional.of(cd); // future proofing? 330 default -> describe(e.getQualifiedName(), d) // getQualifiedName() does not cause symbol completion 331 .map(nameDesc -> DynamicConstantDesc.of(BSM_INVOKE, 332 ofMethod(VIRTUAL, 333 Domain.class.describeConstable().orElseThrow(), 334 "typeElement", 335 MethodTypeDesc.of(CD_TypeElement, 336 CD_CharSequence)), 337 ((Constable)d).describeConstable().orElseThrow(), 338 nameDesc)); 339 }; 340 } 341 342 /** 343 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 344 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 345 * 346 * @param e the argument; may be {@code null} 347 * 348 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 349 * 350 * @return a non-{@code null} {@link Optional} 351 * 352 * @exception NullPointerException if {@code d} is {@code null} 353 */ 354 public static final Optional<? extends ConstantDesc> describe(final TypeParameterElement e, final Domain d) { 355 return switch (e) { 356 case null -> Optional.of(NULL); 357 case Constable c -> c.describeConstable(); 358 case ConstantDesc cd -> Optional.of(cd); // future proofing? 359 default -> { 360 try (var lock = d.lock()) { 361 yield describe(e.getEnclosingElement(), d) 362 .flatMap(parameterizableDesc -> describe(e.getSimpleName(), d) 363 .map(nameDesc -> DynamicConstantDesc.of(BSM_INVOKE, 364 ofMethod(VIRTUAL, 365 Domain.class.describeConstable().orElseThrow(), 366 "typeParameterElement", 367 MethodTypeDesc.of(CD_TypeParameterElement, 368 CD_Parameterizable, 369 CD_Name)), 370 ((Constable)d).describeConstable().orElseThrow(), 371 parameterizableDesc, 372 nameDesc))); 373 } 374 } 375 }; 376 } 377 378 /** 379 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 380 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 381 * 382 * @param e the argument; may be {@code null} 383 * 384 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 385 * 386 * @return a non-{@code null} {@link Optional} 387 * 388 * @exception NullPointerException if {@code d} is {@code null} 389 */ 390 public static final Optional<? extends ConstantDesc> describe(final RecordComponentElement e, final Domain d) { 391 return switch (e) { 392 case null -> Optional.of(NULL); 393 case Constable c -> c.describeConstable(); 394 case ConstantDesc cd -> Optional.of(cd); // future proofing? 395 default -> { 396 try (var lock = d.lock()) { 397 yield describe((TypeElement)e.getEnclosingElement(), d) 398 .map(executableDesc -> DynamicConstantDesc.of(BSM_INVOKE, 399 ofMethod(VIRTUAL, 400 Domain.class.describeConstable().orElseThrow(), 401 "recordComponentElement", 402 MethodTypeDesc.of(CD_RecordComponentElement, 403 CD_ExecutableElement)), 404 ((Constable)d).describeConstable().orElseThrow(), 405 executableDesc)); 406 } 407 } 408 }; 409 } 410 411 /** 412 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 413 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 414 * 415 * @param e the argument; may be {@code null} 416 * 417 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 418 * 419 * @return a non-{@code null} {@link Optional} 420 * 421 * @exception NullPointerException if {@code d} is {@code null} 422 */ 423 public static final Optional<? extends ConstantDesc> describe(final VariableElement e, final Domain d) { 424 return switch (e) { 425 case null -> Optional.of(NULL); 426 case Constable c -> c.describeConstable(); 427 case ConstantDesc cd -> Optional.of(cd); // future proofing? 428 default -> { 429 try (var lock = d.lock()) { 430 yield describe(e.getSimpleName(), d) 431 .flatMap(nameDesc -> describe(e.getEnclosingElement(), d) 432 .map(enclosingElementDesc -> DynamicConstantDesc.of(BSM_INVOKE, 433 ofMethod(VIRTUAL, 434 Domain.class.describeConstable().orElseThrow(), 435 "variableElement", 436 MethodTypeDesc.of(CD_VariableElement, 437 CD_Element, 438 CD_CharSequence)), 439 ((Constable)d).describeConstable().orElseThrow(), 440 enclosingElementDesc, 441 nameDesc))); 442 } 443 } 444 }; 445 } 446 447 /** 448 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 449 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 450 * 451 * @param t the argument; may be {@code null} 452 * 453 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 454 * 455 * @return a non-{@code null} {@link Optional} 456 * 457 * @exception NullPointerException if {@code d} is {@code null} 458 */ 459 public static final Optional<? extends ConstantDesc> describe(final TypeMirror t, final Domain d) { 460 return switch (t) { 461 case null -> Optional.of(NULL); 462 case Constable c -> c.describeConstable(); 463 case ConstantDesc cd -> Optional.of(cd); // future proofing? 464 default -> { 465 try (var lock = d.lock()) { 466 yield switch (t.getKind()) { 467 case ARRAY -> describe((ArrayType)t, d); 468 case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> describe((PrimitiveType)t, d); 469 case DECLARED -> describe((DeclaredType)t, d); 470 case EXECUTABLE, INTERSECTION, UNION -> Optional.empty(); // No way to get these from javax.lang.model.util.Types 471 case ERROR, OTHER -> Optional.empty(); 472 case MODULE, NONE, PACKAGE, VOID -> describe((NoType)t, d); 473 case NULL -> describe((NullType)t, d); 474 case TYPEVAR -> describe((TypeVariable)t, d); // Prefer working with TypeParameterElement instead 475 case WILDCARD -> describe((WildcardType)t, d); 476 }; 477 } 478 } 479 }; 480 } 481 482 /** 483 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 484 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 485 * 486 * @param t the argument; may be {@code null} 487 * 488 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 489 * 490 * @return a non-{@code null} {@link Optional} 491 * 492 * @exception NullPointerException if {@code d} is {@code null} 493 */ 494 public static final Optional<? extends ConstantDesc> describe(final ArrayType t, final Domain d) { 495 return switch (t) { 496 case null -> Optional.of(NULL); 497 case Constable c -> c.describeConstable(); 498 case ConstantDesc cd -> Optional.of(cd); // future proofing? 499 default -> { 500 try (var lock = d.lock()) { 501 yield describe(t.getComponentType(), d) 502 .map(componentTypeDesc -> DynamicConstantDesc.of(BSM_INVOKE, 503 ofMethod(VIRTUAL, 504 Domain.class.describeConstable().orElseThrow(), 505 "arrayTypeOf", 506 MethodTypeDesc.of(CD_ArrayType, 507 CD_TypeMirror)), 508 ((Constable)d).describeConstable().orElseThrow(), 509 componentTypeDesc)); 510 } 511 } 512 }; 513 } 514 515 /** 516 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 517 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 518 * 519 * @param t the argument; may be {@code null} 520 * 521 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 522 * 523 * @return a non-{@code null} {@link Optional} 524 * 525 * @exception NullPointerException if {@code d} is {@code null} 526 */ 527 public static final Optional<? extends ConstantDesc> describe(final DeclaredType t, final Domain d) { 528 return switch (t) { 529 case null -> Optional.of(NULL); 530 case Constable c -> c.describeConstable(); 531 case ConstantDesc cd -> Optional.of(cd); // future proofing? 532 default -> { 533 final ConstantDesc domainDesc = d instanceof Constable constableDomain ? constableDomain.describeConstable().orElse(null) : null; 534 if (domainDesc == null) { 535 yield Optional.empty(); 536 } 537 try (var lock = d.lock()) { 538 yield switch (t.getKind()) { 539 case DECLARED -> { 540 final List<? extends TypeMirror> typeArguments = t.getTypeArguments(); 541 final int typeArgumentCount = typeArguments.size(); 542 final ConstantDesc[] args = new ConstantDesc[4 + typeArgumentCount]; 543 final TypeMirror enclosingType = t.getEnclosingType(); 544 // Call 545 args[0] = ofMethod(VIRTUAL, 546 Domain.class.describeConstable().orElseThrow(), 547 "declaredType", 548 MethodTypeDesc.of(CD_DeclaredType, 549 CD_DeclaredType, 550 CD_TypeElement, 551 CD_TypeMirror.arrayType())); 552 args[1] = domainDesc; 553 args[2] = enclosingType.getKind() == TypeKind.NONE ? NULL : describe(enclosingType, d).orElse(null); 554 if (args[2] == null) { 555 yield Optional.empty(); 556 } 557 args[3] = describe((TypeElement)t.asElement(), d).orElse(null); 558 if (args[3] == null) { 559 yield Optional.empty(); 560 } 561 for (int i = 0; i < typeArgumentCount; i++) { 562 final int index = i + 4; 563 args[index] = describe(typeArguments.get(i), d).orElse(null); 564 if (args[index] == null) { 565 yield Optional.empty(); 566 } 567 } 568 yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE, args)); 569 } 570 default -> Optional.empty(); // could be an error type 571 }; 572 } 573 } 574 }; 575 } 576 577 /** 578 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 579 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 580 * 581 * @param t the argument; may be {@code null} 582 * 583 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 584 * 585 * @return a non-{@code null} {@link Optional} 586 * 587 * @exception NullPointerException if {@code d} is {@code null} 588 */ 589 public static final Optional<? extends ConstantDesc> describe(final NoType t, final Domain d) { 590 return switch (t) { 591 case null -> Optional.of(NULL); 592 case Constable c -> c.describeConstable(); 593 case ConstantDesc cd -> Optional.of(cd); // future proofing? 594 default -> { 595 final ConstantDesc domainDesc = d instanceof Constable c ? c.describeConstable().orElse(null) : null; 596 if (domainDesc == null) { 597 yield Optional.empty(); 598 } 599 try (var lock = d.lock()) { 600 yield t.getKind().describeConstable() 601 .map(typeKindDesc -> DynamicConstantDesc.of(BSM_INVOKE, 602 ofMethod(VIRTUAL, 603 Domain.class.describeConstable().orElseThrow(), 604 "noType", 605 MethodTypeDesc.of(CD_NoType, 606 CD_TypeKind)), 607 domainDesc, 608 typeKindDesc)); 609 } 610 } 611 }; 612 } 613 614 /** 615 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 616 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 617 * 618 * @param t the argument; may be {@code null} 619 * 620 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 621 * 622 * @return a non-{@code null} {@link Optional} 623 * 624 * @exception NullPointerException if {@code d} is {@code null} 625 */ 626 public static final Optional<? extends ConstantDesc> describe(final NullType t, final Domain d) { 627 return switch (t) { 628 case null -> Optional.of(NULL); 629 case Constable c -> c.describeConstable(); 630 case ConstantDesc cd -> Optional.of(cd); // future proofing? 631 default -> (d instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty()) 632 .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, 633 ofMethod(VIRTUAL, 634 Domain.class.describeConstable().orElseThrow(), 635 "nullType", 636 MethodTypeDesc.of(CD_NullType)), 637 domainDesc)); 638 }; 639 } 640 641 /** 642 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 643 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 644 * 645 * @param t the argument; may be {@code null} 646 * 647 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 648 * 649 * @return a non-{@code null} {@link Optional} 650 * 651 * @exception NullPointerException if {@code d} is {@code null} 652 */ 653 public static final Optional<? extends ConstantDesc> describe(final PrimitiveType t, final Domain d) { 654 return switch (t) { 655 case null -> Optional.of(NULL); 656 case Constable c -> c.describeConstable(); 657 case ConstantDesc cd -> Optional.of(cd); // future proofing? 658 default -> { 659 final ConstantDesc domainDesc = d instanceof Constable constableDomain ? constableDomain.describeConstable().orElse(null) : null; 660 if (domainDesc == null) { 661 yield Optional.empty(); 662 } 663 try (var lock = d.lock()) { 664 yield t.getKind().describeConstable() 665 .map(typeKindDesc -> DynamicConstantDesc.of(BSM_INVOKE, 666 ofMethod(VIRTUAL, 667 Domain.class.describeConstable().orElseThrow(), 668 "primitiveType", 669 MethodTypeDesc.of(CD_PrimitiveType, 670 CD_TypeKind)), 671 domainDesc, 672 typeKindDesc)); 673 } 674 } 675 }; 676 } 677 678 /** 679 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 680 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 681 * 682 * @param t the argument; may be {@code null} 683 * 684 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 685 * 686 * @return a non-{@code null} {@link Optional} 687 * 688 * @exception NullPointerException if {@code d} is {@code null} 689 */ 690 public static final Optional<? extends ConstantDesc> describe(final TypeVariable t, final Domain d) { 691 return switch (t) { 692 case null -> Optional.of(NULL); 693 case Constable c -> c.describeConstable(); 694 case ConstantDesc cd -> Optional.of(cd); // future proofing? 695 default -> { 696 final ConstantDesc domainDesc = d instanceof Constable constableDomain ? constableDomain.describeConstable().orElse(null) : null; 697 if (domainDesc == null) { 698 yield Optional.empty(); 699 } 700 try (var lock = d.lock()) { 701 final TypeParameterElement e = (TypeParameterElement)t.asElement(); 702 final ConstantDesc parameterizableDesc = describe(e.getEnclosingElement(), d).orElse(null); 703 if (parameterizableDesc == null) { 704 yield Optional.empty(); 705 } 706 final String name = d.toString(e.getSimpleName()); 707 yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE, 708 ofMethod(VIRTUAL, 709 Domain.class.describeConstable().orElseThrow(), 710 "typeVariable", 711 MethodTypeDesc.of(CD_TypeVariable, 712 CD_Parameterizable, 713 CD_CharSequence)), 714 domainDesc, 715 parameterizableDesc, 716 name)); 717 } 718 } 719 }; 720 } 721 722 /** 723 * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link 724 * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described. 725 * 726 * @param t the argument; may be {@code null} 727 * 728 * @param d the {@link Domain} from which the argument originated; must not be {@code null} 729 * 730 * @return a non-{@code null} {@link Optional} 731 * 732 * @exception NullPointerException if {@code d} is {@code null} 733 */ 734 public static final Optional<? extends ConstantDesc> describe(final WildcardType t, final Domain d) { 735 return switch (t) { 736 case null -> Optional.of(NULL); 737 case Constable c -> c.describeConstable(); 738 case ConstantDesc cd -> Optional.of(cd); // future proofing? 739 default -> { 740 try (var lock = d.lock()) { 741 yield describe(t.getExtendsBound(), d) 742 .flatMap(domainDesc -> describe(t.getExtendsBound(), d) 743 .flatMap(extendsBoundDesc -> describe(t.getSuperBound(), d) 744 .map(superBoundDesc -> DynamicConstantDesc.of(BSM_INVOKE, 745 ofMethod(VIRTUAL, 746 Domain.class.describeConstable().orElseThrow(), 747 "wildcardType", 748 MethodTypeDesc.of(CD_WildcardType, 749 CD_TypeMirror, 750 CD_TypeMirror)), 751 domainDesc, 752 extendsBoundDesc, 753 superBoundDesc)))); 754 } 755 } 756 }; 757 } 758 759 /** 760 * Returns a non-{@code null} nominal descriptor for the supplied {@link List}, or an {@linkplain Optional#empty() 761 * empty} {@link Optional} if the supplied {@link List} cannot be described. 762 * 763 * @param list a {@link List} to be described; may be {@code null}; if non-{@code null} <strong>must be immutable and 764 * must not contain {@code null} elements</strong> or undefined behavior will result 765 * 766 * @return a non-{@code null} {@link Optional} 767 * 768 * @see #describe(List, Function) 769 */ 770 public static final Optional<? extends ConstantDesc> describe(final List<?> list) { 771 return describe(list, e -> e instanceof Constable c ? c.describeConstable() : Optional.empty()); 772 } 773 774 /** 775 * Returns a nominal descriptor for the supplied {@link List}, or an {@linkplain Optional#empty() empty} {@link 776 * Optional} if the supplied {@link List} cannot be described. 777 * 778 * @param <E> the supplied {@code list}'s element type 779 * 780 * @param list a {@link List} to be described; may be {@code null}; if non-{@code null} <strong>must be immutable and 781 * must not contain {@code null} elements</strong> or undefined behavior will result 782 * 783 * @param f a {@link Function} that accepts an element from the supplied {@code list} and returns a non-{@code null} 784 * {@link Optional} housing a nominal descriptor for it, or a non-{@code null} {@linkplain Optional#empty() empty} 785 * {@link Optional} if the element cannot be described; must not be {@code null} 786 * 787 * @return a non-{@code null} {@link Optional} 788 * 789 * @exception NullPointerException if {@code f} is {@code null} and needs to be used 790 * 791 * @see List#of(Object...) 792 */ 793 public static final <E> Optional<? extends ConstantDesc> describe(final List<? extends E> list, 794 final Function<? super E, ? extends Optional<? extends ConstantDesc>> f) { 795 return switch (list) { 796 case null -> Optional.of(NULL); 797 case Constable c -> c.describeConstable(); 798 case ConstantDesc cd -> Optional.of(cd); // future proofing? 799 case List<?> el when el.isEmpty() -> 800 Optional.of(DynamicConstantDesc.of(BSM_INVOKE, 801 ofMethod(INTERFACE_STATIC, 802 CD_List, 803 "of", 804 MethodTypeDesc.of(CD_List)))); 805 default -> { 806 int size = list.size(); 807 assert size > 0; 808 final MethodTypeDesc ofMethodTypeDesc; 809 if (size <= 10) { 810 // List.of() has explicit polymorphic overrides for parameter counts of up to 10, inclusive. 811 final ClassDesc[] parameterArray = new ClassDesc[size]; 812 fill(parameterArray, CD_Object); 813 ofMethodTypeDesc = MethodTypeDesc.of(CD_List, parameterArray); 814 } else { 815 // After 10 parameters, List.of() falls back to varargs. 816 ofMethodTypeDesc = MethodTypeDesc.of(CD_List, CD_Object.arrayType()); 817 } 818 final ConstantDesc[] args = new ConstantDesc[size + 1]; 819 args[0] = ofMethod(INTERFACE_STATIC, CD_List, "of", ofMethodTypeDesc); 820 for (int i = 0; i < size; i++) { 821 final ConstantDesc eDesc = f.apply(list.get(i)).orElse(null); 822 if (eDesc == null) { 823 yield Optional.empty(); 824 } 825 args[i + 1] = eDesc; 826 } 827 yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE, args)); 828 } 829 }; 830 } 831 832 /** 833 * Returns a nominal descriptor for the supplied {@link Map}, or an {@linkplain Optional#empty() empty} {@link 834 * Optional} if the supplied {@link Map} cannot be described. 835 * 836 * @param <K> the supplied {@code map}'s key type 837 * 838 * @param <V> the supplied {@code map}'s value type 839 * 840 * @param map a {@link Map} to be described; may be {@code null}; if non-{@code null} <strong>must be immutable and 841 * must not contain {@code null} keys or values</strong> or undefined behavior will result 842 * 843 * @param kf a {@link Function} that accepts a key from the supplied {@code map} and returns a non-{@code null} {@link 844 * Optional} housing a nominal descriptor for it, or a non-{@code null} {@linkplain Optional#empty() empty} {@link 845 * Optional} if the key cannot be described; must not be {@code null} 846 * 847 * @param vf a {@link Function} that accepts a value from the supplied {@code map} and returns a non-{@code null} 848 * {@link Optional} housing a nominal descriptor for it, or a non-{@code null} {@linkplain Optional#empty() empty} 849 * {@link Optional} if the value cannot be described; must not be {@code null} 850 * 851 * @return a non-{@code null} {@link Optional} 852 * 853 * @exception NullPointerException if {@code kf} or {@code vf} is {@code null} and needs to be used 854 * 855 * @see Map#of(Object, Object) 856 */ 857 public static <K, V> Optional<? extends ConstantDesc> describe(final Map<? extends K, ? extends V> map, 858 final Function<? super K, ? extends Optional<? extends ConstantDesc>> kf, 859 final Function<? super V, ? extends Optional<? extends ConstantDesc>> vf) { 860 return switch (map) { 861 case null -> Optional.of(NULL); 862 case Constable c -> c.describeConstable(); 863 case ConstantDesc c -> Optional.of(c); // future proofing? 864 case Map<?, ?> em when em.isEmpty() -> 865 Optional.of(DynamicConstantDesc.of(BSM_INVOKE, 866 ofMethod(INTERFACE_STATIC, 867 CD_Map, 868 "of", 869 MethodTypeDesc.of(CD_Map)))); 870 default -> { 871 int size = 2 * map.size(); // 2 * entry size to account for key and value 872 assert size > 0; 873 final MethodTypeDesc ofMethodTypeDesc; 874 if (size <= 20) { 875 // Map.of() has explicit polymorphic overrides for (even) parameter counts of up to 20, inclusive. 876 final ClassDesc[] parameterArray = new ClassDesc[size]; 877 fill(parameterArray, CD_Object); 878 ofMethodTypeDesc = MethodTypeDesc.of(CD_Map, parameterArray); 879 } else { 880 // After 20 parameters, Map.of() falls back to varargs. 881 ofMethodTypeDesc = MethodTypeDesc.of(CD_Map, CD_Object.arrayType()); 882 } 883 final ConstantDesc[] args = new ConstantDesc[size + 1]; 884 args[0] = ofMethod(INTERFACE_STATIC, CD_Map, "of", ofMethodTypeDesc); 885 int i = 1; 886 for (final Entry<? extends K, ? extends V> e : map.entrySet()) { 887 final ConstantDesc kDesc = kf.apply(e.getKey()).orElse(null); 888 if (kDesc == null) { 889 yield Optional.empty(); 890 } 891 final ConstantDesc vDesc = vf.apply(e.getValue()).orElse(null); 892 if (vDesc == null) { 893 yield Optional.empty(); 894 } 895 args[i++] = kDesc; 896 args[i++] = vDesc; 897 } 898 yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE, args)); 899 } 900 }; 901 } 902 903}