Skip to content

Commit

Permalink
[javac] Thread nullability and type arguments through `JavaEnvironmen…
Browse files Browse the repository at this point in the history
…t` and `CompilationUnitBuilder`.

PiperOrigin-RevId: 703515165
  • Loading branch information
rluble authored and copybara-github committed Dec 6, 2024
1 parent c87b78c commit b8f6940
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ private Variable createVariable(JCVariableDecl variableDeclaration, boolean isPa
environment.createVariable(
getNamePosition(variableElement.getSimpleName().toString(), variableDeclaration),
variableElement,
isParameter);
isParameter,
getCurrentType().getDeclaration().isNullMarked());
variableByVariableElement.put(variableElement, variable);
return variable;
}
Expand Down Expand Up @@ -870,10 +871,12 @@ private Expression convertMemberReference(JCMemberReference memberReference) {
.build();
}

List<TypeDescriptor> typeArguments = convertTypeArguments(memberReference.getTypeArguments());
MethodDescriptor targetMethodDescriptor =
environment.createMethodDescriptor(
/* methodType= */ memberReference.referentType.asMethodType(),
/* declarationMethodElement= */ methodSymbol);
/* declarationMethodElement= */ methodSymbol,
typeArguments);
Expression qualifier = convertExpressionOrNull(memberReference.getQualifierExpression());
if (qualifier instanceof JavaScriptConstructorReference) {
// The qualifier was just the class name, remove it.
Expand Down Expand Up @@ -977,12 +980,14 @@ private Expression convertNewClass(JCNewClass expression) {

MethodSymbol constructorElement = (MethodSymbol) expression.constructor;
DeclaredTypeDescriptor targetType = environment.createDeclaredTypeDescriptor(expression.type);
var typeArguments = convertTypeArguments(expression.getTypeArguments());
MethodDescriptor constructorMethodDescriptor =
environment.createMethodDescriptor(
/* enclosingTypeDescriptor= */ targetType,
/* methodType= */ (ExecutableType)
constructorElement.asMemberOf(expression.type, environment.internalTypes).asType(),
/* declarationMethodElement= */ constructorElement);
/* declarationMethodElement= */ constructorElement,
typeArguments);
Expression qualifier = convertExpressionOrNull(expression.getEnclosingExpression());
List<Expression> arguments =
convertArguments(constructorMethodDescriptor, expression.getArguments());
Expand Down Expand Up @@ -1021,10 +1026,12 @@ private Expression convertMethodInvocation(JCMethodInvocation methodInvocation)
qualifier = null;
}

var typeArguments = convertTypeArguments(methodInvocation.getTypeArguments());
MethodDescriptor methodDescriptor =
environment.createMethodDescriptor(
/* methodType= */ methodInvocation.getMethodSelect().type.asMethodType(),
/* declarationMethodElement= */ methodSymbol);
/* declarationMethodElement= */ methodSymbol,
typeArguments);

