Skip to content

Commit

Permalink
Honor package-info annotations from dependencies.
Browse files Browse the repository at this point in the history
This brings package-info annotation processing to be more consistent with the way most tools interpret them.

PiperOrigin-RevId: 579181592
  • Loading branch information
rluble authored and copybara-github committed Nov 3, 2023
1 parent 45c2da9 commit 2d9ce58
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ protected void run(Problems problems) {
// not be complete and cannot be fully resolved to descriptors.
.filter(not(ITypeBinding::isAnnotation))
.collect(ImmutableList.toImmutableList());
var environment = new JdtEnvironment();
var environment = new JdtEnvironment(parser);

PackageInfoCache.init(classPathEntries, problems);
environment.initWellKnownTypes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public List<CompilationUnit> compile(FrontendOptions options, Problems problems)
/* useTargetPath= */ options.getGenerateKytheIndexingMetadata(),
options.getForbiddenAnnotations());
problems.abortIfHasErrors();
return CompilationUnitBuilder.build(compilationUnitsAndTypeBindings);
return CompilationUnitBuilder.build(compilationUnitsAndTypeBindings, parser);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,12 @@
/** Base class for implementing that AST conversion from different front ends. */
public abstract class AbstractCompilationUnitBuilder {

private final PackageInfoCache packageInfoCache = PackageInfoCache.get();

/** Type stack to keep track of the lexically enclosing types as they are being created. */
private final List<Type> typeStack = new ArrayList<>();

private String currentSourceFile;
private CompilationUnit currentCompilationUnit;

/**
* Sets the JS namespace and whether it defines a null marked scope for a package that is being
* compiled from source.
*/
protected void setPackagePropertiesFromSource(
String packageName, String jsNamespace, String objectiveCName, boolean isNullMarked) {
packageInfoCache.setPackageProperties(
PackageInfoCache.SOURCE_CLASS_PATH_ENTRY,
packageName,
jsNamespace,
objectiveCName,
isNullMarked);
}

protected String getCurrentSourceFile() {
return currentSourceFile;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public PackageReport build() {
* When nothing is known about a particular package in a particular class path entry the answers
* to questions about package properties are taken from this instance.
*/
private static final PackageReport DEFAULT_PACKAGE_REPORT = PackageReport.newBuilder().build();
public static final PackageReport DEFAULT_PACKAGE_REPORT = PackageReport.newBuilder().build();

/** Allows for the initialization/retrieval of one shared PackageInfoCache instance per thread. */
private static final ThreadLocal<PackageInfoCache> packageInfoCacheStorage = new ThreadLocal<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,11 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.j2cl.transpiler.frontend.jdt.JdtAnnotationUtils.isNullMarked;
import static com.google.j2cl.transpiler.frontend.jdt.JsInteropAnnotationUtils.getJsNamespace;
import static com.google.j2cl.transpiler.frontend.jdt.KtInteropAnnotationUtils.getKtObjectiveCName;
import static java.util.Arrays.stream;
import static java.util.Map.Entry.comparingByKey;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;

import com.google.common.base.Predicates;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import com.google.j2cl.common.FilePosition;
import com.google.j2cl.common.SourcePosition;
Expand Down Expand Up @@ -99,7 +94,6 @@
import com.google.j2cl.transpiler.ast.WhileStatement;
import com.google.j2cl.transpiler.frontend.common.AbstractCompilationUnitBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -136,7 +130,7 @@
/** Creates a J2CL Java AST from the AST provided by JDT. */
public class CompilationUnitBuilder extends AbstractCompilationUnitBuilder {

private final JdtEnvironment environment = new JdtEnvironment();
private final JdtEnvironment environment;

private class ASTConverter {
private org.eclipse.jdt.core.dom.CompilationUnit jdtCompilationUnit;
Expand All @@ -152,14 +146,7 @@ private CompilationUnit convert(
String packageName =
packageDeclaration == null ? "" : packageDeclaration.getName().getFullyQualifiedName();
setCurrentCompilationUnit(CompilationUnit.createForFile(sourceFilePath, packageName));
// Records information about package-info files supplied as source code.
if (getCurrentSourceFile().endsWith("package-info.java") && packageDeclaration != null) {
setPackagePropertiesFromSource(
packageName,
getJsNamespace(packageDeclaration),
getKtObjectiveCName(packageDeclaration),
isNullMarked(packageDeclaration));
}

for (Object object : jdtCompilationUnit.types()) {
AbstractTypeDeclaration abstractTypeDeclaration = (AbstractTypeDeclaration) object;
getCurrentCompilationUnit().addType(convert(abstractTypeDeclaration));
Expand Down Expand Up @@ -1426,43 +1413,29 @@ private CompilationUnit buildCompilationUnit(
}

public static List<CompilationUnit> build(
CompilationUnitsAndTypeBindings compilationUnitsAndTypeBindings) {
CompilationUnitsAndTypeBindings compilationUnitsAndTypeBindings, JdtParser jdtParser) {
JdtEnvironment environment =
new JdtEnvironment(
PackageAnnotationsResolver.create(
compilationUnitsAndTypeBindings.getCompilationUnitsByFilePath().entrySet().stream()
.filter(e -> e.getKey().endsWith("package-info.java"))
.map(Entry::getValue),
jdtParser));

Map<String, org.eclipse.jdt.core.dom.CompilationUnit> jdtUnitsByFilePath =
compilationUnitsAndTypeBindings.getCompilationUnitsByFilePath();
List<ITypeBinding> wellKnownTypeBindings = compilationUnitsAndTypeBindings.getTypeBindings();
CompilationUnitBuilder compilationUnitBuilder =
new CompilationUnitBuilder(wellKnownTypeBindings);

List<Entry<String, org.eclipse.jdt.core.dom.CompilationUnit>> entries =
new ArrayList<>(jdtUnitsByFilePath.entrySet());
// Ensure that all source package-info classes come before all other classes so that the
// freshness of the PackageInfoCache can be trusted.
sortPackageInfoFirst(entries);
new CompilationUnitBuilder(wellKnownTypeBindings, environment);

return entries.stream()
return jdtUnitsByFilePath.entrySet().stream()
.map(entry -> compilationUnitBuilder.buildCompilationUnit(entry.getKey(), entry.getValue()))
.collect(toImmutableList());
}

private static void sortPackageInfoFirst(
List<Entry<String, org.eclipse.jdt.core.dom.CompilationUnit>> entries) {
// Ensure that all source package-info classes come before all other classes so that the
// freshness of the PackageInfoCache can be trusted.
Collections.sort(
entries,
comparingByKey(
(thisFilePath, thatFilePath) -> {
boolean thisIsPackageInfo = thisFilePath.endsWith("package-info.java");
boolean thatIsPackageInfo = thatFilePath.endsWith("package-info.java");
return ComparisonChain.start()
.compareTrueFirst(thisIsPackageInfo, thatIsPackageInfo)
.compare(thisFilePath, thatFilePath)
.result();
}));
}

private CompilationUnitBuilder(List<ITypeBinding> wellKnownTypeBindings) {
private CompilationUnitBuilder(
List<ITypeBinding> wellKnownTypeBindings, JdtEnvironment environment) {
this.environment = environment;
environment.initWellKnownTypes(wellKnownTypeBindings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,11 @@
import com.google.j2cl.transpiler.ast.Variable;
import com.google.j2cl.transpiler.ast.Visibility;
import com.google.j2cl.transpiler.frontend.common.Nullability;
import com.google.j2cl.transpiler.frontend.common.PackageInfoCache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -88,6 +86,16 @@ public class JdtEnvironment {
private final Map<IVariableBinding, FieldDescriptor> cachedFieldDescriptorByVariableBinding =
new HashMap<>();

private final PackageAnnotationsResolver packageAnnotationsResolver;

public JdtEnvironment(JdtParser jdtParser) {
this.packageAnnotationsResolver = PackageAnnotationsResolver.create(Stream.of(), jdtParser);
}

public JdtEnvironment(PackageAnnotationsResolver packageAnnotationsResolver) {
this.packageAnnotationsResolver = packageAnnotationsResolver;
}

@Nullable
public static BinaryOperator getBinaryOperator(InfixExpression.Operator operator) {
switch (operator.toString()) {
Expand Down Expand Up @@ -491,16 +499,6 @@ private static NullabilityAnnotation getNullabilityAnnotation(
return NullabilityAnnotation.NO_ANNOTATION;
}

/**
* In case the given type binding is nested, return the outermost possible enclosing type binding.
*/
private static ITypeBinding toTopLevelTypeBinding(ITypeBinding typeBinding) {
ITypeBinding topLevelClass = typeBinding;
while (topLevelClass.getDeclaringClass() != null) {
topLevelClass = topLevelClass.getDeclaringClass();
}
return topLevelClass;
}

private static boolean isIntersectionType(ITypeBinding binding) {
return binding.isIntersectionType()
Expand Down Expand Up @@ -1114,27 +1112,25 @@ private static String getJsName(final ITypeBinding typeBinding) {
}

@Nullable
private String getObjectiveCNamePrefix(
ITypeBinding typeBinding, PackageInfoCache packageInfoCache) {
return getPropertyIfTopLevel(typeBinding, packageInfoCache::getObjectiveCName);
private String getObjectiveCNamePrefix(ITypeBinding typeBinding) {
checkArgument(!typeBinding.isPrimitive());
String objectiveCNamePrefix = KtInteropAnnotationUtils.getKtObjectiveCName(typeBinding);
boolean isTopLevelType = typeBinding.getDeclaringClass() == null;

return objectiveCNamePrefix != null || !isTopLevelType
? objectiveCNamePrefix
: packageAnnotationsResolver.getObjectiveCNamePrefix(typeBinding.getPackage().getName());
}

@Nullable
private String getJsNamespace(ITypeBinding typeBinding, PackageInfoCache packageInfoCache) {
private String getJsNamespace(ITypeBinding typeBinding) {
checkArgument(!typeBinding.isPrimitive());
String jsNamespace = JsInteropAnnotationUtils.getJsNamespace(typeBinding);
return jsNamespace != null
? jsNamespace
: getPropertyIfTopLevel(typeBinding, packageInfoCache::getJsNamespace);
}

@Nullable
private String getPropertyIfTopLevel(
ITypeBinding typeBinding, Function<String, String> propertyForBinaryName) {
boolean isTopLevelType = typeBinding.getDeclaringClass() == null;
return isTopLevelType
? propertyForBinaryName.apply(getBinaryNameFromTypeBinding(typeBinding))
: null;

return jsNamespace != null || !isTopLevelType
? jsNamespace
: packageAnnotationsResolver.getJsNameSpace(typeBinding.getPackage().getName());
}

@Nullable
Expand All @@ -1155,17 +1151,6 @@ public TypeDeclaration createDeclarationForType(final ITypeBinding typeBinding)
checkArgument(!typeBinding.isWildcardType());
checkArgument(!typeBinding.isCapture());

PackageInfoCache packageInfoCache = PackageInfoCache.get();

ITypeBinding topLevelTypeBinding = toTopLevelTypeBinding(typeBinding);
if (topLevelTypeBinding.isFromSource()) {
// Let the PackageInfoCache know that this class is Source, otherwise it would have to rummage
// around in the class path to figure it out and it might even come up with the wrong answer
// for example if this class has also been globbed into some other library that is a
// dependency of this one.
PackageInfoCache.get().markAsSource(getBinaryNameFromTypeBinding(topLevelTypeBinding));
}

// Compute these first since they're reused in other calculations.
String packageName =
typeBinding.getPackage() == null ? null : typeBinding.getPackage().getName();
Expand All @@ -1185,7 +1170,7 @@ public TypeDeclaration createDeclarationForType(final ITypeBinding typeBinding)
.map(this::createFieldDescriptor)
.collect(toImmutableList());

boolean isNullMarked = isNullMarked(typeBinding, packageInfoCache);
boolean isNullMarked = isNullMarked(typeBinding);
IBinding declaringMemberBinding = getDeclaringMethodOrFieldBinding(typeBinding);

typeDeclaration =
Expand Down Expand Up @@ -1225,8 +1210,8 @@ public TypeDeclaration createDeclarationForType(final ITypeBinding typeBinding)
.setAnonymous(typeBinding.isAnonymous())
.setLocal(isLocal(typeBinding))
.setSimpleJsName(getJsName(typeBinding))
.setCustomizedJsNamespace(getJsNamespace(typeBinding, packageInfoCache))
.setObjectiveCNamePrefix(getObjectiveCNamePrefix(typeBinding, packageInfoCache))
.setCustomizedJsNamespace(getJsNamespace(typeBinding))
.setObjectiveCNamePrefix(getObjectiveCNamePrefix(typeBinding))
.setKtTypeInfo(KtInteropUtils.getKtTypeInfo(typeBinding))
.setKtObjcInfo(KtInteropUtils.getKtObjcInfo(typeBinding))
.setNullMarked(isNullMarked)
Expand All @@ -1249,10 +1234,9 @@ public TypeDeclaration createDeclarationForType(final ITypeBinding typeBinding)
return typeDeclaration;
}

private boolean isNullMarked(ITypeBinding typeBinding, PackageInfoCache packageInfoCache) {
private boolean isNullMarked(ITypeBinding typeBinding) {
return hasNullMarkedAnnotation(typeBinding)
|| packageInfoCache.isNullMarked(
getBinaryNameFromTypeBinding(toTopLevelTypeBinding(typeBinding)));
|| packageAnnotationsResolver.isNullMarked(typeBinding.getPackage().getName());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.eclipse.jdt.core.BindingKey;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.IProblem;
Expand Down Expand Up @@ -132,6 +133,15 @@ public List<ITypeBinding> resolveBindings(Collection<String> binaryNames) {
.getTypeBindings();
}

@Nullable
public ITypeBinding resolveBinding(String qualifiedBinaryName) {
List<ITypeBinding> bindings = resolveBindings(ImmutableList.of(qualifiedBinaryName));
if (bindings.isEmpty()) {
return null;
}
return Iterables.getOnlyElement(bindings);
}

private ASTParser newASTParser() {
ASTParser parser = ASTParser.newParser(AST_JLS_VERSION);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ public static IAnnotationBinding getJsOverlayAnnotation(IBinding methodBinding)
methodBinding.getAnnotations(), JS_OVERLAY_ANNOTATION_NAME);
}

public static IAnnotationBinding getJsPackageAnnotation(ITypeBinding packageBinding) {
return JdtAnnotationUtils.findAnnotationBindingByName(
packageBinding.getAnnotations(), JS_PACKAGE_ANNOTATION_NAME);
}

public static boolean isJsPackageAnnotation(IAnnotationBinding annotation) {
return annotation.getAnnotationType().getQualifiedName().equals(JS_PACKAGE_ANNOTATION_NAME);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import static com.google.j2cl.transpiler.frontend.jdt.JdtAnnotationUtils.getStringAttribute;

import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.PackageDeclaration;

/** Utility methods to get information about Kotlin Interop annotations. */
Expand All @@ -53,6 +54,11 @@ public static IAnnotationBinding getKtDisabledAnnotation(
return findAnnotationBindingByName(annotationBindings, KT_DISABLED_ANNOTATION_NAME);
}

/** The namespace specified on a package, type, method or field. */
public static String getKtObjectiveCName(ITypeBinding typeBinding) {
return getKtObjectiveCName(getKtObjectiveCNameAnnotation(typeBinding.getAnnotations()));
}

public static String getKtObjectiveCName(IAnnotationBinding annotationBinding) {
return getStringAttribute(annotationBinding, "value");
}
Expand Down
Loading

0 comments on commit 2d9ce58

Please sign in to comment.