From 532688b571dee3f49cf4a57dc62495b4173dae58 Mon Sep 17 00:00:00 2001 From: Dmitrii Tikhomirov Date: Sat, 8 Jun 2024 23:35:19 -0700 Subject: [PATCH] prepare for j2cl v20230718-1 --- pom.xml | 2 +- tests/commons/pom.xml | 4 +- tests/entrypoint/jstype/pom.xml | 4 +- tests/entrypoint/pojo/pom.xml | 4 +- tests/es6shim/pom.xml | 4 +- tests/exports/javaenv/pom-advanced.xml | 4 +- tests/exports/javaenv/pom.xml | 4 +- tests/exports/jsenv/bundle_jar_pom.xml | 2 +- tests/exports/jsenv/pom.xml | 2 +- tests/pom.xml | 4 +- .../processors/test/TextResourcesTest.java | 10 +- tests/translation/default/pom.xml | 4 +- tests/translation/fr/pom.xml | 4 +- tests/translation/fr_nr/pom.xml | 4 +- tests/translation/no_bundle/pom.xml | 4 +- .../j2cl/processors/utils/J2CLUtils.java | 1235 +++++++++-------- 16 files changed, 689 insertions(+), 606 deletions(-) diff --git a/pom.xml b/pom.xml index cc05290..0698def 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 1.1.0 2.0.0 - 0.11.0-9336533b6 + v20230718-1 0.11 1.0-rc7 4.8.141 diff --git a/tests/commons/pom.xml b/tests/commons/pom.xml index 6ca6517..1043a16 100644 --- a/tests/commons/pom.xml +++ b/tests/commons/pom.xml @@ -24,8 +24,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 3.0-beta-02 2.0.0 diff --git a/tests/entrypoint/jstype/pom.xml b/tests/entrypoint/jstype/pom.xml index 3299979..86b813e 100644 --- a/tests/entrypoint/jstype/pom.xml +++ b/tests/entrypoint/jstype/pom.xml @@ -24,8 +24,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 4.12.1 4.13.1 diff --git a/tests/entrypoint/pojo/pom.xml b/tests/entrypoint/pojo/pom.xml index bb2aaf9..b9bb997 100644 --- a/tests/entrypoint/pojo/pom.xml +++ b/tests/entrypoint/pojo/pom.xml @@ -24,8 +24,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 4.12.1 4.13.1 diff --git a/tests/es6shim/pom.xml b/tests/es6shim/pom.xml index 65ecffa..c32aff2 100644 --- a/tests/es6shim/pom.xml +++ b/tests/es6shim/pom.xml @@ -25,8 +25,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 3.0-beta-02 2.0.0 diff --git a/tests/exports/javaenv/pom-advanced.xml b/tests/exports/javaenv/pom-advanced.xml index 248bcdd..33e37b7 100644 --- a/tests/exports/javaenv/pom-advanced.xml +++ b/tests/exports/javaenv/pom-advanced.xml @@ -26,9 +26,9 @@ 3.1.0 3.0.1 3.0 - 0.21.0 + 0.22.0 - 0.11.0-9336533b6 + v20240603-1 4.12.1 4.13.1 diff --git a/tests/exports/javaenv/pom.xml b/tests/exports/javaenv/pom.xml index a3354a9..eff1c03 100644 --- a/tests/exports/javaenv/pom.xml +++ b/tests/exports/javaenv/pom.xml @@ -26,9 +26,9 @@ 3.1.0 3.0.1 3.0 - 0.21.0 + 0.23-SNAPSHOT - 0.11.0-9336533b6 + v20230718-1 4.12.1 4.13.1 diff --git a/tests/exports/jsenv/bundle_jar_pom.xml b/tests/exports/jsenv/bundle_jar_pom.xml index 709dd94..ac0ef81 100644 --- a/tests/exports/jsenv/bundle_jar_pom.xml +++ b/tests/exports/jsenv/bundle_jar_pom.xml @@ -25,7 +25,7 @@ 3.1.0 3.0.1 3.0 - 0.21.0 + 0.22.0 4.12.1 4.13.1 diff --git a/tests/exports/jsenv/pom.xml b/tests/exports/jsenv/pom.xml index a4508c6..66e281c 100644 --- a/tests/exports/jsenv/pom.xml +++ b/tests/exports/jsenv/pom.xml @@ -25,7 +25,7 @@ 3.1.0 3.0.1 3.0 - 0.21.0 + 0.23-SNAPSHOT 4.12.1 4.13.1 diff --git a/tests/pom.xml b/tests/pom.xml index c6e46ef..b332116 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -19,7 +19,7 @@ 1.8 1.8 - 0.21.0 + 0.23-SNAPSHOT 2.17 2.8.2 3.8.0 @@ -28,7 +28,7 @@ 1.0.0 1.1.0 - 0.11.0-9336533b6 + v20230718-1 4.11 4.12.1 diff --git a/tests/resources/src/test/java/org/treblereel/j2cl/processors/test/TextResourcesTest.java b/tests/resources/src/test/java/org/treblereel/j2cl/processors/test/TextResourcesTest.java index abc24b2..7330183 100644 --- a/tests/resources/src/test/java/org/treblereel/j2cl/processors/test/TextResourcesTest.java +++ b/tests/resources/src/test/java/org/treblereel/j2cl/processors/test/TextResourcesTest.java @@ -45,14 +45,14 @@ private String readFileAsString(String fileName) { @Test public void testEscape() { - String content = readFileAsString("escape.txt"); - assertEquals(content, TextTestResourceImpl.INSTANCE.escape().getText()); + String content = readFileAsString("escape.txt"); + assertEquals(content, TextTestResourceImpl.INSTANCE.escape().getText()); } @Test public void testBigTxt() { - String content = readFileAsString("bigtextresource.txt"); - assertEquals(content, TextTestResourceImpl.INSTANCE.getBig().getText()); + String content = readFileAsString("bigtextresource.txt"); + assertEquals(content, TextTestResourceImpl.INSTANCE.getBig().getText()); } @Test @@ -108,6 +108,6 @@ private void assertEquals(String str1, String str2) { } private String normalize(String s) { - return s.replace("\r\n","\n"); + return s.replace("\r\n", "\n"); } } diff --git a/tests/translation/default/pom.xml b/tests/translation/default/pom.xml index b193c6f..25b5187 100644 --- a/tests/translation/default/pom.xml +++ b/tests/translation/default/pom.xml @@ -25,8 +25,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 3.0-beta-02 2.0.0 diff --git a/tests/translation/fr/pom.xml b/tests/translation/fr/pom.xml index 7a8e005..29d2e52 100644 --- a/tests/translation/fr/pom.xml +++ b/tests/translation/fr/pom.xml @@ -25,8 +25,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 3.0-beta-02 2.0.0 diff --git a/tests/translation/fr_nr/pom.xml b/tests/translation/fr_nr/pom.xml index 5b83350..a8e033f 100644 --- a/tests/translation/fr_nr/pom.xml +++ b/tests/translation/fr_nr/pom.xml @@ -25,8 +25,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 3.0-beta-02 2.0.0 diff --git a/tests/translation/no_bundle/pom.xml b/tests/translation/no_bundle/pom.xml index 45e9906..5011cb7 100644 --- a/tests/translation/no_bundle/pom.xml +++ b/tests/translation/no_bundle/pom.xml @@ -29,8 +29,8 @@ 3.1.0 3.0.1 3.0 - 0.21.0 - 0.11.0-9336533b6 + 0.23-SNAPSHOT + v20230718-1 3.0-beta-02 2.0.0 diff --git a/utils/src/main/java/org/treblereel/j2cl/processors/utils/J2CLUtils.java b/utils/src/main/java/org/treblereel/j2cl/processors/utils/J2CLUtils.java index 48bcd92..c0619ba 100644 --- a/utils/src/main/java/org/treblereel/j2cl/processors/utils/J2CLUtils.java +++ b/utils/src/main/java/org/treblereel/j2cl/processors/utils/J2CLUtils.java @@ -17,10 +17,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.j2cl.transpiler.ast.TypeDeclaration.Kind; +import static com.google.j2cl.transpiler.ast.TypeDeclaration.newBuilder; -import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.collect.Streams; import com.google.j2cl.common.InternalCompilerError; import com.google.j2cl.transpiler.ast.ArrayTypeDescriptor; @@ -29,8 +31,7 @@ import com.google.j2cl.transpiler.ast.FieldDescriptor; import com.google.j2cl.transpiler.ast.JsEnumInfo; import com.google.j2cl.transpiler.ast.JsInfo; -import com.google.j2cl.transpiler.ast.JsMemberType; -import com.google.j2cl.transpiler.ast.Kind; +import com.google.j2cl.transpiler.ast.Literal; import com.google.j2cl.transpiler.ast.MemberDescriptor; import com.google.j2cl.transpiler.ast.MethodDescriptor; import com.google.j2cl.transpiler.ast.MethodDescriptor.ParameterDescriptor; @@ -43,8 +44,11 @@ import com.google.j2cl.transpiler.frontend.javac.JsInteropAnnotationUtils; import com.google.j2cl.transpiler.frontend.javac.JsInteropUtils; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; -import java.util.function.Predicate; +import java.util.Map; import java.util.function.Supplier; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.AnnotatedConstruct; @@ -66,7 +70,6 @@ import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import jsinterop.annotations.JsPackage; /** * Utility functions to interact with JavaC internal representations. @@ -74,35 +77,120 @@ *

it's taken from J2CL project */ public class J2CLUtils { - private final Types types; + private final Types javacTypes; private final Elements elements; private final ProcessingEnvironment processingEnv; + private final Map + cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope = new HashMap<>(); + private final Map + cachedDeclaredTypeDescriptorByDeclaredTypeOutOfNullMarkedScope = new HashMap<>(); public J2CLUtils(ProcessingEnvironment processingEnv) { - this.types = processingEnv.getTypeUtils(); + this.javacTypes = processingEnv.getTypeUtils(); this.elements = processingEnv.getElementUtils(); this.processingEnv = processingEnv; + initWellKnownTypes(TypeDescriptors.getWellKnownTypeNames()); } - public static boolean hasJsMemberAnnotation(ExecutableElement method) { - return JsInteropAnnotationUtils.getJsMethodAnnotation(method) != null - || JsInteropAnnotationUtils.getJsPropertyAnnotation(method) != null - || JsInteropAnnotationUtils.getJsConstructorAnnotation(method) != null; + private boolean isNonNullAnnotation(AnnotationMirror annotation) { + DeclaredType annotationType = annotation.getAnnotationType(); + String name = annotationType.asElement().getSimpleName().toString(); + return name.equals("NonNull") || name.equals("JsNonNull"); } - public static boolean isAnonymous(TypeMirror type) { - if (type.getKind().equals(TypeKind.DECLARED)) { - DeclaredType declaredType = (DeclaredType) type; - TypeElement typeElem = (TypeElement) declaredType.asElement(); - if (typeElem.getNestingKind().equals(NestingKind.ANONYMOUS)) { - return true; + private boolean isNullableAnnotation(AnnotationMirror annotation) { + DeclaredType annotationType = annotation.getAnnotationType(); + return annotationType.asElement().getSimpleName().toString().equals("Nullable"); + } + + private DeclaredTypeDescriptor withNullability( + DeclaredTypeDescriptor typeDescriptor, boolean nullable) { + return nullable ? typeDescriptor.toNullable() : typeDescriptor.toNonNullable(); + } + + /** + * In case the given type element is nested, return the outermost possible enclosing type element. + */ + private TypeElement toTopLevelTypeBinding(Element element) { + if (element.getEnclosingElement().getKind() == ElementKind.PACKAGE) { + return (TypeElement) element; + } + return toTopLevelTypeBinding(element.getEnclosingElement()); + } + + private boolean isValuesMethod(ExecutableElement methodElement) { + return methodElement.getSimpleName().contentEquals("values") + && methodElement.getParameters().isEmpty(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Utility methods to process nullability annotations on classes that are compiled separately. + // Javac does not present TYPE_USE annotation in the returned type instances. + private TypeDescriptor applyParameterNullabilityAnnotations( + TypeDescriptor typeDescriptor, ExecutableElement declarationMethodElement, int index) { + return typeDescriptor; + } + + private int getInnerDepth(DeclaredTypeDescriptor innerType) { + if (innerType.getTypeDeclaration().isCapturingEnclosingInstance()) { + return getInnerDepth(innerType.getEnclosingTypeDescriptor()) + 1; + } + return 0; + } + + /** Returns true if the element is annotated with @UncheckedCast. */ + private boolean hasUncheckedCastAnnotation(Element element) { + return false; + } + + /** Returns true if the element is annotated with @HasNoSideEffects. */ + private boolean isAnnotatedWithHasNoSideEffects(Element element) { + return true; + } + + private List getTypeArguments(DeclaredType declaredType) { + List typeArguments = new ArrayList<>(); + DeclaredType currentType = declaredType; + do { + typeArguments.addAll(currentType.getTypeArguments()); + Element enclosingElement = currentType.asElement().getEnclosingElement(); + if (enclosingElement.getKind() == ElementKind.METHOD + || enclosingElement.getKind() == ElementKind.CONSTRUCTOR) { + typeArguments.addAll( + ((Parameterizable) enclosingElement) + .getTypeParameters().stream().map(Element::asType).collect(toImmutableList())); } + currentType = + currentType.getEnclosingType() instanceof DeclaredType + ? (DeclaredType) currentType.getEnclosingType() + : null; + } while (currentType != null); + return typeArguments; + } + + private Kind getKindFromTypeBinding(TypeElement typeElement) { + if (isEnum(typeElement) && !isAnonymous(typeElement)) { + // Do not consider the anonymous classes that constitute enum values as Enums, only the + // enum "class" itself is considered Kind.ENUM. + return Kind.ENUM; + } else if (isClass(typeElement) || (isEnum(typeElement) && isAnonymous(typeElement))) { + return Kind.CLASS; + } else if (isInterface(typeElement)) { + return Kind.INTERFACE; } + throw new InternalCompilerError("Type binding %s not handled.", typeElement); + } + + private String getJsName(final TypeElement classSymbol) { + return JsInteropAnnotationUtils.getJsName(classSymbol); + } + + private boolean hasNullMarkedAnnotation(TypeElement classSymbol) { return false; } - private static List getTypeParameters(TypeElement typeElement) { + private List getTypeParameters(TypeElement typeElement) { List typeParameterElements = new ArrayList<>(typeElement.getTypeParameters()); Element currentElement = typeElement; @@ -125,7 +213,7 @@ private static List getTypeParameters(TypeElement typeElem return typeParameterElements; } - public static TypeElement getEnclosingType(Element typeElement) { + public TypeElement getEnclosingType(Element typeElement) { Element enclosing = typeElement.getEnclosingElement(); while (enclosing != null && !(enclosing instanceof TypeElement)) { enclosing = enclosing.getEnclosingElement(); @@ -133,23 +221,40 @@ public static TypeElement getEnclosingType(Element typeElement) { return (TypeElement) enclosing; } - public static String getJsName(final TypeElement classSymbol) { - return JsInteropAnnotationUtils.getJsName(classSymbol); + private TypeElement getEnclosingType(TypeElement typeElement) { + Element enclosing = typeElement.getEnclosingElement(); + while (enclosing != null && !(enclosing instanceof TypeElement)) { + enclosing = enclosing.getEnclosingElement(); + } + return (TypeElement) enclosing; } - public static boolean isLocal(TypeElement typeElement) { - return typeElement.getNestingKind() == NestingKind.LOCAL; + private boolean isEnum(TypeElement typeElement) { + return typeElement.getKind() == ElementKind.ENUM; } - public static boolean isStatic(Element element) { - return element.getModifiers().contains(Modifier.STATIC); + private boolean isAnnotation(TypeElement typeElement) { + return typeElement.getKind() == ElementKind.ANNOTATION_TYPE; } - public static boolean isNative(Element element) { - return element.getModifiers().contains(Modifier.NATIVE); + private boolean isAnonymous(TypeElement typeElement) { + return typeElement.getNestingKind() == NestingKind.ANONYMOUS; + } + + private boolean isClass(TypeElement typeElement) { + return typeElement.getKind() == ElementKind.CLASS; + } + + private boolean isInterface(TypeElement typeElement) { + return typeElement.getKind() == ElementKind.INTERFACE + || typeElement.getKind() == ElementKind.ANNOTATION_TYPE; + } + + private boolean isLocal(TypeElement typeElement) { + return typeElement.getNestingKind() == NestingKind.LOCAL; } - public static Visibility getVisibility(Element element) { + public Visibility getVisibility(Element element) { if (element.getModifiers().contains(Modifier.PUBLIC)) { return Visibility.PUBLIC; } else if (element.getModifiers().contains(Modifier.PROTECTED)) { @@ -161,296 +266,325 @@ public static Visibility getVisibility(Element element) { } } - public static String getJsNamespace(TypeElement classSymbol) { - String jsNamespace = JsInteropAnnotationUtils.getJsNamespace(classSymbol); - if (jsNamespace != null) { - return jsNamespace; - } - - // Maybe namespace is set via package-info file? - boolean isTopLevelType = classSymbol.getEnclosingElement().getKind() == ElementKind.PACKAGE; - if (isTopLevelType) { - JsPackage jsPackage = classSymbol.getEnclosingElement().getAnnotation(JsPackage.class); - return jsPackage != null ? jsPackage.namespace() : null; - } - return null; + private boolean isDeprecated(AnnotatedConstruct binding) { + return binding.getAnnotation(Deprecated.class) != null; } - private static List getTypeArguments(DeclaredType declaredType) { - List typeArguments = new ArrayList<>(); - DeclaredType currentType = declaredType; - do { - typeArguments.addAll(currentType.getTypeArguments()); - Element enclosingElement = currentType.asElement().getEnclosingElement(); - if (enclosingElement.getKind() == ElementKind.METHOD - || enclosingElement.getKind() == ElementKind.CONSTRUCTOR) { - typeArguments.addAll( - ((Parameterizable) enclosingElement) - .getTypeParameters().stream().map(Element::asType).collect(toImmutableList())); - } - currentType = - currentType.getEnclosingType() instanceof DeclaredType - ? (DeclaredType) currentType.getEnclosingType() - : null; - } while (currentType != null); - return typeArguments; + private boolean isDefaultMethod(Element element) { + return element.getModifiers().contains(Modifier.DEFAULT); } - private static DeclaredTypeDescriptor withNullability( - DeclaredTypeDescriptor typeDescriptor, boolean nullable) { - return nullable ? typeDescriptor.toNullable() : typeDescriptor.toNonNullable(); + private boolean isAbstract(Element element) { + return element.getModifiers().contains(Modifier.ABSTRACT); } - private static boolean isNonNullAnnotation(AnnotationMirror annotation) { - DeclaredType annotationType = annotation.getAnnotationType(); - String name = annotationType.asElement().getSimpleName().toString(); - return name.equals("NonNull") || name.equals("JsNonNull"); + private boolean isFinal(Element element) { + return element.getModifiers().contains(Modifier.FINAL); } - private static boolean isNullableAnnotation(AnnotationMirror annotation) { - DeclaredType annotationType = annotation.getAnnotationType(); - return annotationType.asElement().getSimpleName().toString().equals("Nullable"); + public boolean isStatic(Element element) { + return element.getModifiers().contains(Modifier.STATIC); } - public static boolean isStatic(VariableElement variableElement) { - return variableElement.getModifiers().contains(Modifier.STATIC); + private boolean isNative(Element element) { + return element.getModifiers().contains(Modifier.NATIVE); } - public static boolean isFinal(Element declarationMethodElement) { - return declarationMethodElement.getModifiers().contains(Modifier.FINAL); + private boolean isSynthetic(Element element) { + return false; + // return element instanceof Symbol && (((Symbol) element).flags() & Flags.SYNTHETIC) != 0; } - public static boolean isInterface(TypeElement typeElement) { - return typeElement.getKind().isInterface(); + private MethodDescriptor ctorMethodDescriptorFromJavaConstructor(MethodDescriptor constructor) { + return constructor.transform( + builder -> + builder + .setReturnTypeDescriptor(PrimitiveTypes.VOID) + .setName(getCtorName(constructor)) + .setConstructor(false) + .setStatic(false) + .setOriginalJsInfo(JsInfo.NONE) + .removeParameterOptionality() + .setOrigin(MethodDescriptor.MethodOrigin.SYNTHETIC_CTOR_FOR_CONSTRUCTOR) + .setVisibility(Visibility.PUBLIC)); } - public static boolean isInterface(TypeMirror typeElement) { - return isInterface(MoreTypes.asTypeElement(typeElement)); + private String getCtorName(MethodDescriptor methodDescriptor) { + // Synthesize a name that is unique per class to avoid property clashes in JS. + return MethodDescriptor.CTOR_METHOD_PREFIX + + "__" + + methodDescriptor.getEnclosingTypeDescriptor().getMangledName(); } - public static boolean isDeprecated(AnnotatedConstruct binding) { - return hasAnnotation(binding, Deprecated.class.getName()); - } + private void initWellKnownTypes(Collection wellKnownQualifiedBinaryNames) { + if (TypeDescriptors.isInitialized()) { + return; + } + TypeDescriptors.SingletonBuilder builder = new TypeDescriptors.SingletonBuilder(); + // Add well-known, non-primitive types. + wellKnownQualifiedBinaryNames.forEach( + binaryName -> { + String qualifiedSourceName = binaryName.replace('$', '.'); + TypeElement element = getTypeElement(qualifiedSourceName); + if (element != null) { + builder.addReferenceType(createDeclaredTypeDescriptor(element.asType())); + } + }); + + DeclaredTypeDescriptor typeDescriptor = + createSyntheticJavaEmulInternalExceptionsTypeDescriptor(); + builder.addReferenceType(typeDescriptor); + builder.buildSingleton(); + } + + private DeclaredTypeDescriptor createSyntheticJavaEmulInternalExceptionsTypeDescriptor() { + TypeDeclaration typeDeclaration = + TypeDeclaration.newBuilder() + .setClassComponents(ImmutableList.of("Exceptions")) + .setNative(false) + .setCustomizedJsNamespace("javaemul.internal") + .setPackageName("javaemul.internal") + .setTypeParameterDescriptors(ImmutableList.of()) + .setVisibility(Visibility.PUBLIC) + .setKind(Kind.CLASS) + .build(); - public static boolean hasAnnotation(AnnotatedConstruct construct, String annotationSourceName) { - return findAnnotationBindingByName(construct.getAnnotationMirrors(), annotationSourceName) - != null; + return DeclaredTypeDescriptor.newBuilder() + .setTypeDeclaration(typeDeclaration) + .setTypeArgumentDescriptors(Collections.EMPTY_LIST) + .build(); } - public static AnnotationMirror findAnnotationBindingByName( - List annotations, String name) { - if (annotations == null) { - return null; - } - for (AnnotationMirror annotationBinding : annotations) { - if (((TypeElement) annotationBinding.getAnnotationType().asElement()) - .getQualifiedName() - .contentEquals(name)) { - return annotationBinding; - } - } - return null; + public DeclaredTypeDescriptor createDeclaredTypeDescriptor(TypeMirror typeMirror) { + return createDeclaredTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false); } - public static boolean isDefaultMethod(Element element) { - return element.getModifiers().contains(Modifier.DEFAULT); + private DeclaredTypeDescriptor createDeclaredTypeDescriptor( + TypeMirror typeMirror, boolean inNullMarkedScope) { + return createTypeDescriptor(typeMirror, inNullMarkedScope, DeclaredTypeDescriptor.class); } - public static boolean isClass(TypeElement typeElement) { - return typeElement.getKind().isClass(); + /** Creates a specific subclass of TypeDescriptor from a TypeMirror. */ + public T createTypeDescriptor(TypeMirror typeMirror, Class clazz) { + return createTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false, clazz); } - public static boolean isEnum(TypeElement typeElement) { - return typeElement.getKind().equals(ElementKind.ENUM); + /** Creates a specific subclass of TypeDescriptor from a TypeMirror. */ + private T createTypeDescriptor( + TypeMirror typeMirror, boolean inNullMarkedScope, Class clazz) { + return clazz.cast(createTypeDescriptor(typeMirror, inNullMarkedScope)); } - public static boolean isAbstract(Element element) { - return element.getModifiers().contains(Modifier.ABSTRACT); + /** Creates a TypeDescriptor from a TypeMirror. */ + public TypeDescriptor createTypeDescriptor(TypeMirror typeMirror) { + return createTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false); } - public static boolean isJsEnum(TypeElement typeElement) { - return JsInteropUtils.isJsEnum(typeElement); + /** Creates a TypeDescriptor from a TypeMirror. */ + private TypeDescriptor createTypeDescriptor(TypeMirror typeMirror, boolean inNullMarkedScope) { + return createTypeDescriptorWithNullability(typeMirror, ImmutableList.of(), inNullMarkedScope); } - public TypeDeclaration createDeclarationForType(final TypeElement typeElement) { - if (typeElement == null) { + /** Creates a type descriptor for the given TypeMirror, taking into account nullability. */ + private TypeDescriptor createTypeDescriptorWithNullability( + TypeMirror typeMirror, + List elementAnnotations, + boolean inNullMarkedScope) { + if (typeMirror == null || typeMirror.getKind() == TypeKind.NONE) { return null; } + if (typeMirror.getKind().isPrimitive()) { + return PrimitiveTypes.get(typeMirror.toString()); + } - boolean isFromSource = false; - // Compute these first since they're reused in other calculations. - String packageName = getPackageOf(typeElement).getQualifiedName().toString(); - boolean isAbstract = isAbstract(typeElement) && !isInterface(typeElement); - boolean isFinal = isFinal(typeElement); - - Supplier> declaredMethods = - () -> { - ImmutableList.Builder listBuilder = ImmutableList.builder(); - for (ExecutableElement methodElement : - ElementFilter.methodsIn(typeElement.getEnclosedElements())) { - MethodDescriptor methodDescriptor = createDeclarationMethodDescriptor(methodElement); - listBuilder.add(methodDescriptor); - } - return listBuilder.build(); - }; + if (typeMirror.getKind() == TypeKind.VOID) { + return PrimitiveTypes.VOID; + } - Supplier> declaredFields = - () -> - typeElement.getEnclosedElements().stream() - .filter( - element -> - element.getKind() == ElementKind.FIELD - || element.getKind() == ElementKind.ENUM_CONSTANT) - .map(VariableElement.class::cast) - .map(this::createFieldDescriptor) - .collect(toImmutableList()); + if (typeMirror.getKind() == TypeKind.INTERSECTION) { + throw new InternalCompilerError("Intersection types are not supported."); + // return createIntersectionType((IntersectionClassType) typeMirror); + } - JsEnumInfo jsEnumInfo = JsInteropUtils.getJsEnumInfo(typeElement); + if (typeMirror.getKind() == TypeKind.UNION) { + throw new InternalCompilerError("Union types are not supported."); + // return createUnionType((UnionClassType) typeMirror); + } - List typeParameterElements = getTypeParameters(typeElement); + if (typeMirror.getKind() == TypeKind.NULL) { + return TypeDescriptors.get().javaLangObject; + } - boolean isNullMarked = isNullMarked(typeElement); - return TypeDeclaration.newBuilder() - .setClassComponents(getClassComponents(typeElement)) - .setEnclosingTypeDeclaration(createDeclarationForType(getEnclosingType(typeElement))) - .setInterfaceTypeDescriptorsFactory( - () -> - createTypeDescriptors( - typeElement.getInterfaces(), - isNullMarked, - DeclaredTypeDescriptor.class, - typeElement)) - .setUnparameterizedTypeDescriptorFactory( - () -> createDeclaredTypeDescriptor(typeElement.asType())) - .setHasAbstractModifier(isAbstract) - .setKind(getKindFromTypeBinding(typeElement)) - .setCapturingEnclosingInstance(capturesEnclosingInstance(typeElement)) - .setFinal(isFinal) - .setFunctionalInterface(isFunctionalInterface(typeElement.asType())) - .setJsFunctionInterface(JsInteropUtils.isJsFunction(typeElement)) - .setJsType(JsInteropUtils.isJsType(typeElement)) - .setJsEnumInfo(jsEnumInfo) - .setNative(JsInteropUtils.isJsNativeType(typeElement)) - .setAnonymous(isAnonymous(typeElement)) - .setLocal(isLocal(typeElement)) - .setSimpleJsName(getJsName(typeElement)) - .setCustomizedJsNamespace(getJsNamespace(typeElement)) - .setNullMarked(isNullMarked) - .setPackageName(packageName) - .setSuperTypeDescriptorFactory( - () -> - (DeclaredTypeDescriptor) - applyNullabilityAnnotations( - createDeclaredTypeDescriptor(typeElement.getSuperclass(), isNullMarked), - typeElement, - (obj) -> false)) - .setTypeParameterDescriptors( - typeParameterElements.stream() - .map(TypeParameterElement::asType) - .map(javax.lang.model.type.TypeVariable.class::cast) - .map(this::createTypeVariable) - .collect(toImmutableList())) - .setVisibility(getVisibility(typeElement)) - .setDeclaredMethodDescriptorsFactory(declaredMethods) - .setDeclaredFieldDescriptorsFactory(declaredFields) - .setUnusableByJsSuppressed(JsInteropAnnotationUtils.isUnusableByJsSuppressed(typeElement)) - .setDeprecated(isDeprecated(typeElement)) - .build(); - } + if (typeMirror.getKind() == TypeKind.TYPEVAR) { + return createTypeVariable((javax.lang.model.type.TypeVariable) typeMirror); + } - public MethodDescriptor createDeclarationMethodDescriptor(ExecutableElement methodElement) { - DeclaredTypeDescriptor enclosingTypeDescriptor = - createDeclaredTypeDescriptor(methodElement.getEnclosingElement().asType()); - return createDeclarationMethodDescriptor(methodElement, enclosingTypeDescriptor); - } + if (typeMirror.getKind() == TypeKind.WILDCARD) { + return createWildcardTypeVariable( + ((javax.lang.model.type.WildcardType) typeMirror).getExtendsBound()); + } - ///////////////////////////////////////////////////////////////////////////////////////////////// - // Utility methods to process nullability annotations on classes that are compiled separately. - // Javac does not present TYPE_USE annotation in the returned type instances. - private static TypeDescriptor applyParameterNullabilityAnnotations( - TypeDescriptor typeDescriptor, ExecutableElement declarationMethodElement, int index) { - return applyNullabilityAnnotations(typeDescriptor, declarationMethodElement, (obj) -> false); - // position -> - // position.parameter_index == index - // && position.type == TargetType.METHOD_FORMAL_PARAMETER); - } + boolean isNullable = isNullable(typeMirror, elementAnnotations, inNullMarkedScope); + if (typeMirror.getKind() == TypeKind.ARRAY) { + ArrayType arrayType = (ArrayType) typeMirror; + TypeDescriptor componentTypeDescriptor = + createTypeDescriptor(arrayType.getComponentType(), inNullMarkedScope); + return ArrayTypeDescriptor.newBuilder() + .setComponentTypeDescriptor(componentTypeDescriptor) + .setNullable(isNullable) + .build(); + } - private static TypeDescriptor applyNullabilityAnnotations( - TypeDescriptor typeDescriptor, - Element declarationMethodElement, - Predicate positionSelector) { - return typeDescriptor; + return withNullability( + createDeclaredType(MoreTypes.asDeclared(typeMirror), inNullMarkedScope), isNullable); } - public boolean isSameType(TypeMirror asType, TypeMirror type) { - return types.isSameType(asType, type); - } + /** + * Returns whether the given type binding should be nullable, according to the annotations on it + * and if nullability is enabled for the package containing the binding. + */ + private boolean isNullable( + TypeMirror typeMirror, + List elementAnnotations, + boolean inNullMarkedScope) { + checkArgument(!typeMirror.getKind().isPrimitive()); - public PackageElement getPackageOf(TypeElement typeElement) { - return elements.getPackageOf(typeElement); - } + if (typeMirror.getKind() == TypeKind.VOID) { + return true; + } - /** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */ - public MethodDescriptor createDeclarationMethodDescriptor( - ExecutableElement methodElement, DeclaredTypeDescriptor enclosingTypeDescriptor) { - return createMethodDescriptor(enclosingTypeDescriptor, methodElement, methodElement); + Iterable allAnnotations = + Iterables.concat(elementAnnotations, typeMirror.getAnnotationMirrors()); + + for (AnnotationMirror annotationMirror : allAnnotations) { + if (isNonNullAnnotation(annotationMirror)) { + return false; + } + if (isNullableAnnotation(annotationMirror)) { + return true; + } + } + + return !inNullMarkedScope; } - MethodDescriptor createMethodDescriptor( - DeclaredTypeDescriptor enclosingTypeDescriptor, - ExecutableElement methodElement, - ExecutableElement declarationMethodElement) { + private TypeVariable createTypeVariable(javax.lang.model.type.TypeVariable typeVariable) { + /* if (typeVariable instanceof CapturedType) { + return createWildcardTypeVariable(typeVariable.getUpperBound()); + }*/ - MethodDescriptor declarationMethodDescriptor = null; + Supplier boundTypeDescriptorFactory = + () -> createTypeDescriptor(typeVariable.getUpperBound()); - ImmutableList parameters = - methodElement.getParameters().stream() - .map(VariableElement::asType) - .collect(toImmutableList()); + List classComponents = getClassComponents(typeVariable); + return TypeVariable.newBuilder() + .setUpperBoundTypeDescriptorSupplier(boundTypeDescriptorFactory) + .setUniqueKey( + String.join("::", classComponents) + + (typeVariable.getUpperBound() != null + ? typeVariable.getUpperBound().toString() + : "")) + .setName(typeVariable.asElement().getSimpleName().toString()) + .build(); + } - TypeMirror returnType = methodElement.getReturnType(); - if (isSpecialized(declarationMethodElement, parameters, returnType)) { - declarationMethodDescriptor = - createDeclarationMethodDescriptor( - declarationMethodElement, enclosingTypeDescriptor.toUnparameterizedTypeDescriptor()); - } + private TypeVariable createWildcardTypeVariable(TypeMirror bound) { + return TypeVariable.newBuilder() + .setUpperBoundTypeDescriptorSupplier(() -> createTypeDescriptor(bound)) + .setWildcard(true) + .setName("?") + .setUniqueKey("::?::" + (bound != null ? bound.toString() : "")) + .build(); + } - TypeDescriptor returnTypeDescriptor = - createTypeDescriptorWithNullability( - returnType, declarationMethodElement.getAnnotationMirrors(), false); + private ImmutableList getClassComponents( + javax.lang.model.type.TypeVariable typeVariable) { + Element enclosingElement = typeVariable.asElement().getEnclosingElement(); + if (enclosingElement.getKind() == ElementKind.CLASS + || enclosingElement.getKind() == ElementKind.INTERFACE + || enclosingElement.getKind() == ElementKind.ENUM) { + return ImmutableList.builder() + .addAll(getClassComponents(enclosingElement)) + .add( + // If it is a class-level type variable, use the simple name (with prefix "C_") as the + // current name component. + "C_" + typeVariable.asElement().getSimpleName()) + .build(); + } else { + return ImmutableList.builder() + .addAll(getClassComponents(enclosingElement.getEnclosingElement())) + .add( + "M_" + + enclosingElement.getSimpleName() + + "_" + + typeVariable.asElement().getSimpleName()) + .build(); + } + } - ImmutableList.Builder parametersBuilder = ImmutableList.builder(); - for (int i = 0; i < parameters.size(); i++) { - parametersBuilder.add( - applyParameterNullabilityAnnotations( - createTypeDescriptorWithNullability( - parameters.get(i), - declarationMethodElement.getParameters().get(i).getAnnotationMirrors(), - false), - declarationMethodElement, - i)); + private ImmutableList getClassComponents(Element element) { + if (!(element instanceof TypeElement)) { + return ImmutableList.of(); + } + TypeElement typeElement = (TypeElement) element; + List classComponents = new ArrayList<>(); + TypeElement currentType = typeElement; + while (currentType != null) { + String simpleName; + if (currentType.getNestingKind() == NestingKind.LOCAL + || currentType.getNestingKind() == NestingKind.ANONYMOUS) { + // JavaC binary name for local class is like package.components.EnclosingClass$1SimpleName + // Extract the generated name by taking the part after the binary name of the declaring + // class. + String binaryName = getBinaryNameFromTypeBinding(currentType); + String declaringClassPrefix = + getBinaryNameFromTypeBinding(getEnclosingType(currentType)) + "$"; + simpleName = binaryName.substring(declaringClassPrefix.length()); + } else { + simpleName = asElement(erasure(currentType.asType())).getSimpleName().toString(); + } + classComponents.add(0, simpleName); + Element enclosingElement = currentType.getEnclosingElement(); + while (enclosingElement != null + && enclosingElement.getKind() != ElementKind.CLASS + && enclosingElement.getKind() != ElementKind.INTERFACE + && enclosingElement.getKind() != ElementKind.ENUM) { + enclosingElement = enclosingElement.getEnclosingElement(); + } + currentType = (TypeElement) enclosingElement; } + return ImmutableList.copyOf(classComponents); + } - return createDeclaredMethodDescriptor( - enclosingTypeDescriptor.toNullable(), - declarationMethodElement, - declarationMethodDescriptor, - parametersBuilder.build(), - returnTypeDescriptor); + /** Returns the binary name for a type element. */ + private String getBinaryNameFromTypeBinding(TypeElement typeElement) { + return elements.getBinaryName(typeElement).toString(); } - public DeclaredTypeDescriptor createDeclaredTypeDescriptor(TypeMirror typeMirror) { - return createDeclaredTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false); + private boolean isEnumSyntheticMethod(ExecutableElement methodElement) { + // Enum synthetic methods are not marked as such because per JLS 13.1 these methods are + // implicitly declared but are not marked as synthetic. + return getEnclosingType(methodElement).getKind() == ElementKind.ENUM + && (isValuesMethod(methodElement) || isValueOfMethod(methodElement)); } - DeclaredTypeDescriptor createDeclaredTypeDescriptor( - TypeMirror typeMirror, boolean inNullMarkedScope) { - return createTypeDescriptor(typeMirror, inNullMarkedScope, DeclaredTypeDescriptor.class); + private boolean isValueOfMethod(ExecutableElement methodElement) { + return methodElement.getSimpleName().contentEquals("valueOf") + && methodElement.getParameters().size() == 1 + && asTypeElement(methodElement.getParameters().get(0).asType()) + .getQualifiedName() + .contentEquals("java.lang.String"); } - T createTypeDescriptor( - TypeMirror typeMirror, boolean inNullMarkedScope, Class clazz) { - return clazz.cast(createTypeDescriptor(typeMirror, inNullMarkedScope)); + /** + * Returns true if instances of this type capture its outer instances; i.e. if it is an non member + * class, or an anonymous or local class defined in an instance context. + */ + private boolean capturesEnclosingInstance(TypeElement typeElement) { + if (isAnonymous(typeElement)) { + return hasOuterInstance(typeElement) || !isStatic(typeElement.getEnclosingElement()); + } + return hasOuterInstance(typeElement); } public boolean hasOuterInstance(TypeElement typeElement) { @@ -458,19 +592,11 @@ public boolean hasOuterInstance(TypeElement typeElement) { && !isInterface((TypeElement) typeElement.getEnclosingElement()); } - public boolean isAnonymous(TypeElement typeElement) { - return isAnonymous(typeElement.asType()); - } - - private boolean isFunctionalInterface(TypeMirror type) { - return false; - } - public FieldDescriptor createFieldDescriptor(VariableElement variableElement) { return createFieldDescriptor(variableElement, variableElement.asType()); } - public FieldDescriptor createFieldDescriptor(VariableElement variableElement, TypeMirror type) { + FieldDescriptor createFieldDescriptor(VariableElement variableElement, TypeMirror type) { boolean isStatic = isStatic(variableElement); Visibility visibility = getVisibility(variableElement); @@ -482,7 +608,7 @@ public FieldDescriptor createFieldDescriptor(VariableElement variableElement, Ty createTypeDescriptorWithNullability( type, variableElement.getAnnotationMirrors(), - isNullMarked(getEnclosingType(variableElement))); + enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()); boolean isEnumConstant = variableElement.getKind().equals(ElementKind.ENUM_CONSTANT); if (isEnumConstant) { @@ -491,14 +617,18 @@ public FieldDescriptor createFieldDescriptor(VariableElement variableElement, Ty } FieldDescriptor declarationFieldDescriptor = null; - if (!isSameType(variableElement.asType(), type)) { + if (!javacTypes.isSameType(variableElement.asType(), type)) { // Field references might be parameterized, and when they are we set the declaration // descriptor to the unparameterized declaration. declarationFieldDescriptor = createFieldDescriptor(variableElement, variableElement.asType()); } JsInfo jsInfo = JsInteropUtils.getJsInfo(variableElement); - boolean isCompileTimeConstant = variableElement.getConstantValue() != null; + Object constantValue = variableElement.getConstantValue(); + boolean isCompileTimeConstant = constantValue != null; + if (isCompileTimeConstant) { + thisTypeDescriptor = thisTypeDescriptor.toNonNullable(); + } boolean isFinal = isFinal(variableElement); return FieldDescriptor.newBuilder() .setEnclosingTypeDescriptor(enclosingTypeDescriptor) @@ -506,9 +636,11 @@ public FieldDescriptor createFieldDescriptor(VariableElement variableElement, Ty .setTypeDescriptor(thisTypeDescriptor) .setStatic(isStatic) .setVisibility(visibility) - .setJsInfo(jsInfo) + .setOriginalJsInfo(jsInfo) .setFinal(isFinal) .setCompileTimeConstant(isCompileTimeConstant) + .setConstantValue( + constantValue != null ? Literal.fromValue(constantValue, thisTypeDescriptor) : null) .setDeclarationDescriptor(declarationFieldDescriptor) .setEnumConstant(isEnumConstant) .setUnusableByJsSuppressed( @@ -517,31 +649,85 @@ public FieldDescriptor createFieldDescriptor(VariableElement variableElement, Ty .build(); } - public TypeDescriptor createTypeDescriptor(TypeMirror typeMirror) { - return createTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false); - } + /** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */ + MethodDescriptor createMethodDescriptor( + DeclaredTypeDescriptor enclosingTypeDescriptor, + ExecutableElement methodElement, + ExecutableElement declarationMethodElement) { - public Element asElement(TypeMirror typeMirror) { - if (typeMirror.getKind().isPrimitive()) { - return MoreTypes.asElement(typeMirror); + MethodDescriptor declarationMethodDescriptor = null; + + ImmutableList parameters = + methodElement.getParameters().stream() + .map(VariableElement::asType) + .collect(toImmutableList()); + + TypeMirror returnType = methodElement.getReturnType(); + if (isSpecialized(declarationMethodElement, parameters, returnType)) { + declarationMethodDescriptor = + createDeclarationMethodDescriptor( + declarationMethodElement, enclosingTypeDescriptor.toUnparameterizedTypeDescriptor()); } - if (typeMirror.getKind().equals(TypeKind.DECLARED)) { - return MoreTypes.asDeclared(typeMirror).asElement(); + + TypeDescriptor returnTypeDescriptor = + createTypeDescriptorWithNullability( + returnType, + declarationMethodElement.getAnnotationMirrors(), + enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()); + + ImmutableList.Builder parametersBuilder = ImmutableList.builder(); + for (int i = 0; i < parameters.size(); i++) { + parametersBuilder.add( + applyParameterNullabilityAnnotations( + createTypeDescriptorWithNullability( + parameters.get(i), + declarationMethodElement.getParameters().get(i).getAnnotationMirrors(), + enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()), + declarationMethodElement, + i)); } - return types.asElement(typeMirror); + + return createDeclaredMethodDescriptor( + enclosingTypeDescriptor.toNullable(), + declarationMethodElement, + declarationMethodDescriptor, + parametersBuilder.build(), + returnTypeDescriptor); } - public TypeMirror erasure(TypeMirror typeMirror) { - return types.erasure(typeMirror); + /** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */ + public MethodDescriptor createDeclarationMethodDescriptor(ExecutableElement methodElement) { + DeclaredTypeDescriptor enclosingTypeDescriptor = + createDeclaredTypeDescriptor(methodElement.getEnclosingElement().asType()); + return createDeclarationMethodDescriptor(methodElement, enclosingTypeDescriptor); } - public String getBinaryNameFromTypeBinding(TypeElement typeElement) { - return elements.getBinaryName(typeElement).toString(); + /** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */ + public MethodDescriptor createDeclarationMethodDescriptor( + ExecutableElement methodElement, DeclaredTypeDescriptor enclosingTypeDescriptor) { + return createMethodDescriptor(enclosingTypeDescriptor, methodElement, methodElement); } - /** Creates a TypeDescriptor from a TypeMirror. */ - TypeDescriptor createTypeDescriptor(TypeMirror typeMirror, boolean inNullMarkedScope) { - return createTypeDescriptorWithNullability(typeMirror, ImmutableList.of(), inNullMarkedScope); + /** + * Returns true if any of the type parameters has been specialized. + * + *

For example the type {@code List} specialized the type variable {@code T} from the + * class declaration. + */ + private boolean isSpecialized( + ExecutableElement declarationMethodElement, + List parameters, + TypeMirror returnType) { + return !isSameType(returnType, declarationMethodElement.getReturnType()) + || !Streams.zip( + parameters.stream(), + declarationMethodElement.getParameters().stream(), + (thisType, thatType) -> isSameType(thisType, thatType.asType())) + .allMatch(equals -> equals); + } + + private boolean isSameType(TypeMirror thisType, TypeMirror thatType) { + return javacTypes.isSameType(thisType, thatType); } private MethodDescriptor createDeclaredMethodDescriptor( @@ -560,7 +746,7 @@ private MethodDescriptor createDeclaredMethodDescriptor( boolean isStatic = isStatic(declarationMethodElement); Visibility visibility = getVisibility(declarationMethodElement); boolean isDefault = isDefaultMethod(declarationMethodElement); - JsInfo jsInfo = computeJsInfo(declarationMethodElement); + JsInfo jsInfo = JsInteropUtils.getJsInfo(declarationMethodElement); boolean isNative = isNative(declarationMethodElement) @@ -582,12 +768,6 @@ private MethodDescriptor createDeclaredMethodDescriptor( .build()); } - if (enclosingTypeDescriptor.getTypeDeclaration().isAnonymous() - && isConstructor - && enclosingTypeDescriptor.getSuperTypeDescriptor().hasJsConstructor()) { - jsInfo = JsInfo.Builder.from(jsInfo).setJsMemberType(JsMemberType.CONSTRUCTOR).build(); - } - boolean hasUncheckedCast = hasUncheckedCastAnnotation(declarationMethodElement); return MethodDescriptor.newBuilder() .setEnclosingTypeDescriptor(enclosingTypeDescriptor) @@ -596,8 +776,7 @@ private MethodDescriptor createDeclaredMethodDescriptor( .setDeclarationDescriptor(declarationMethodDescriptor) .setReturnTypeDescriptor(isConstructor ? enclosingTypeDescriptor : returnTypeDescriptor) .setTypeParameterTypeDescriptors(typeParameterTypeDescriptors) - .setJsInfo(jsInfo) - .setJsFunction(isOrOverridesJsFunctionMethod(declarationMethodElement)) + .setOriginalJsInfo(jsInfo) .setVisibility(visibility) .setStatic(isStatic) .setConstructor(isConstructor) @@ -605,9 +784,9 @@ private MethodDescriptor createDeclaredMethodDescriptor( .setFinal(isFinal(declarationMethodElement)) .setDefaultMethod(isDefault) .setAbstract(isAbstract(declarationMethodElement)) - // .setSynthetic(isSynthetic(declarationMethodElement)) - // .setEnumSyntheticMethod(isEnumSyntheticMethod(declarationMethodElement)) - // .setSideEffectFree(isAnnotatedWithHasNoSideEffects(declarationMethodElement)) + .setSynthetic(isSynthetic(declarationMethodElement)) + .setEnumSyntheticMethod(isEnumSyntheticMethod(declarationMethodElement)) + .setSideEffectFree(isAnnotatedWithHasNoSideEffects(declarationMethodElement)) .setUnusableByJsSuppressed( JsInteropAnnotationUtils.isUnusableByJsSuppressed(declarationMethodElement)) .setDeprecated(isDeprecated(declarationMethodElement)) @@ -615,169 +794,78 @@ private MethodDescriptor createDeclaredMethodDescriptor( .build(); } - private boolean hasUncheckedCastAnnotation(Element element) { - return hasAnnotation(element, "javaemul.internal.annotations.UncheckedCast"); + public ImmutableList createTypeDescriptors( + List typeMirrors, boolean inNullMarkedScope) { + return typeMirrors.stream() + .map(typeMirror -> createTypeDescriptor(typeMirror, inNullMarkedScope)) + .collect(toImmutableList()); } - private boolean isOrOverridesJsFunctionMethod(ExecutableElement methodBinding) { - Element declaringType = methodBinding.getEnclosingElement(); - if (JsInteropUtils.isJsFunction(declaringType)) { - throw new RuntimeException(" not implemented"); - } - return false; - } + public ImmutableList createTypeDescriptors( + List typeMirrors, + boolean inNullMarkedScope, + Class clazz, + Element declarationElement) { + ImmutableList.Builder typeDescriptorsBuilder = ImmutableList.builder(); + for (int i = 0; i < typeMirrors.size(); i++) { + final int index = i; + TypeDescriptor typeDescriptor = + createTypeDescriptor(typeMirrors.get(i), inNullMarkedScope, clazz); + /* typeDescriptorsBuilder.add(typeDescriptor); - private JsInfo computeJsInfo(ExecutableElement method) { - JsInfo originalJsInfo = JsInteropUtils.getJsInfo(method); - if (originalJsInfo.isJsOverlay() - || originalJsInfo.getJsName() != null - || originalJsInfo.getJsNamespace() != null) { - // Do not examine overridden methods if the method is marked as JsOverlay or it has a JsMember - // annotation that customizes the name. - return originalJsInfo; + typeDescriptorsBuilder.add( + clazz.cast( + applyNullabilityAnnotations( + createTypeDescriptor(typeMirrors.get(i), inNullMarkedScope, clazz), + declarationElement, + position -> + position.type == TargetType.CLASS_EXTENDS && position.type_index == index)));*/ } - // Don't inherit @JsAsync annotation from overridden methods. - return JsInfo.Builder.from(originalJsInfo).setJsAsync(originalJsInfo.isJsAsync()).build(); - } - - private boolean isSpecialized( - ExecutableElement declarationMethodElement, - List parameters, - TypeMirror returnType) { - return !isSameType(returnType, declarationMethodElement.getReturnType()) - || !Streams.zip( - parameters.stream(), - declarationMethodElement.getParameters().stream(), - (thisType, thatType) -> isSameType(thisType, thatType.asType())) - .allMatch(equals -> equals); + return typeDescriptorsBuilder.build(); } - private ImmutableList createTypeDescriptors( - List typeMirrors, boolean inNullMarkedScope) { + public ImmutableList createTypeDescriptors( + List typeMirrors, boolean inNullMarkedScope, Class clazz) { return typeMirrors.stream() - .map(typeMirror -> createTypeDescriptor(typeMirror, inNullMarkedScope)) + .map(typeMirror -> createTypeDescriptor(typeMirror, inNullMarkedScope, clazz)) .collect(toImmutableList()); } - private ImmutableList createTypeDescriptors( - List typeMirrors, - boolean inNullMarkedScope, - Class clazz, - Element declarationElement) { - ImmutableList.Builder typeDescriptorsBuilder = ImmutableList.builder(); - for (int i = 0; i < typeMirrors.size(); i++) { - final int index = i; - typeDescriptorsBuilder.add( - createTypeDescriptor(typeMirrors.get(i), inNullMarkedScope, clazz)); - } - return typeDescriptorsBuilder.build(); + private TypeElement getTypeElement(String qualifiedSourceName) { + return elements.getTypeElement(qualifiedSourceName); } - private TypeVariable createTypeVariable(javax.lang.model.type.TypeVariable typeVariable) { - if (typeVariable.getKind().equals(TypeKind.WILDCARD)) { - return createWildcardTypeVariable(typeVariable.getUpperBound()); + private Element asElement(TypeMirror typeMirror) { + if (typeMirror.getKind().isPrimitive()) { + return MoreTypes.asElement(typeMirror); } - - Supplier boundTypeDescriptorFactory = - () -> createTypeDescriptor(typeVariable.getUpperBound()); - - List classComponents = getClassComponents(typeVariable); - - return TypeVariable.newBuilder() - .setUpperBoundTypeDescriptorSupplier(boundTypeDescriptorFactory) // TODO - .setUniqueKey( - String.join("::", classComponents) - + (typeVariable.getUpperBound() != null - ? typeVariable.getUpperBound().toString() - : "")) - .setName(typeVariable.asElement().getSimpleName().toString()) - .build(); - } - - private TypeVariable createWildcardTypeVariable(TypeMirror bound) { - return TypeVariable.newBuilder() - .setUpperBoundTypeDescriptorSupplier(() -> createTypeDescriptor(bound)) - .setWildcardOrCapture(true) - .setName("?") - .setUniqueKey("::?::" + (bound != null ? bound.toString() : "")) - .build(); - } - - private boolean capturesEnclosingInstance(TypeElement typeElement) { - if (isAnonymous(typeElement)) { - return hasOuterInstance(typeElement) || !isStatic(typeElement.getEnclosingElement()); + if (typeMirror.getKind().equals(TypeKind.DECLARED)) { + return MoreTypes.asDeclared(typeMirror).asElement(); } - return hasOuterInstance(typeElement); + return javacTypes.asElement(typeMirror); } - private static boolean isNullMarked(TypeElement classSymbol) { - return hasNullMarkedAnnotation(classSymbol); + private TypeElement asTypeElement(TypeMirror typeMirror) { + return (TypeElement) asElement(typeMirror); } - private static boolean hasNullMarkedAnnotation(TypeElement classSymbol) { - if (findAnnotationBindingByName( - classSymbol.getAnnotationMirrors(), "org.jspecify.nullness.NullMarked") - != null) { - // The type is NullMarked, no need to look further. - return true; - } - - Element enclosingElement = classSymbol.getEnclosingElement(); - return enclosingElement instanceof TypeElement - && hasNullMarkedAnnotation((TypeElement) enclosingElement); + private TypeMirror erasure(TypeMirror typeMirror) { + return javacTypes.erasure(typeMirror); } - private TypeDescriptor createTypeDescriptorWithNullability( - TypeMirror typeMirror, - List elementAnnotations, - boolean inNullMarkedScope) { - if (typeMirror == null || typeMirror.getKind() == TypeKind.NONE) { - return null; - } - - if (typeMirror.getKind().isPrimitive() || typeMirror.getKind() == TypeKind.VOID) { - return PrimitiveTypes.get(typeMirror.toString()); - } - - if (typeMirror.getKind() == TypeKind.INTERSECTION) { - throw new RuntimeException("Not implemented"); - } - - if (typeMirror.getKind() == TypeKind.UNION) { - throw new RuntimeException("Not implemented"); - } - - if (typeMirror.getKind() == TypeKind.NULL) { - return TypeDescriptors.get().javaLangObject; - } - - if (typeMirror.getKind() == TypeKind.TYPEVAR) { - return createTypeVariable((javax.lang.model.type.TypeVariable) typeMirror); - } - - if (typeMirror.getKind() == TypeKind.WILDCARD) { - return createWildcardTypeVariable( - ((javax.lang.model.type.WildcardType) typeMirror).getExtendsBound()); - } - - boolean isNullable = isNullable(typeMirror, elementAnnotations, inNullMarkedScope); - if (typeMirror.getKind() == TypeKind.ARRAY) { - ArrayType arrayType = (ArrayType) typeMirror; - TypeDescriptor componentTypeDescriptor = - createTypeDescriptor(arrayType.getComponentType(), inNullMarkedScope); - return ArrayTypeDescriptor.newBuilder() - .setComponentTypeDescriptor(componentTypeDescriptor) - .setNullable(isNullable) - .build(); - } - - return withNullability( - createDeclaredType(MoreTypes.asDeclared(typeMirror), inNullMarkedScope), isNullable); + private PackageElement getPackageOf(TypeElement typeElement) { + return elements.getPackageOf(typeElement); } private DeclaredTypeDescriptor createDeclaredType( final DeclaredType classType, boolean inNullMarkedScope) { + DeclaredTypeDescriptor cachedTypeDescriptor = + getCachedTypeDescriptor(classType, inNullMarkedScope); + if (cachedTypeDescriptor != null) { + return cachedTypeDescriptor; + } + Supplier> declaredMethods = () -> getDeclaredMethods(classType).stream() @@ -809,159 +897,176 @@ private DeclaredTypeDescriptor createDeclaredType( .setTypeDeclaration(typeDeclaration) .setEnclosingTypeDescriptor(createDeclaredTypeDescriptor(classType.getEnclosingType())) .setSuperTypeDescriptorFactory( - () -> - createDeclaredTypeDescriptor( - types.directSupertypes(classType).stream() - .filter(e -> !isInterface(e)) - .findFirst() - .orElse(null), - inNullMarkedScope)) + td -> + td.isInterface() + ? null + : createDeclaredTypeDescriptor( + javacTypes.directSupertypes(classType).stream() + .filter(type -> !asTypeElement(type).getKind().isInterface()) + .findFirst() + .orElse(null), + inNullMarkedScope)) .setInterfaceTypeDescriptorsFactory( td -> createTypeDescriptors( - types.directSupertypes(classType).stream() - .filter(e -> isInterface(e)) + javacTypes.directSupertypes(classType).stream() + .filter(type -> !asTypeElement(type).getKind().isInterface()) .collect(toImmutableList()), inNullMarkedScope, DeclaredTypeDescriptor.class)) .setSingleAbstractMethodDescriptorFactory( td -> { - ExecutableElement functionalInterfaceMethod = - getFunctionalInterfaceMethod(classType); - - ExecutableElement asMemberOf = - MoreElements.asExecutable( - MoreTypes.asElement( - types.asMemberOf(classType, functionalInterfaceMethod))); - - return createMethodDescriptor( - td, asMemberOf, getFunctionalInterfaceMethodDecl(classType)); + // MethodSymbol functionalInterfaceMethod = + // getFunctionalInterfaceMethod(classType); + throw new UnsupportedOperationException("Not implemented yet"); + /* return createMethodDescriptor( + td, + (MethodSymbol) + functionalInterfaceMethod.asMemberOf( + ((ClassSymbol) classType.asElement()).asType(), internalTypes), + getFunctionalInterfaceMethodDecl(classType));*/ }) .setTypeArgumentDescriptors( createTypeDescriptors(getTypeArguments(classType), inNullMarkedScope)) .setDeclaredFieldDescriptorsFactory(declaredFields) .setDeclaredMethodDescriptorsFactory(declaredMethods) .build(); + putTypeDescriptorInCache(inNullMarkedScope, classType, typeDescriptor); return typeDescriptor; } + private DeclaredTypeDescriptor getCachedTypeDescriptor( + DeclaredType classType, boolean inNullMarkedScope) { + Map cache = + inNullMarkedScope + ? cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope + : cachedDeclaredTypeDescriptorByDeclaredTypeOutOfNullMarkedScope; + return cache.get(classType); + } + + private void putTypeDescriptorInCache( + boolean inNullMarkedScope, DeclaredType classType, DeclaredTypeDescriptor typeDescriptor) { + Map cache = + inNullMarkedScope + ? cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope + : cachedDeclaredTypeDescriptorByDeclaredTypeOutOfNullMarkedScope; + cache.put(classType, typeDescriptor); + } + private ImmutableList getDeclaredMethods(DeclaredType classType) { return ElementFilter.methodsIn(classType.asElement().getEnclosedElements()).stream() .collect(toImmutableList()); } - public ImmutableList createTypeDescriptors( - List typeMirrors, boolean inNullMarkedScope, Class clazz) { - return typeMirrors.stream() - .map(typeMirror -> createTypeDescriptor(typeMirror, inNullMarkedScope, clazz)) - .collect(toImmutableList()); - } + private String getJsNamespace(TypeElement classSymbol) { + String jsNamespace = JsInteropAnnotationUtils.getJsNamespace(classSymbol); + if (jsNamespace != null) { + return jsNamespace; + } - private ExecutableElement getFunctionalInterfaceMethodDecl(TypeMirror typeMirror) { - throw new RuntimeException("Not implemented"); + // Maybe namespace is set via package-info file? + boolean isTopLevelType = classSymbol.getEnclosingElement().getKind() == ElementKind.PACKAGE; + if (isTopLevelType) { + return getBinaryNameFromTypeBinding(classSymbol); + } + return null; } - private boolean isNullable( - TypeMirror typeMirror, - List elementAnnotations, - boolean inNullMarkedScope) { - checkArgument(!typeMirror.getKind().isPrimitive()); - - if (typeMirror.getKind().equals(TypeKind.VOID)) { - // Void is always nullable. - return true; + TypeDeclaration createDeclarationForType(final TypeElement typeElement) { + if (typeElement == null) { + return null; } - List allAnnotations = new ArrayList<>(); - allAnnotations.addAll(elementAnnotations); - allAnnotations.addAll(typeMirror.getAnnotationMirrors()); + // Compute these first since they're reused in other calculations. + String packageName = getPackageOf(typeElement).getQualifiedName().toString(); + boolean isAbstract = isAbstract(typeElement) && !isInterface(typeElement); + Kind kind = getKindFromTypeBinding(typeElement); + // TODO(b/341721484): Even though enums can not have the final modifier, turbine make them final + // in the header jars. + boolean isFinal = isFinal(typeElement) && kind != Kind.ENUM; - for (AnnotationMirror annotationMirror : allAnnotations) { - if (isNonNullAnnotation(annotationMirror)) { - return false; - } - if (isNullableAnnotation(annotationMirror)) { - return true; - } - } + Supplier> declaredMethods = + () -> { + ImmutableList.Builder listBuilder = ImmutableList.builder(); + for (ExecutableElement methodElement : + ElementFilter.methodsIn(typeElement.getEnclosedElements())) { + MethodDescriptor methodDescriptor = createDeclarationMethodDescriptor(methodElement); + listBuilder.add(methodDescriptor); + } + return listBuilder.build(); + }; - return !inNullMarkedScope; - } + Supplier> declaredFields = + () -> + typeElement.getEnclosedElements().stream() + .filter( + element -> + element.getKind() == ElementKind.FIELD + || element.getKind() == ElementKind.ENUM_CONSTANT) + .map(VariableElement.class::cast) + .map(this::createFieldDescriptor) + .collect(toImmutableList()); - private ImmutableList getClassComponents( - javax.lang.model.type.TypeVariable typeVariable) { - Element enclosingElement = typeVariable.asElement().getEnclosingElement(); - if (enclosingElement.getKind() == ElementKind.CLASS - || enclosingElement.getKind() == ElementKind.INTERFACE - || enclosingElement.getKind() == ElementKind.ENUM) { - return ImmutableList.builder() - .addAll(getClassComponents(enclosingElement)) - .add( - // If it is a class-level type variable, use the simple name (with prefix "C_") as the - // current name component. - "C_" + typeVariable.asElement().getSimpleName()) - .build(); - } else { - return ImmutableList.builder() - .addAll(getClassComponents(enclosingElement.getEnclosingElement())) - .add( - "M_" - + enclosingElement.getSimpleName() - + "_" - + typeVariable.asElement().getSimpleName()) - .build(); - } - } + JsEnumInfo jsEnumInfo = JsInteropUtils.getJsEnumInfo(typeElement); - private ImmutableList getClassComponents(Element element) { - if (!(element instanceof TypeElement)) { - return ImmutableList.of(); - } - TypeElement typeElement = (TypeElement) element; - List classComponents = new ArrayList<>(); - TypeElement currentType = typeElement; - while (currentType != null) { - String simpleName; - if (currentType.getNestingKind() == NestingKind.LOCAL - || currentType.getNestingKind() == NestingKind.ANONYMOUS) { - // JavaC binary name for local class is like package.components.EnclosingClass$1SimpleName - // Extract the generated name by taking the part after the binary name of the declaring - // class. - String binaryName = getBinaryNameFromTypeBinding(currentType); - String declaringClassPrefix = - getBinaryNameFromTypeBinding(getEnclosingType(currentType)) + "$"; - simpleName = binaryName.substring(declaringClassPrefix.length()); - } else { - simpleName = asElement(erasure(currentType.asType())).getSimpleName().toString(); - } - classComponents.add(0, simpleName); - Element enclosingElement = currentType.getEnclosingElement(); - while (enclosingElement != null - && enclosingElement.getKind() != ElementKind.CLASS - && enclosingElement.getKind() != ElementKind.INTERFACE - && enclosingElement.getKind() != ElementKind.ENUM) { - enclosingElement = enclosingElement.getEnclosingElement(); - } - currentType = (TypeElement) enclosingElement; - } - return ImmutableList.copyOf(classComponents); - } + List typeParameterElements = getTypeParameters(typeElement); - private Kind getKindFromTypeBinding(TypeElement typeElement) { - if (isEnum(typeElement) && !isAnonymous(typeElement)) { - // Do not consider the anonymous classes that constitute enum values as Enums, only the - // enum "class" itself is considered Kind.ENUM. - return Kind.ENUM; - } else if (isClass(typeElement) || (isEnum(typeElement) && isAnonymous(typeElement))) { - return Kind.CLASS; - } else if (isInterface(typeElement)) { - return Kind.INTERFACE; - } - throw new InternalCompilerError("Type binding %s not handled.", typeElement); + // boolean isNullMarked = isNullMarked(typeElement, packageInfoCache); + boolean isNullMarked = false; + return newBuilder() + .setClassComponents(getClassComponents(typeElement)) + .setEnclosingTypeDeclaration(createDeclarationForType(getEnclosingType(typeElement))) + .setInterfaceTypeDescriptorsFactory( + () -> + createTypeDescriptors( + typeElement.getInterfaces(), + isNullMarked, + DeclaredTypeDescriptor.class, + typeElement)) + .setUnparameterizedTypeDescriptorFactory( + () -> createDeclaredTypeDescriptor(typeElement.asType())) + .setHasAbstractModifier(isAbstract) + .setKind(kind) + // .setAnnotation(isAnnotation(typeElement)) + .setCapturingEnclosingInstance(capturesEnclosingInstance(typeElement)) + .setFinal(isFinal) + .setFunctionalInterface(isFunctionalInterface(typeElement.asType())) + .setJsFunctionInterface(JsInteropUtils.isJsFunction(typeElement)) + .setJsType(JsInteropUtils.isJsType(typeElement)) + .setJsEnumInfo(jsEnumInfo) + .setNative(JsInteropUtils.isJsNativeType(typeElement)) + .setAnonymous(isAnonymous(typeElement)) + .setLocal(isLocal(typeElement)) + .setSimpleJsName(getJsName(typeElement)) + .setCustomizedJsNamespace(getJsNamespace(typeElement)) + .setNullMarked(isNullMarked) + /* .setOriginalSimpleSourceName( + typeElement.getSimpleName() != null ? typeElement.getSimpleName().toString() : null)*/ + .setPackageName(packageName) + /* .setSuperTypeDescriptorFactory( + () -> + (DeclaredTypeDescriptor) + applyNullabilityAnnotations( + createDeclaredTypeDescriptor(typeElement.getSuperclass(), isNullMarked), + typeElement, + position -> + position.type == TargetType.CLASS_EXTENDS && position.type_index == -1))*/ + .setTypeParameterDescriptors( + typeParameterElements.stream() + .map(TypeParameterElement::asType) + .map(javax.lang.model.type.TypeVariable.class::cast) + .map(this::createTypeVariable) + .collect(toImmutableList())) + .setVisibility(getVisibility(typeElement)) + .setDeclaredMethodDescriptorsFactory(declaredMethods) + .setDeclaredFieldDescriptorsFactory(declaredFields) + .setUnusableByJsSuppressed(JsInteropAnnotationUtils.isUnusableByJsSuppressed(typeElement)) + .setDeprecated(isDeprecated(typeElement)) + .build(); } - private ExecutableElement getFunctionalInterfaceMethod(TypeMirror typeMirror) { - throw new UnsupportedOperationException(); + private boolean isFunctionalInterface(TypeMirror type) { + return type.getAnnotationsByType(FunctionalInterface.class).length > 0; } public MemberDescriptor getDefaultConstructor(TypeElement clazz) { @@ -976,26 +1081,4 @@ public MemberDescriptor getDefaultConstructor(TypeElement clazz) { .orElseGet(() -> AstUtils.createImplicitConstructorDescriptor(typeDeclaration)); return ctorMethodDescriptorFromJavaConstructor(ctor); } - - private static String getCtorName(MethodDescriptor methodDescriptor) { - // Synthesize a name that is unique per class to avoid property clashes in JS. - return MethodDescriptor.CTOR_METHOD_PREFIX - + "__" - + methodDescriptor.getEnclosingTypeDescriptor().getMangledName(); - } - - private static MethodDescriptor ctorMethodDescriptorFromJavaConstructor( - MethodDescriptor constructor) { - return constructor.transform( - builder -> - builder - .setReturnTypeDescriptor(PrimitiveTypes.VOID) - .setName(getCtorName(constructor)) - .setConstructor(false) - .setStatic(false) - .setJsInfo(JsInfo.NONE) - .removeParameterOptionality() - .setOrigin(MethodDescriptor.MethodOrigin.SYNTHETIC_CTOR_FOR_CONSTRUCTOR) - .setVisibility(Visibility.PUBLIC)); - } }