From 24b3e67253c07ee448cae88ba31ad1b9c2ab78f1 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Sat, 28 Sep 2024 18:41:47 -0700 Subject: [PATCH] Interim changes (#21) Signed-off-by: Laird Nelson --- README.md | 2 +- .../main/java/org/microbean/lang/Lang.java | 17 +++- .../microbean/lang/TypeAndElementSource.java | 98 ++++++++++--------- .../lang/visitor/AdaptingVisitor.java | 55 ++++++----- .../lang/visitor/PrecedesPredicate.java | 4 +- .../lang/visitor/SupertypeVisitor.java | 10 +- .../java/org/microbean/lang/TestErase.java | 6 +- .../microbean/lang/TestSameTypeVisitor.java | 6 +- .../microbean/lang/TestSupertypeVisitor.java | 6 +- .../org/microbean/lang/TestTypeClosure.java | 10 +- .../lang/visitor/TestTypeClosureVisitor.java | 2 +- 11 files changed, 120 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index d3bfb368..a30803cc 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Maven dependency: org.microbean microbean-lang - 0.0.19 + 0.0.20 ``` diff --git a/lang/src/main/java/org/microbean/lang/Lang.java b/lang/src/main/java/org/microbean/lang/Lang.java index 4fbde8a9..953e3628 100644 --- a/lang/src/main/java/org/microbean/lang/Lang.java +++ b/lang/src/main/java/org/microbean/lang/Lang.java @@ -2127,8 +2127,8 @@ private static final TypeMirror[] typeArray(final Type[] ts) { /** * Asynchronously and idempotently initializes the {@link Lang} class for use. * - *

This method is automatically called when appropriate, but is {@code public} to support eager initialization use - * cases.

+ *

This method is automatically called by the internals of this class when appropriate, but is {@code public} to + * support eager initialization use cases.

*/ // Idempotent. public static final void initialize() { @@ -2292,6 +2292,11 @@ public final boolean sameType(final TypeMirror t, final TypeMirror s) { return t == s || Lang.sameType(t, s); } + @Override + public final boolean subtype(final TypeMirror t, final TypeMirror s) { + return t == s || Lang.subtype(t, s); + } + @Override public final TypeElement typeElement(final CharSequence canonicalName) { return Lang.typeElement(canonicalName); @@ -2466,8 +2471,10 @@ public final void run() { options.add("-verbose"); } - final DiagnosticLogger diagnosticLogger = new DiagnosticLogger(Locale.getDefault()); - final StandardJavaFileManager sjfm = jc.getStandardFileManager(diagnosticLogger, Locale.getDefault(), Charset.defaultCharset()); + final Locale defaultLocale = Locale.getDefault(); + + final DiagnosticLogger diagnosticLogger = new DiagnosticLogger(defaultLocale); + final StandardJavaFileManager sjfm = jc.getStandardFileManager(diagnosticLogger, defaultLocale, Charset.defaultCharset()); // (Any "loading" is actually performed by, e.g. com.sun.tools.javac.jvm.ClassReader.fillIn(), not reflective // machinery. Once a class has been so loaded, com.sun.tools.javac.code.Symtab#getClass(ModuleSymbol, Name) will @@ -2480,7 +2487,7 @@ public final void run() { List.of("java.lang.annotation.RetentionPolicy"), // arbitrary, but loads the least amount of stuff up front null); // compilation units; null means we aren't actually compiling anything task.setProcessors(List.of(new P())); - task.setLocale(Locale.getDefault()); + task.setLocale(defaultLocale); task.addModules(additionalRootModuleNames); if (LOGGER.isLoggable(DEBUG)) { diff --git a/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java b/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java index 5a313ffd..4ac5b863 100644 --- a/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java +++ b/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java @@ -74,6 +74,8 @@ public DeclaredType declaredType(final TypeElement typeElement, public boolean sameType(final TypeMirror t, final TypeMirror s); + public boolean subtype(final TypeMirror t, final TypeMirror s); + public TypeElement typeElement(final CharSequence canonicalName); // Note that Elements#getTypeElement(ModuleElement, CharSequence), to which this basically ultimately delegates, says @@ -91,49 +93,48 @@ public DeclaredType declaredType(final TypeElement typeElement, */ - public default ArrayType arrayType(final Class c) { - if (!c.isArray()) { - throw new IllegalArgumentException("c: " + c); - } - return this.arrayTypeOf(this.type(c.getComponentType())); - } - - public default ArrayType arrayType(final GenericArrayType g) { - return this.arrayTypeOf(this.type(g.getGenericComponentType())); + public default ArrayType arrayType(final Type t) { + return switch (t) { + case null -> throw new NullPointerException("t"); + case Class c when c.isArray() -> this.arrayTypeOf(this.type(c.getComponentType())); + case GenericArrayType g -> this.arrayTypeOf(this.type(g.getGenericComponentType())); + default -> throw new IllegalArgumentException("t: " + t); + }; } public default DeclaredType declaredType(final CharSequence canonicalName) { return this.declaredType(this.typeElement(canonicalName)); } - public default DeclaredType declaredType(final Class c) { - if (c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass()) { - throw new IllegalArgumentException("c: " + c); + public default DeclaredType declaredType(final Type t) { + return switch (t) { + case null -> throw new NullPointerException("t"); + case Class c when c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass() + -> throw new IllegalArgumentException("t: " + t); + case Class c -> { + final Class ec = c.getEnclosingClass(); + yield ec == null ? this.declaredType(this.typeElement(c)) : this.declaredType(this.declaredType(ec), this.typeElement(c)); } - final Class ec = c.getEnclosingClass(); - return this.declaredType(ec == null ? null : this.declaredType(ec), this.typeElement(c)); - } - - public default DeclaredType declaredType(final ParameterizedType p) { - return - this.declaredType(switch (p.getOwnerType()) { - case null -> null; - case Class c -> this.declaredType(c); - case ParameterizedType pt -> this.declaredType(pt); - default -> throw new IllegalArgumentException("p: " + p); - }, - this.typeElement((Class)p.getRawType()), - this.typeArray(p.getActualTypeArguments())); + case ParameterizedType p -> this.declaredType(switch (p.getOwnerType()) { + case null -> null; + case Class c -> this.declaredType(c); + case ParameterizedType pt -> this.declaredType(pt); + default -> throw new IllegalArgumentException("t: " + t); + }, + this.typeElement(p.getRawType()), + this.typeArray(p.getActualTypeArguments())); + default -> throw new IllegalArgumentException("t: " + t); + }; } public default Optional describeConstable(final AnnotatedConstruct a) { return switch (a) { - case null -> Optional.of(NULL); - case Constable c -> c.describeConstable(); + case null -> Optional.of(NULL); + case Constable c -> c.describeConstable(); case ConstantDesc cd -> Optional.of(cd); // future proofing - case TypeMirror t -> Optional.empty(); - case Element e -> Optional.empty(); - default -> throw new IllegalArgumentException("a: " + a); + case TypeMirror t -> Optional.empty(); + case Element e -> Optional.empty(); + default -> throw new IllegalArgumentException("a: " + a); }; } @@ -146,16 +147,17 @@ public default PrimitiveType primitiveType(final Class c) { public default TypeMirror type(final Type t) { return switch (t) { - case null -> throw new NullPointerException(); - case Class c when c == void.class -> this.noType(TypeKind.VOID); - case Class c when c.isArray() -> this.arrayType(c); - case Class c when c.isPrimitive() -> this.primitiveType(c); - case Class c -> this.declaredType(c); - case ParameterizedType p -> this.declaredType(p); - case GenericArrayType g -> this.arrayType(g); - case java.lang.reflect.TypeVariable tv -> this.typeVariable(tv); - case java.lang.reflect.WildcardType w -> this.wildcardType(w); - default -> throw new IllegalArgumentException("t: " + t); + case null -> throw new NullPointerException(); + case Class c when c == void.class -> this.noType(TypeKind.VOID); + case Class c when c.isArray() -> this.arrayType(c); + case Class c when c.isPrimitive() -> this.primitiveType(c); + case Class c when c.isLocalClass() || c.isAnonymousClass() -> throw new IllegalArgumentException("t: " + t); + case Class c -> this.declaredType(c); + case ParameterizedType p -> this.declaredType(p); + case GenericArrayType g -> this.arrayType(g); + case java.lang.reflect.TypeVariable tv -> this.typeVariable(tv); + case java.lang.reflect.WildcardType w -> this.wildcardType(w); + default -> throw new IllegalArgumentException("t: " + t); }; } @@ -167,11 +169,15 @@ public default TypeMirror[] typeArray(final Type[] ts) { return rv; } - public default TypeElement typeElement(final Class c) { - if (c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass()) { - throw new IllegalArgumentException("c: " + c); - } - return this.typeElement(c.getCanonicalName()); + public default TypeElement typeElement(final Type t) { + return switch (t) { + case null -> throw new NullPointerException("t"); + case Class c when c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass() + -> throw new IllegalArgumentException("t: " + t); + case Class c -> this.typeElement(c.getCanonicalName()); + case ParameterizedType p -> this.typeElement(p.getRawType()); + default -> throw new IllegalArgumentException("t: " + t); + }; } public default WildcardType wildcardType(final java.lang.reflect.WildcardType t) { diff --git a/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java b/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java index 646c55b9..dbf9fd04 100644 --- a/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java +++ b/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java @@ -41,7 +41,7 @@ import static org.microbean.lang.type.Types.asElement; /** - * Does something adapting-like. + * Does something adapting-like. Looks to be related to type variable resolution. * *

Usage: call {@link #adapt(DeclaredType, DeclaredType)}, not {@link #visit(TypeMirror)}.

* @@ -67,6 +67,7 @@ final class AdaptingVisitor extends SimpleTypeVisitor14 { private final Types types; + // The compiler calls this "mapping"; the class is called "Adapting"; I think they are the same thing private final Map mapping; private final SameTypeVisitor sameTypeVisitor; @@ -92,6 +93,10 @@ final class AdaptingVisitor extends SimpleTypeVisitor14 { this.to = Objects.requireNonNull(to, "to"); } + final void adaptSelf(final DeclaredType target) { + this.adapt((DeclaredType)target.asElement().asType(), target); + } + final void adapt(final DeclaredType source, final DeclaredType target) { this.visitDeclared(source, target); final int fromSize = this.from.size(); @@ -103,10 +108,29 @@ final void adapt(final DeclaredType source, final DeclaredType target) { } } - final void adaptSelf(final DeclaredType target) { - this.adapt((DeclaredType)target.asElement().asType(), target); + private final void adaptRecursive(final Collection source, final Collection target) { + if (source.size() == target.size()) { + final Iterator sourceIterator = source.iterator(); + final Iterator targetIterator = target.iterator(); + while (sourceIterator.hasNext()) { + assert targetIterator.hasNext(); + this.adaptRecursive(sourceIterator.next(), targetIterator.next()); + } + } } + private final void adaptRecursive(final TypeMirror source, final TypeMirror target) { + final TypeMirrorPair pair = new TypeMirrorPair(this.sameTypeVisitor, source, target); + if (this.cache.add(pair)) { + try { + this.visit(source, target); + } finally { + this.cache.remove(pair); + } + } + } + + // Adapts the component type of source. @Override public final Void visitArray(final ArrayType source, final TypeMirror target) { assert source.getKind() == TypeKind.ARRAY; @@ -116,6 +140,8 @@ public final Void visitArray(final ArrayType source, final TypeMirror target) { return null; } + // Adapts type arguments of source. "Recursive" means something like "go into type parameters etc." + // Adapting a type argument takes effect on wildcard-extending type variables only, it looks like. @Override public final Void visitDeclared(final DeclaredType source, final TypeMirror target) { assert source.getKind() == TypeKind.DECLARED; @@ -135,6 +161,7 @@ public final Void visitTypeVariable(final TypeVariable source, final TypeMirror this.from.add(source); this.to.add(target); } else if (val.getKind() == TypeKind.WILDCARD && target.getKind() == TypeKind.WILDCARD) { + // This block is the only place in this entire class where the magic happens. final WildcardType valWc = (WildcardType)val; final TypeMirror valSuperBound = valWc.getSuperBound(); final TypeMirror valExtendsBound = valWc.getExtendsBound(); @@ -172,6 +199,7 @@ public final Void visitTypeVariable(final TypeVariable source, final TypeMirror return null; } + // Adapts the bounds of a wildcard. @Override public final Void visitWildcard(final WildcardType source, final TypeMirror target) { assert source.getKind() == TypeKind.WILDCARD; @@ -191,26 +219,5 @@ public final Void visitWildcard(final WildcardType source, final TypeMirror targ return null; } - private final void adaptRecursive(final TypeMirror source, final TypeMirror target) { - final TypeMirrorPair pair = new TypeMirrorPair(this.sameTypeVisitor, source, target); - if (this.cache.add(pair)) { - try { - this.visit(source, target); - } finally { - this.cache.remove(pair); - } - } - } - - private final void adaptRecursive(final Collection source, final Collection target) { - if (source.size() == target.size()) { - final Iterator sourceIterator = source.iterator(); - final Iterator targetIterator = target.iterator(); - while (sourceIterator.hasNext()) { - assert targetIterator.hasNext(); - this.adaptRecursive(sourceIterator.next(), targetIterator.next()); - } - } - } } diff --git a/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java b/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java index e58bca73..56b46cf8 100644 --- a/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java +++ b/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java @@ -61,7 +61,7 @@ public final boolean test(final Element e, final Element f) { case DECLARED: switch (s.getKind()) { case DECLARED: - if (this.equality.equals(e, f)) { // in place of == + if (this.equality.equals(e, f)) { // already checked e == f // Both are completely interchangeable DeclaredTypes; can't say which comes first. return false; } @@ -77,7 +77,7 @@ public final boolean test(final Element e, final Element f) { case TYPEVAR: switch (s.getKind()) { case TYPEVAR: - if (this.equality.equals(e, f)) { // in place of == + if (this.equality.equals(e, f)) { // already checked e == f // Both are completely interchangeable TypeVariables; can't say which comes first. return false; } diff --git a/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java b/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java index 246af148..bd27d313 100644 --- a/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java +++ b/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java @@ -151,7 +151,15 @@ public final TypeMirror visitDeclared(final DeclaredType t, final Void x) { @Override // SimpleTypeVisitor14 public final TypeMirror visitIntersection(final IntersectionType t, final Void x) { assert t.getKind() == TypeKind.INTERSECTION; - return t.getBounds().get(0); // TODO: presumes first bound will be the supertype; see https://github.com/openjdk/jdk/blob/jdk-19+25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1268 + // Since an intersection type is always ordered from most specialized to least, it follows that the direct supertype + // of an intersection type is its first bound. See + // https://docs.oracle.com/javase/specs/jls/se22/html/jls-4.html#jls-4.10.2 + // + // TODO: wait, no, this isn't correct at all. The JLS says: "The direct supertypes of an intersection type T1 & + // ... & Tn are Ti (1 ≤ i ≤ n)." But javac's Types class sets the supertype_field (singular) field to the first + // element of the list. See + // https://github.com/openjdk/jdk/blob/jdk-19+25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1268. + return t.getBounds().get(0); } @Override // SimpleTypeVisitor14 diff --git a/lang/src/test/java/org/microbean/lang/TestErase.java b/lang/src/test/java/org/microbean/lang/TestErase.java index 86dcf5b0..8eeea1ae 100644 --- a/lang/src/test/java/org/microbean/lang/TestErase.java +++ b/lang/src/test/java/org/microbean/lang/TestErase.java @@ -151,12 +151,10 @@ public final PrimitiveType primitiveType(final TypeKind k) { public boolean sameType(final TypeMirror t, final TypeMirror s) { return javacModelTypes.isSameType(t, s); } - /* @Override - public final TypeElement typeElement(final CharSequence m, final CharSequence n) { - return elements.getTypeElement(elements.getModuleElement(m), n); + public boolean subtype(final TypeMirror t, final TypeMirror s) { + return javacModelTypes.isSubtype(t, s); } - */ @Override public final TypeElement typeElement(final CharSequence n) { return elements.getTypeElement(n); diff --git a/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java b/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java index 9727fe98..690b6dfe 100644 --- a/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java +++ b/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java @@ -117,12 +117,10 @@ public final PrimitiveType primitiveType(final TypeKind k) { public boolean sameType(final TypeMirror t, final TypeMirror s) { return javacModelTypes.isSameType(t, s); } - /* @Override - public final TypeElement typeElement(final CharSequence m, final CharSequence n) { - return elements.getTypeElement(elements.getModuleElement(m), n); + public boolean subtype(final TypeMirror t, final TypeMirror s) { + return javacModelTypes.isSubtype(t, s); } - */ @Override public final TypeElement typeElement(final CharSequence n) { return elements.getTypeElement(n); diff --git a/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java b/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java index 108db893..fbbcda85 100644 --- a/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java +++ b/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java @@ -137,12 +137,10 @@ public final PrimitiveType primitiveType(final TypeKind k) { public boolean sameType(final TypeMirror t, final TypeMirror s) { return javacModelTypes.isSameType(t, s); } - /* @Override - public final TypeElement typeElement(final CharSequence m, final CharSequence n) { - return elements.getTypeElement(elements.getModuleElement(m), n); + public boolean subtype(final TypeMirror t, final TypeMirror s) { + return javacModelTypes.isSubtype(t, s); } - */ @Override public final TypeElement typeElement(final CharSequence n) { return elements.getTypeElement(n); diff --git a/lang/src/test/java/org/microbean/lang/TestTypeClosure.java b/lang/src/test/java/org/microbean/lang/TestTypeClosure.java index 865c21d3..ee9e09cd 100644 --- a/lang/src/test/java/org/microbean/lang/TestTypeClosure.java +++ b/lang/src/test/java/org/microbean/lang/TestTypeClosure.java @@ -132,12 +132,10 @@ public final PrimitiveType primitiveType(final TypeKind k) { public boolean sameType(final TypeMirror t, final TypeMirror s) { return javacModelTypes.isSameType(t, s); } - /* @Override - public final TypeElement typeElement(final CharSequence m, final CharSequence n) { - return elements.getTypeElement(elements.getModuleElement(m), n); + public boolean subtype(final TypeMirror t, final TypeMirror s) { + return javacModelTypes.isSubtype(t, s); } - */ @Override public final TypeElement typeElement(final CharSequence n) { return elements.getTypeElement(n); @@ -248,6 +246,10 @@ public final PrimitiveType primitiveType(final TypeKind k) { public boolean sameType(final TypeMirror t, final TypeMirror s) { return javacModelTypes.isSameType(t, s); } + @Override + public boolean subtype(final TypeMirror t, final TypeMirror s) { + return javacModelTypes.isSubtype(t, s); + } /* @Override public final TypeElement typeElement(final CharSequence m, final CharSequence n) { diff --git a/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java b/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java index f366a8b9..0dd8ad83 100644 --- a/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java +++ b/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java @@ -95,7 +95,7 @@ final void testArrayTypeClosure() { final TypeMirror t = Lang.arrayTypeOf(Lang.declaredType("java.lang.Integer")); assertSame(TypeKind.ARRAY, t.getKind()); - final Type integerArrayType = (Type)unwrap(Lang.arrayTypeOf(Lang.declaredType("java.lang.Integer"))); + final Type integerArrayType = (Type)unwrap(t); assertSame(TypeKind.ARRAY, integerArrayType.getKind()); final List closure = javacCodeTypes.closure(integerArrayType); // Perhaps surprisingly, the closure (as implemented by javac) of any type, including array types, that is not a