001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2025 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; 015 016import java.lang.annotation.Annotation; 017 018import java.lang.constant.ClassDesc; 019import java.lang.constant.Constable; 020import java.lang.constant.ConstantDesc; 021import java.lang.constant.DynamicConstantDesc; 022 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Optional; 026 027import java.util.function.Supplier; 028 029import javax.lang.model.AnnotatedConstruct; 030 031import javax.lang.model.element.AnnotationMirror; 032import javax.lang.model.element.Element; 033 034import javax.lang.model.type.TypeMirror; 035 036import org.microbean.construct.constant.Constables; 037 038import org.microbean.construct.element.SyntheticAnnotationMirror; 039import org.microbean.construct.element.UniversalAnnotation; 040import org.microbean.construct.element.UniversalElement; 041 042import org.microbean.construct.type.UniversalType; 043 044import static java.lang.constant.ConstantDescs.BSM_INVOKE; 045import static java.lang.constant.ConstantDescs.CD_List; 046 047import static java.lang.constant.MethodHandleDesc.ofConstructor; 048 049import static java.util.Collections.unmodifiableList; 050 051import static java.util.Objects.requireNonNull; 052 053/** 054 * An abstract implementation of {@link AnnotatedConstruct} from which only {@link UniversalElement} and {@link 055 * UniversalType} descend. 056 * 057 * @param <T> a type of {@link AnnotatedConstruct}, which may be only either {@link Element} or {@link TypeMirror} 058 * 059 * @param <U> a type representing one of the permitted subclasses 060 * 061 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 062 * 063 * @see AnnotatedConstruct 064 * 065 * @see UniversalElement 066 * 067 * @see UniversalType 068 */ 069public abstract sealed class UniversalConstruct<T extends AnnotatedConstruct, U extends UniversalConstruct<T, U>> 070 implements AnnotatedConstruct, Constable 071 permits UniversalElement, UniversalType { 072 073 074 /* 075 * Instance fields. 076 */ 077 078 079 // Retained only for Constable implementation 080 private final PrimordialDomain domain; 081 082 // Eventually this should become a lazy constant/stable value 083 // volatile not needed 084 private Supplier<? extends T> delegateSupplier; 085 086 // Eventually this should become a lazy constant/stable value 087 private volatile String s; 088 089 // Eventually this should become a lazy constant/stable value 090 private volatile List<? extends UniversalAnnotation> annotations; 091 092 private final List<? extends UniversalAnnotation> syntheticAnnotations; 093 094 095 /* 096 * Constructors. 097 */ 098 099 100 /** 101 * Creates a new {@link AnnotatedConstruct}. 102 * 103 * @param delegate a delegate to which operations are delegated; must not be {@code null} 104 * 105 * @param domain a {@link PrimordialDomain} representing the construct domain from which the supplied {@code 106 * delegate} is presumed to have originated; must not be {@code null} 107 * 108 * @exception NullPointerException if either argument is {@code null} 109 * 110 * @see #UniversalConstruct(AnnotatedConstruct, List, PrimordialDomain) 111 */ 112 protected UniversalConstruct(final T delegate, final PrimordialDomain domain) { 113 this(delegate, null, domain); 114 } 115 116 /** 117 * Creates a new {@link AnnotatedConstruct}. 118 * 119 * @param delegate a delegate to which operations are delegated; must not be {@code null} 120 * 121 * @param annotations a {@link List} of {@link AnnotationMirror} instances representing annotations, often 122 * synthetic, that this {@link UniversalConstruct} should bear; may be {@code null} in which case only the annotations 123 * from the supplied {@code delegate} will be used 124 * 125 * @param domain a {@link PrimordialDomain} representing the construct domain from which the supplied {@code 126 * delegate} is presumed to have originated; must not be {@code null} 127 * 128 * @exception NullPointerException if any argument is {@code null} 129 * 130 * @see #annotate(List) 131 */ 132 @SuppressWarnings("try") 133 protected UniversalConstruct(final T delegate, 134 final List<? extends AnnotationMirror> annotations, 135 final PrimordialDomain domain) { 136 super(); 137 this.domain = requireNonNull(domain, "domain"); 138 if (annotations == null) { 139 this.syntheticAnnotations = List.of(); 140 } else if (annotations.isEmpty()) { 141 this.syntheticAnnotations = List.of(); 142 this.annotations = List.of(); // volatile write 143 } else { 144 final List<UniversalAnnotation> delegateAnnotations = new ArrayList<>(annotations.size()); 145 final List<UniversalAnnotation> syntheticAnnotations = new ArrayList<>(annotations.size()); 146 for (final AnnotationMirror annotation : annotations) { 147 switch (annotation) { 148 case null -> throw new IllegalArgumentException("annotations: " + annotations); 149 case SyntheticAnnotationMirror sam -> syntheticAnnotations.add(UniversalAnnotation.of(sam, domain)); 150 case UniversalAnnotation ua -> { 151 switch (ua.delegate()) { 152 case SyntheticAnnotationMirror sam -> syntheticAnnotations.add(ua); 153 case UniversalAnnotation x -> throw new AssertionError(); 154 default -> delegateAnnotations.add(UniversalAnnotation.of(annotation, domain)); 155 } 156 } 157 default -> delegateAnnotations.add(UniversalAnnotation.of(annotation, domain)); 158 } 159 } 160 this.annotations = delegateAnnotations.isEmpty() ? List.of() : unmodifiableList(delegateAnnotations); // volatile write 161 this.syntheticAnnotations = syntheticAnnotations.isEmpty() ? List.of() : unmodifiableList(syntheticAnnotations); 162 } 163 final T unwrappedDelegate = unwrap(requireNonNull(delegate, "delegate")); 164 if (unwrappedDelegate == delegate) { 165 this.delegateSupplier = () -> { 166 try (var lock = domain.lock()) { 167 // No unwrapping happened so do symbol completion early; most common case. 168 if (unwrappedDelegate instanceof Element) { 169 ((Element)unwrappedDelegate).getModifiers(); 170 } else { 171 ((TypeMirror)unwrappedDelegate).getKind(); 172 } 173 this.delegateSupplier = () -> unwrappedDelegate; 174 } 175 return unwrappedDelegate; 176 }; 177 } else { 178 assert delegate instanceof UniversalConstruct<?, ?>; 179 // Symbol completion already happened because unwrapping actually happened 180 this.delegateSupplier = () -> unwrappedDelegate; 181 } 182 } 183 184 185 /* 186 * Instance methods. 187 */ 188 189 190 /** 191 * <strong>Experimental</strong>; returns a new {@link UniversalConstruct} instance annotated with only the supplied 192 * annotations, which are often synthetic. 193 * 194 * @param replacementAnnotations a {@link List} of {@link AnnotationMirror}s; must not be {@code null} 195 * 196 * @return a new {@link UniversalConstruct} instance; never {@code null} 197 * 198 * @exception NullPointerException if {@code replacementAnnotations} is {@code null} 199 */ 200 // Experimental. Unsupported. 201 protected abstract U annotate(final List<? extends AnnotationMirror> replacementAnnotations); 202 203 /** 204 * Returns the delegate to which operations are delegated. 205 * 206 * <p>The delegate is guaranteed not to be an instance of {@link UniversalConstruct}.</p> 207 * 208 * @return a non-{@code null} delegate 209 * 210 * @see Element 211 * 212 * @see TypeMirror 213 */ 214 public final T delegate() { 215 final T delegate = this.delegateSupplier.get(); 216 assert !(delegate instanceof UniversalConstruct); 217 return delegate; 218 } 219 220 @Override // Constable 221 public final Optional<? extends ConstantDesc> describeConstable() { 222 final T delegate = this.delegate(); 223 final List<? extends UniversalAnnotation> annotations = this.annotations; // volatile read; may be null and that's OK 224 return this.domain() instanceof Domain d ? Constables.describe(delegate, d) 225 .flatMap(delegateDesc -> Constables.describe(annotations, Constable::describeConstable) 226 .map(annosDesc -> DynamicConstantDesc.of(BSM_INVOKE, 227 ofConstructor(ClassDesc.of(this.getClass().getName()), 228 ClassDesc.of(delegate instanceof TypeMirror ? TypeMirror.class.getName() : Element.class.getName()), 229 CD_List, 230 ClassDesc.of(PrimordialDomain.class.getName())), 231 delegateDesc, 232 annosDesc, 233 ((Constable)d).describeConstable().orElseThrow()))) : 234 Optional.empty(); 235 } 236 237 /** 238 * Returns the {@link PrimordialDomain} supplied at construction time. 239 * 240 * @return the non-{@code null} {@link PrimordialDomain} supplied at construction time 241 */ 242 public final PrimordialDomain domain() { 243 return this.domain; 244 } 245 246 @Override // Object 247 public final boolean equals(final Object other) { 248 // Interesting; equality does not cause symbol completion. See: 249 // 250 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L553-L559 251 // (the only type that overrides this is ArrayType; see 252 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1402-L1406) 253 // 254 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java 255 // (Symbol (Element) doesn't override it at all.) 256 return this == other || switch (other) { 257 case null -> false; 258 case UniversalConstruct<?, ?> uc when this.getClass() == uc.getClass() -> this.delegate().equals(uc.delegate()); 259 default -> false; 260 }; 261 } 262 263 @Override // AnnotatedConstruct 264 @SuppressWarnings("try") 265 public final List<? extends UniversalAnnotation> getAnnotationMirrors() { 266 List<? extends UniversalAnnotation> annotations = this.annotations; // volatile read 267 if (annotations == null) { 268 try (var lock = this.domain().lock()) { 269 annotations = 270 this.annotations = UniversalAnnotation.of(this.delegate().getAnnotationMirrors(), this.domain()); // volatile read/write 271 } 272 assert annotations != null; 273 } 274 if (annotations.isEmpty()) { 275 return this.syntheticAnnotations; 276 } else if (this.syntheticAnnotations.isEmpty()) { 277 return annotations; 278 } else { 279 final List<UniversalAnnotation> rv = new ArrayList<>(annotations); 280 rv.addAll(this.syntheticAnnotations); 281 return unmodifiableList(rv); 282 } 283 } 284 285 @Override // AnnotatedConstruct 286 @SuppressWarnings("try") 287 public final <A extends Annotation> A getAnnotation(final Class<A> annotationType) { 288 // TODO: is this lock actually needed, given how delegateSupplier works? 289 try (var lock = this.domain().lock()) { 290 return this.delegate().getAnnotation(annotationType); 291 } 292 } 293 294 @Override // AnnotatedConstruct 295 @SuppressWarnings("try") 296 public final <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) { 297 // TODO: is this lock actually needed, given how delegateSupplier works? 298 try (var lock = this.domain().lock()) { 299 return this.delegate().getAnnotationsByType(annotationType); 300 } 301 } 302 303 @Override // Object 304 public final int hashCode() { 305 // Interesting; hashCode doesn't cause symbol completion. See: 306 // 307 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L565-L568 308 // (AnnoConstruct doesn't define it so super.hashCode() is Object.hashCode()) 309 // 310 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java 311 // (Symbol (Element) doesn't override it at all.) 312 return this.delegate().hashCode(); 313 } 314 315 @Override // Object 316 @SuppressWarnings("try") 317 public final String toString() { 318 String s = this.s; // volatile read 319 if (s == null) { 320 try (var lock = this.domain().lock()) { 321 s = this.s = this.delegate().toString(); // volatile write, read 322 } 323 assert s != null; 324 } 325 return s; 326 } 327 328 329 /* 330 * Static methods. 331 */ 332 333 334 /** 335 * <dfn>Unwraps</dfn> the supplied {@link AnnotatedConstruct} implementation such that the returned value is not an 336 * instance of {@link UniversalConstruct}. 337 * 338 * @param <T> an {@link AnnotatedConstruct} subtype (possibly {@link UniversalElement} or {@link UniversalType}) 339 * 340 * @param t an {@link AnnotatedConstruct}; may be {@code null} 341 * 342 * @return an object of the appropriate type that is guaranteed not to be an instance of {@link UniversalConstruct} 343 * 344 * @see #delegate() 345 */ 346 @SuppressWarnings("unchecked") 347 public static final <T extends AnnotatedConstruct> T unwrap(T t) { 348 while (t instanceof UniversalConstruct<?, ?> uc) { 349 t = (T)uc.delegate(); 350 } 351 return t; 352 } 353 354}