blob: 3bf888349c9c8b963aa4314966dc0f0efba32173 [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.synthesis;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.MainDexInfo;
import java.util.Comparator;
/**
* A synthesizing context is a description of the context that gives rise to a synthetic item.
*
* <p>Note that a context can only itself be a synthetic item if it was provided as an input that
* was marked as synthetic already, in which case the context consists of the synthetic input type
* as well as the original synthesizing context type specified in it synthesis annotation.
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
class SynthesizingContext implements Comparable<SynthesizingContext> {
// The synthesizing context is the type used for ensuring a hygienic placement of a synthetic.
// Thus this type will potentially be used as the prefix of a synthetic class.
private final DexType synthesizingContextType;
// The input context is the program input type that is the actual context of a synthetic.
// In particular, if the synthetic type is itself a program input, then it will be its own
// input context but it will have a distinct synthesizing context (encoded in its annotation).
private final DexType inputContextType;
private final Origin inputContextOrigin;
private final FeatureSplit featureSplit;
static SynthesizingContext fromNonSyntheticInputContext(ClasspathOrLibraryClass context) {
// A context that is itself non-synthetic is the single context, thus both the input context
// and synthesizing context coincide.
return new SynthesizingContext(
context.getContextType(),
context.getContextType(),
context.getOrigin(),
// Synthesizing from a non-program context is just considered to be "base".
FeatureSplit.BASE);
}
static SynthesizingContext fromType(DexType type) {
// This method should only be used for synthesizing from a non-program context!
// Thus we have no origin info and place the context in the "base" feature.
return new SynthesizingContext(type, type, Origin.unknown(), FeatureSplit.BASE);
}
static SynthesizingContext fromNonSyntheticInputContext(
ProgramDefinition context, FeatureSplit featureSplit) {
// A context that is itself non-synthetic is the single context, thus both the input context
// and synthesizing context coincide.
return new SynthesizingContext(
context.getContextType(), context.getContextType(), context.getOrigin(), featureSplit);
}
static SynthesizingContext fromSyntheticInputClass(
DexProgramClass clazz, DexType synthesizingContextType, AppView<?> appView) {
// A context that is itself synthetic must denote a synthesizing context from which to ensure
// hygiene. This synthesizing context type is encoded on the synthetic for intermediate builds.
FeatureSplit featureSplit =
appView
.appInfoForDesugaring()
.getClassToFeatureSplitMap()
.getFeatureSplit(clazz, appView.getSyntheticItems());
return new SynthesizingContext(synthesizingContextType, clazz.type, clazz.origin, featureSplit);
}
private SynthesizingContext(
DexType synthesizingContextType,
DexType inputContextType,
Origin inputContextOrigin,
FeatureSplit featureSplit) {
this.synthesizingContextType = synthesizingContextType;
this.inputContextType = inputContextType;
this.inputContextOrigin = inputContextOrigin;
this.featureSplit = featureSplit;
}
@Override
public int compareTo(SynthesizingContext other) {
return Comparator
// The first item to compare is the synthesizing context type. This is the type used to
// choose the context prefix for items.
.comparing(SynthesizingContext::getSynthesizingContextType)
// To ensure that equals coincides with compareTo == 0, we then compare 'type'.
.thenComparing(c -> c.inputContextType)
.compare(this, other);
}
DexType getSynthesizingContextType() {
return synthesizingContextType;
}
Origin getInputContextOrigin() {
return inputContextOrigin;
}
FeatureSplit getFeatureSplit() {
return featureSplit;
}
SynthesizingContext rewrite(NonIdentityGraphLens lens) {
DexType rewrittenInputeContextType = lens.lookupType(inputContextType);
DexType rewrittenSynthesizingContextType = lens.lookupType(synthesizingContextType);
return rewrittenInputeContextType == inputContextType
&& rewrittenSynthesizingContextType == synthesizingContextType
? this
: new SynthesizingContext(
rewrittenSynthesizingContextType,
rewrittenInputeContextType,
inputContextOrigin,
featureSplit);
}
void registerPrefixRewriting(DexType hygienicType, AppView<?> appView) {
if (!appView.options().isDesugaredLibraryCompilation()) {
return;
}
assert hygienicType.toSourceString().startsWith(synthesizingContextType.toSourceString());
DexType rewrittenContext =
appView
.options()
.desugaredLibraryConfiguration
.getEmulateLibraryInterface()
.get(synthesizingContextType);
if (rewrittenContext == null) {
return;
}
String contextPrefix =
getBinaryNameFromDescriptor(synthesizingContextType.toDescriptorString());
String rewrittenPrefix = getBinaryNameFromDescriptor(rewrittenContext.toDescriptorString());
String suffix =
getBinaryNameFromDescriptor(hygienicType.toDescriptorString())
.substring(contextPrefix.length());
DexType rewrittenType =
appView
.dexItemFactory()
.createType(getDescriptorFromClassBinaryName(rewrittenPrefix + suffix));
appView.rewritePrefix.rewriteType(hygienicType, rewrittenType);
}
@Override
public String toString() {
return "SynthesizingContext{"
+ getSynthesizingContextType()
+ (featureSplit != FeatureSplit.BASE ? ", feature:" + featureSplit : "")
+ "}";
}
// TODO(b/181858113): Remove once deprecated main-dex-list is removed.
boolean isDerivedFromMainDexList(MainDexInfo mainDexInfo) {
return mainDexInfo.isSyntheticContextOnMainDexList(inputContextType);
}
}