if (methodDescriptor.isConstructor()
&& methodDescriptor.isMemberOf(TypeDescriptors.get().javaLangEnum)) {
Expand Down Expand Up @@ -1078,6 +1085,16 @@ private List<Expression> convertArguments(
return AstUtils.maybePackageVarargs(methodDescriptor, arguments);
}

private ImmutableList<TypeDescriptor> convertTypeArguments(
List<? extends JCExpression> typeArguments) {
if (typeArguments == null) {
return ImmutableList.of();
}
return environment.createTypeDescriptors(
typeArguments.stream().map(e -> e.type).collect(toImmutableList()),
getCurrentType().getDeclaration().isNullMarked());
}

/**
* Returns a qualifier for a method invocation that doesn't have one, specifically,
* instanceMethod() will return a resolved qualifier that may refer to "this" or to the enclosing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import static com.google.j2cl.transpiler.frontend.common.FrontendConstants.UNCHECKED_CAST_ANNOTATION_NAME;
import static com.google.j2cl.transpiler.frontend.common.FrontendConstants.WASM_ANNOTATION_NAME;
import static com.google.j2cl.transpiler.frontend.javac.AnnotationUtils.findAnnotationByName;
import static com.google.j2cl.transpiler.frontend.javac.AnnotationUtils.getAnnotationName;
import static com.google.j2cl.transpiler.frontend.javac.AnnotationUtils.getAnnotationParameterString;
import static com.google.j2cl.transpiler.frontend.javac.AnnotationUtils.hasAnnotation;
import static com.google.j2cl.transpiler.frontend.javac.AnnotationUtils.hasNullMarkedAnnotation;
import static com.google.j2cl.transpiler.frontend.javac.JsInteropAnnotationUtils.getJsNamespace;
import static com.google.j2cl.transpiler.frontend.javac.KtInteropUtils.getKtVariance;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
Expand All @@ -42,9 +44,11 @@
import com.google.j2cl.transpiler.ast.JsEnumInfo;
import com.google.j2cl.transpiler.ast.JsInfo;
import com.google.j2cl.transpiler.ast.KtInfo;
import com.google.j2cl.transpiler.ast.KtVariance;
import com.google.j2cl.transpiler.ast.Literal;
import com.google.j2cl.transpiler.ast.MethodDescriptor;
import com.google.j2cl.transpiler.ast.MethodDescriptor.ParameterDescriptor;
import com.google.j2cl.transpiler.ast.NullabilityAnnotation;
import com.google.j2cl.transpiler.ast.PackageDeclaration;
import com.google.j2cl.transpiler.ast.PostfixOperator;
import com.google.j2cl.transpiler.ast.PrefixOperator;
Expand All @@ -64,6 +68,7 @@
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.TypeVariableSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.Type;
Expand Down Expand Up @@ -241,13 +246,16 @@ static BinaryOperator getBinaryOperator(com.sun.source.tree.Tree.Kind operator)
}

Variable createVariable(
SourcePosition sourcePosition, VariableElement variableElement, boolean isParameter) {
SourcePosition sourcePosition,
VariableElement variableElement,
boolean isParameter,
boolean inNullMarkedScope) {
TypeMirror type = variableElement.asType();
String name = variableElement.getSimpleName().toString();
TypeDescriptor typeDescriptor =
isParameter
? createTypeDescriptorWithNullability(
type, variableElement.getAnnotationMirrors(), /* inNullMarkedScope= */ false)
type, variableElement.getAnnotationMirrors(), inNullMarkedScope)
: createTypeDescriptor(type);
boolean isFinal = isFinal(variableElement);
boolean isUnusableByJsSuppressed =
Expand Down Expand Up @@ -319,11 +327,15 @@ private TypeDescriptor createTypeDescriptorWithNullability(
}

if (typeMirror.getKind() == TypeKind.TYPEVAR) {
return createTypeVariable((javax.lang.model.type.TypeVariable) typeMirror);
return createTypeVariable(
(javax.lang.model.type.TypeVariable) typeMirror, elementAnnotations, inNullMarkedScope);
}

if (typeMirror.getKind() == TypeKind.WILDCARD) {
return createWildcardTypeVariable(((WildcardType) typeMirror).getExtendsBound());
return createWildcardTypeVariable(
((WildcardType) typeMirror).getExtendsBound(),
((WildcardType) typeMirror).getSuperBound(),
inNullMarkedScope);
}

boolean isNullable = isNullable(typeMirror, elementAnnotations, inNullMarkedScope);
Expand Down Expand Up @@ -351,11 +363,6 @@ private boolean isNullable(
boolean inNullMarkedScope) {
checkArgument(!typeMirror.getKind().isPrimitive());

if (asTypeElement(typeMirror).getQualifiedName().contentEquals("java.lang.Void")) {
// Void is always nullable.
return true;
}

Iterable<? extends AnnotationMirror> allAnnotations =
Iterables.concat(elementAnnotations, typeMirror.getAnnotationMirrors());

Expand Down Expand Up @@ -383,33 +390,47 @@ private static boolean isNullableAnnotation(AnnotationMirror annotation) {
annotationType.asElement().getQualifiedName().toString());
}

