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