diff --git a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java index 5ede200429d..36e831bfdce 100644 --- a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java +++ b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java @@ -68,10 +68,13 @@ ImmutableSortedSet getInjectionSites(XType type) { XTypeElement typeElement = currentType.get().getTypeElement(); enclosingTypeElementOrder.put(typeElement, enclosingTypeElementOrder.size()); for (XElement enclosedElement : typeElement.getEnclosedElements()) { - enclosedElementOrder.put(enclosedElement, enclosedElementOrder.size()); injectionSiteVisitor .visit(enclosedElement, currentType.get()) - .ifPresent(injectionSites::add); + .ifPresent( + injectionSite -> { + enclosedElementOrder.put(enclosedElement, enclosedElementOrder.size()); + injectionSites.add(injectionSite); + }); } } return ImmutableSortedSet.copyOf( diff --git a/java/dagger/internal/codegen/xprocessing/XTypes.java b/java/dagger/internal/codegen/xprocessing/XTypes.java index 654ce697a4a..7f4b77aacc6 100644 --- a/java/dagger/internal/codegen/xprocessing/XTypes.java +++ b/java/dagger/internal/codegen/xprocessing/XTypes.java @@ -388,12 +388,46 @@ private static class KnownTypeNames { * {@link Optional} is returned if there is no non-{@link Object} superclass. */ public static Optional nonObjectSuperclass(XType type) { - return isDeclared(type) - ? type.getSuperTypes().stream() - .filter(supertype -> !supertype.getTypeName().equals(TypeName.OBJECT)) - .filter(supertype -> isDeclared(supertype) && supertype.getTypeElement().isClass()) - .collect(toOptional()) - : Optional.empty(); + if (!isDeclared(type)) { + return Optional.empty(); + } + // We compare elements (rather than TypeName) here because its more efficient on the heap. + XTypeElement objectElement = objectElement(getProcessingEnv(type)); + XTypeElement typeElement = type.getTypeElement(); + if (!typeElement.isClass() || typeElement.equals(objectElement)) { + return Optional.empty(); + } + XType superClass = typeElement.getSuperClass(); + if (!isDeclared(superClass)) { + return Optional.empty(); + } + XTypeElement superClassElement = superClass.getTypeElement(); + if (!superClassElement.isClass() || superClassElement.equals(objectElement)) { + return Optional.empty(); + } + // TODO(b/310954522): XType#getSuperTypes() is less efficient (especially on the heap) as it + // requires creating XType for not just superclass but all super interfaces as well, so we go + // through a bit of effort here to avoid that call unless its absolutely necessary since + // nonObjectSuperclass is called quite a bit via InjectionSiteFactory. However, we should + // eventually optimize this on the XProcessing side instead, e.g. maybe separating + // XType#getSuperClass() into a separate method. + return superClass.getTypeArguments().isEmpty() + ? Optional.of(superClass) + : type.getSuperTypes().stream() + .filter(XTypes::isDeclared) + .filter(supertype -> supertype.getTypeElement().isClass()) + .filter(supertype -> !supertype.getTypeElement().equals(objectElement)) + .collect(toOptional()); + } + + private static XTypeElement objectElement(XProcessingEnv processingEnv) { + switch (processingEnv.getBackend()) { + case JAVAC: + return processingEnv.requireTypeElement(TypeName.OBJECT); + case KSP: + return processingEnv.requireTypeElement("kotlin.Any"); + } + throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); } /**