private TypeVariable createTypeVariable(javax.lang.model.type.TypeVariable typeVariable) {
private TypeVariable createTypeVariable(
javax.lang.model.type.TypeVariable typeVariable,
List<? extends AnnotationMirror> elementAnnotations,
boolean inNullMarkedScope) {
if (typeVariable instanceof CapturedType) {
return createWildcardTypeVariable(typeVariable.getUpperBound());
return createWildcardTypeVariable(
typeVariable.getUpperBound(), typeVariable.getLowerBound(), inNullMarkedScope);
}

Supplier<TypeDescriptor> boundTypeDescriptorFactory =
() -> createTypeDescriptor(typeVariable.getUpperBound());
() -> createTypeDescriptor(typeVariable.getUpperBound(), inNullMarkedScope);

List<String> classComponents = getClassComponents(typeVariable);
KtVariance ktVariance =
getKtVariance(((TypeVariableSymbol) typeVariable.asElement()).baseSymbol());
return TypeVariable.newBuilder()
.setUpperBoundTypeDescriptorFactory(boundTypeDescriptorFactory)
.setUniqueKey(
String.join("::", classComponents)
+ (typeVariable.getUpperBound() != null
? typeVariable.getUpperBound().toString()
: ""))
.setKtVariance(KtInteropUtils.getKtVariance(typeVariable.asElement()))
.setKtVariance(ktVariance)
.setName(typeVariable.asElement().getSimpleName().toString())
.setNullabilityAnnotation(getNullabilityAnnotation(typeVariable, elementAnnotations))
.build();
}

private TypeVariable createWildcardTypeVariable(@Nullable TypeMirror bound) {
private TypeVariable createWildcardTypeVariable(
@Nullable TypeMirror upperBound, @Nullable TypeMirror lowerBound, boolean inNullMarkedScope) {
return TypeVariable.newBuilder()
.setUpperBoundTypeDescriptorFactory(() -> createTypeDescriptor(bound))
.setUpperBoundTypeDescriptorFactory(
() -> createTypeDescriptor(upperBound, inNullMarkedScope))
.setLowerBoundTypeDescriptor(createTypeDescriptor(lowerBound, inNullMarkedScope))
.setWildcard(true)
.setName("?")
.setUniqueKey("::?::" + (bound != null ? bound.toString() : ""))
.setUniqueKey(
"::^::"
+ (upperBound != null ? upperBound.toString() : "")
+ "::v::"
+ (lowerBound != null ? lowerBound.toString() : ""))
.build();
}

Expand Down Expand Up @@ -590,18 +611,22 @@ FieldDescriptor createFieldDescriptor(VariableElement variableElement, TypeMirro
* @param declarationMethodElement the method declaration.
*/
MethodDescriptor createMethodDescriptor(
ExecutableType methodType, ExecutableElement declarationMethodElement) {
ExecutableType methodType,
ExecutableElement declarationMethodElement,
List<TypeDescriptor> typeArguments) {

DeclaredTypeDescriptor enclosingTypeDescriptor =
createDeclaredTypeDescriptor(declarationMethodElement.getEnclosingElement().asType());
return createMethodDescriptor(enclosingTypeDescriptor, methodType, declarationMethodElement);
return createMethodDescriptor(
enclosingTypeDescriptor, methodType, declarationMethodElement, typeArguments);
}

/** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */
MethodDescriptor createMethodDescriptor(
DeclaredTypeDescriptor enclosingTypeDescriptor,
ExecutableType methodType,
ExecutableElement declarationMethodElement) {
ExecutableElement declarationMethodElement,
List<TypeDescriptor> typeArguments) {

// TODO(b/380911302): Remove redundancy in the creation of method descriptors.
// The enclosing type descriptor might be a subclass of the actual type descriptor, hence
Expand Down Expand Up @@ -647,19 +672,23 @@ MethodDescriptor createMethodDescriptor(
}

return createDeclaredMethodDescriptor(
enclosingTypeDescriptor.toNullable(),
enclosingTypeDescriptor,
declarationMethodElement,
declarationMethodDescriptor,
parametersBuilder.build(),
returnTypeDescriptor);
returnTypeDescriptor,
typeArguments);
}

/** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */
MethodDescriptor createMethodDescriptor(ExecutableElement methodElement) {
DeclaredTypeDescriptor enclosingTypeDescriptor =
createDeclaredTypeDescriptor(methodElement.getEnclosingElement().asType());
return createMethodDescriptor(
enclosingTypeDescriptor, (ExecutableType) methodElement.asType(), methodElement);
enclosingTypeDescriptor,
(ExecutableType) methodElement.asType(),
methodElement,
ImmutableList.of());
}


Expand Down Expand Up @@ -804,7 +833,8 @@ private MethodDescriptor createDeclaredMethodDescriptor(
ExecutableElement declarationMethodElement,
MethodDescriptor declarationMethodDescriptor,
List<TypeDescriptor> parameters,
TypeDescriptor returnTypeDescriptor) {
TypeDescriptor returnTypeDescriptor,
List<TypeDescriptor> typeArguments) {
ImmutableList<TypeVariable> typeParameterTypeDescriptors =
declarationMethodElement.getTypeParameters().stream()
.map(Element::asType)
Expand Down Expand Up @@ -851,6 +881,7 @@ private MethodDescriptor createDeclaredMethodDescriptor(
.setDeclarationDescriptor(declarationMethodDescriptor)
.setReturnTypeDescriptor(isConstructor ? enclosingTypeDescriptor : returnTypeDescriptor)
.setTypeParameterTypeDescriptors(typeParameterTypeDescriptors)
.setTypeArgumentTypeDescriptors(typeArguments)
.setExceptionTypeDescriptors(thrownExceptions)
.setOriginalJsInfo(jsInfo)
.setOriginalKtInfo(ktInfo)
Expand Down Expand Up @@ -1002,12 +1033,15 @@ public ImmutableList<TypeDescriptor> createTypeArgumentDescriptors(
typeMirrors.stream(),
declaredTypeParameters.stream(),
(typeMirror, typeParameter) -> {
javax.lang.model.type.TypeVariable typeVariable =
(javax.lang.model.type.TypeVariable) typeParameter.asType();
if (typeMirror.getKind() == TypeKind.WILDCARD
&& isNullOrJavaLangObject(((WildcardType) typeMirror).getExtendsBound())) {
&& isNullOrJavaLangObject(((WildcardType) typeMirror).getExtendsBound())
&& !isNullOrJavaLangObject(typeVariable.getUpperBound())) {
// If this is a wildcard but the bound is not specified (or is Object), we might be
// able to get a tighter bound from the declaration.
return createWildcardTypeVariable(
((javax.lang.model.type.TypeVariable) typeParameter.asType()).getUpperBound());
typeVariable.getUpperBound(), /* lowerBound= */ null, inNullMarkedScope);
}
return createTypeDescriptor(typeMirror, inNullMarkedScope);
})
Expand Down Expand Up @@ -1204,6 +1238,14 @@ TypeDeclaration createDeclarationForType(final TypeElement typeElement) {
return TypeDeclaration.newBuilder()
.setClassComponents(getClassComponents(typeElement))
.setEnclosingTypeDeclaration(createDeclarationForType(getEnclosingType(typeElement)))
.setSuperTypeDescriptorFactory(
() ->
(DeclaredTypeDescriptor)
applyNullabilityAnnotations(
createDeclaredTypeDescriptor(typeElement.getSuperclass(), isNullMarked),
typeElement,
position ->
position.type == TargetType.CLASS_EXTENDS && position.type_index == -1))
.setInterfaceTypeDescriptorsFactory(
() ->
createTypeDescriptors(
Expand Down Expand Up @@ -1240,19 +1282,11 @@ TypeDeclaration createDeclarationForType(final TypeElement typeElement) {
.setOriginalSimpleSourceName(
typeElement.getSimpleName() != null ? typeElement.getSimpleName().toString() : null)
.setPackage(createPackageDeclaration(getPackageOf(typeElement)))
.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)
.map(tv -> createTypeVariable(tv, ImmutableList.of(), isNullMarked))
.collect(toImmutableList()))
.setVisibility(getVisibility(typeElement))
.setDeclaredMethodDescriptorsFactory(declaredMethods)
Expand Down Expand Up @@ -1369,6 +1403,27 @@ private String getObjectiveCNamePrefix(TypeElement typeElement) {
: KtInteropAnnotationUtils.getKtObjectiveCName(getPackageOf(typeElement));
}

/** Return whether a type is annotated for nullablility and which type of annotation it has. */
private static NullabilityAnnotation getNullabilityAnnotation(
AnnotatedConstruct annotatedConstruct, List<? extends AnnotationMirror> elementAnnotations) {

Iterable<AnnotationMirror> allAnnotations =
Iterables.concat(elementAnnotations, annotatedConstruct.getAnnotationMirrors());
for (AnnotationMirror annotation : allAnnotations) {
String annotationName = getAnnotationName(annotation);

if (Nullability.isNonNullAnnotation(annotationName)) {
return NullabilityAnnotation.NOT_NULLABLE;
}

if (Nullability.isNullableAnnotation(annotationName)) {
return NullabilityAnnotation.NULLABLE;
}
}

return NullabilityAnnotation.NONE;
}

private boolean isFunctionalInterface(TypeMirror type) {
return internalTypes.isFunctionalInterface((Type) type)
&& ((Type) type).asElement().getKind() == ElementKind.INTERFACE;
Expand Down

0 comments on commit b8f6940

Please sign in to comment.