blob: 2a3698b4e158cbd5b030d45ebd7aa1c8ceb55d12 [file] [log] [blame]
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassResolutionResult;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.android.tools.r8.shaking.KeepClassInfo;
import com.google.common.collect.Sets;
import java.util.Set;
public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
private final AppView<?> appView;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
private final Set<DexType> prunedTypes;
public KotlinMetadataEnqueuerExtension(
AppView<?> appView,
EnqueuerDefinitionSupplier enqueuerDefinitionSupplier,
Set<DexType> prunedTypes) {
this.appView = appView;
this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
this.prunedTypes = prunedTypes;
}
private KotlinMetadataDefinitionSupplier definitionsForContext(ProgramDefinition context) {
return new KotlinMetadataDefinitionSupplier(context, enqueuerDefinitionSupplier, prunedTypes);
}
@Override
public void done(Enqueuer enqueuer) {
// In the first round of tree shaking build up all metadata such that it can be traced later.
boolean keepKotlinMetadata =
KeepClassInfo.isKotlinMetadataClassKept(
appView.dexItemFactory(),
appView.options(),
appView.appInfo()::definitionForWithoutExistenceAssert,
enqueuer::getKeepInfo);
// In the first round of tree shaking build up all metadata such that it can be traced later.
if (enqueuer.getMode().isInitialTreeShaking()) {
Set<DexMethod> keepByteCodeFunctions = Sets.newIdentityHashSet();
Set<DexProgramClass> localOrAnonymousClasses = Sets.newIdentityHashSet();
enqueuer.forAllLiveClasses(
clazz -> {
assert clazz.getKotlinInfo().isNoKotlinInformation();
if (enqueuer
.getKeepInfo(clazz)
.isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
if (KotlinClassMetadataReader.isLambda(appView, clazz)
&& clazz.hasClassInitializer()) {
feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
}
clazz.clearKotlinInfo();
clazz.removeAnnotations(
annotation ->
annotation.getAnnotationType()
== appView.dexItemFactory().kotlinMetadataType);
} else {
clazz.setKotlinInfo(
KotlinClassMetadataReader.getKotlinInfo(
clazz, appView, method -> keepByteCodeFunctions.add(method.getReference())));
if (clazz.getEnclosingMethodAttribute() != null
&& clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
localOrAnonymousClasses.add(clazz);
}
}
});
for (DexProgramClass localOrAnonymousClass : localOrAnonymousClasses) {
EnclosingMethodAttribute enclosingAttribute =
localOrAnonymousClass.getEnclosingMethodAttribute();
DexClass holder =
definitionsForContext(localOrAnonymousClass)
.definitionForHolder(enclosingAttribute.getEnclosingMethod());
if (holder == null) {
continue;
}
DexEncodedMethod method = holder.lookupMethod(enclosingAttribute.getEnclosingMethod());
// If we cannot lookup the method, the conservative choice is keep the byte code.
if (method == null
|| (method.getKotlinInfo().isFunction()
&& method.getKotlinInfo().asFunction().hasCrossInlineParameter())) {
localOrAnonymousClass.forEachProgramMethod(
m -> keepByteCodeFunctions.add(m.getReference()));
}
}
appView.setCfByteCodePassThrough(keepByteCodeFunctions);
} else {
assert enqueuer.getMode().isFinalTreeShaking();
enqueuer.forAllLiveClasses(
clazz -> {
if (enqueuer
.getKeepInfo(clazz)
.isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
clazz.clearKotlinInfo();
clazz.members().forEach(DexEncodedMember::clearKotlinInfo);
clazz.removeAnnotations(
annotation ->
annotation.getAnnotationType()
== appView.dexItemFactory().kotlinMetadataType);
} else {
// Use the concrete getNoKotlinInfo() instead of isNoKotlinInformation() to handle
// invalid kotlin info as well.
assert hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz))
== (clazz.getKotlinInfo() != getNoKotlinInfo());
}
});
}
// Trace through the modeled kotlin metadata.
enqueuer.forAllLiveClasses(
clazz -> {
clazz.getKotlinInfo().trace(definitionsForContext(clazz));
clazz.forEachProgramMember(
member ->
member.getDefinition().getKotlinInfo().trace(definitionsForContext(member)));
});
}
public class KotlinMetadataDefinitionSupplier implements DexDefinitionSupplier {
private final ProgramDefinition context;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
private final Set<DexType> prunedTypes;
private KotlinMetadataDefinitionSupplier(
ProgramDefinition context,
EnqueuerDefinitionSupplier enqueuerDefinitionSupplier,
Set<DexType> prunedTypes) {
this.context = context;
this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
this.prunedTypes = prunedTypes;
}
@Override
public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
throw new Unreachable("Not yet used");
}
@Override
public DexClass definitionFor(DexType type) {
// TODO(b/157700128) Metadata cannot at this point keep anything alive. Therefore, if a type
// has been pruned it may still be referenced, so we do an early check here to ensure it will
// not end up as. Ideally, those types should be removed by a pass on the modeled data.
if (prunedTypes != null && prunedTypes.contains(type)) {
return null;
}
return enqueuerDefinitionSupplier.definitionFor(type, context);
}
@Override
public DexItemFactory dexItemFactory() {
return appView.dexItemFactory();
}
}
}