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(Domain.class.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(Domain.class.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(Domain.class.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(Domain.class.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(Domain.class.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(Domain.class.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(Domain.class.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(Domain.class.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(Domain.class.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[4 + typeArgumentCount];
529          final TypeMirror enclosingType = t.getEnclosingType();
530          // Call 
531          args[0] = MethodHandleDesc.ofMethod(VIRTUAL,
532                                              ClassDesc.of(Domain.class.getName()),
533                                              "declaredType",
534                                              MethodTypeDesc.of(CD_DeclaredType,
535                                                                CD_DeclaredType,
536                                                                CD_TypeElement,
537                                                                CD_TypeMirror.arrayType()));
538          args[1] = domainDesc;
539          args[2] = enclosingType.getKind() == TypeKind.NONE ? NULL : describe(enclosingType, d).orElse(null);
540          if (args[2] == null) {
541            yield Optional.empty();
542          }
543          args[3] = describe((TypeElement)t.asElement(), d).orElse(null);
544          if (args[3] == null) {
545            yield Optional.empty();
546          }
547          for (int i = 0; i < typeArgumentCount; i++) {
548            final int index = i + 3;
549            args[index] = describe(typeArguments.get(i), d).orElse(null);
550            if (args[index] == null) {
551              yield Optional.empty();
552            }
553          }
554          yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE, args));
555        }
556        default -> Optional.empty(); // could be an error type
557        };
558      }
559    }
560    };
561  }
562
563  /**
564   * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link
565   * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described.
566   *
567   * @param t the argument; may be {@code null}
568   *
569   * @param d the {@link Domain} from which the argument originated; must not be {@code null}
570   *
571   * @return a non-{@code null} {@link Optional}
572   *
573   * @exception NullPointerException if {@code d} is {@code null}
574   */
575  public static final Optional<? extends ConstantDesc> describe(final NoType t, final Domain d) {
576    return switch (t) {
577    case null -> Optional.of(NULL);
578    case Constable c -> c.describeConstable();
579    case ConstantDesc cd -> Optional.of(cd); // future proofing?
580    default -> {
581      final ConstantDesc domainDesc = d instanceof Constable c ? c.describeConstable().orElse(null) : null;
582      if (domainDesc == null) {
583        yield Optional.empty();
584      }
585      try (var lock = d.lock()) {
586        yield t.getKind().describeConstable()
587          .map(typeKindDesc -> DynamicConstantDesc.of(BSM_INVOKE,
588                                                      MethodHandleDesc.ofMethod(VIRTUAL,
589                                                                                ClassDesc.of(Domain.class.getName()),
590                                                                                "noType",
591                                                                                MethodTypeDesc.of(CD_NoType,
592                                                                                                  CD_TypeKind)),
593                                                      domainDesc,
594                                                      typeKindDesc));
595      }
596    }
597    };
598  }
599
600  /**
601   * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link
602   * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described.
603   *
604   * @param t the argument; may be {@code null}
605   *
606   * @param d the {@link Domain} from which the argument originated; must not be {@code null}
607   *
608   * @return a non-{@code null} {@link Optional}
609   *
610   * @exception NullPointerException if {@code d} is {@code null}
611   */
612  public static final Optional<? extends ConstantDesc> describe(final NullType t, final Domain d) {
613    return switch (t) {
614    case null -> Optional.of(NULL);
615    case Constable c -> c.describeConstable();
616    case ConstantDesc cd -> Optional.of(cd); // future proofing?
617    default -> (d instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty())
618      .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE,
619                                                MethodHandleDesc.ofMethod(VIRTUAL,
620                                                                          ClassDesc.of(Domain.class.getName()),
621                                                                          "nullType",
622                                                                          MethodTypeDesc.of(CD_NullType)),
623                                                domainDesc));
624    };
625  }
626
627  /**
628   * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link
629   * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described.
630   *
631   * @param t the argument; may be {@code null}
632   *
633   * @param d the {@link Domain} from which the argument originated; must not be {@code null}
634   *
635   * @return a non-{@code null} {@link Optional}
636   *
637   * @exception NullPointerException if {@code d} is {@code null}
638   */
639  public static final Optional<? extends ConstantDesc> describe(final PrimitiveType t, final Domain d) {
640    return switch (t) {
641    case null -> Optional.of(NULL);
642    case Constable c -> c.describeConstable();
643    case ConstantDesc cd -> Optional.of(cd); // future proofing?
644    default -> {
645      final ConstantDesc domainDesc = d instanceof Constable constableDomain ? constableDomain.describeConstable().orElse(null) : null;
646      if (domainDesc == null) {
647        yield Optional.empty();
648      }
649      try (var lock = d.lock()) {
650        yield t.getKind().describeConstable()
651          .map(typeKindDesc -> DynamicConstantDesc.of(BSM_INVOKE,
652                                                      MethodHandleDesc.ofMethod(VIRTUAL,
653                                                                                ClassDesc.of(Domain.class.getName()),
654                                                                                "primitiveType",
655                                                                                MethodTypeDesc.of(CD_PrimitiveType,
656                                                                                                  CD_TypeKind)),
657                                                      domainDesc,
658                                                      typeKindDesc));
659      }
660    }
661    };
662  }
663
664  /**
665   * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link
666   * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described.
667   *
668   * @param t the argument; may be {@code null}
669   *
670   * @param d the {@link Domain} from which the argument originated; must not be {@code null}
671   *
672   * @return a non-{@code null} {@link Optional}
673   *
674   * @exception NullPointerException if {@code d} is {@code null}
675   */
676  public static final Optional<? extends ConstantDesc> describe(final TypeVariable t, final Domain d) {
677    return switch (t) {
678    case null -> Optional.of(NULL);
679    case Constable c -> c.describeConstable();
680    case ConstantDesc cd -> Optional.of(cd); // future proofing?
681    default -> {
682      final ConstantDesc domainDesc = d instanceof Constable constableDomain ? constableDomain.describeConstable().orElse(null) : null;
683      if (domainDesc == null) {
684        yield Optional.empty();
685      }
686      try (var lock = d.lock()) {
687        final TypeParameterElement e = (TypeParameterElement)t.asElement();
688        final ConstantDesc parameterizableDesc = describe(e.getEnclosingElement(), d).orElse(null);
689        if (parameterizableDesc == null) {
690          yield Optional.empty();
691        }
692        final String name = d.toString(e.getSimpleName());
693        yield Optional.of(DynamicConstantDesc.of(BSM_INVOKE,
694                                                 MethodHandleDesc.ofMethod(VIRTUAL,
695                                                                           ClassDesc.of(Domain.class.getName()),
696                                                                           "typeVariable",
697                                                                           MethodTypeDesc.of(CD_TypeVariable,
698                                                                                             CD_Parameterizable,
699                                                                                             CD_CharSequence)),
700                                                 domainDesc,
701                                                 parameterizableDesc,
702                                                 name));
703      }
704    }
705    };
706  }
707
708  /**
709   * Returns a nominal descriptor for the supplied argument, presuming it to have originated from the supplied {@link
710   * Domain}, or an {@linkplain Optional#empty() empty} {@link Optional} if the supplied argument cannot be described.
711   *
712   * @param t the argument; may be {@code null}
713   *
714   * @param d the {@link Domain} from which the argument originated; must not be {@code null}
715   *
716   * @return a non-{@code null} {@link Optional}
717   *
718   * @exception NullPointerException if {@code d} is {@code null}
719   */
720  public static final Optional<? extends ConstantDesc> describe(final WildcardType t, final Domain d) {
721    return switch (t) {
722    case null -> Optional.of(NULL);
723    case Constable c -> c.describeConstable();
724    case ConstantDesc cd -> Optional.of(cd); // future proofing?
725    default -> {
726      try (var lock = d.lock()) {
727        yield describe(t.getExtendsBound(), d)
728          .flatMap(domainDesc -> describe(t.getExtendsBound(), d)
729                   .flatMap(extendsBoundDesc -> describe(t.getSuperBound(), d)
730                            .map(superBoundDesc -> DynamicConstantDesc.of(BSM_INVOKE,
731                                                                          MethodHandleDesc.ofMethod(VIRTUAL,
732                                                                                                    ClassDesc.of(Domain.class.getName()),
733                                                                                                    "wildcardType",
734                                                                                                    MethodTypeDesc.of(CD_WildcardType,
735                                                                                                                      CD_TypeMirror,
736                                                                                                                      CD_TypeMirror)),
737
738                                                                          domainDesc,
739                                                                          extendsBoundDesc,
740                                                                          superBoundDesc))));
741      }
742    }
743    };
744  }
745
746}