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