blob: 92bd91e3b397e02a1f8031d0053f4392ed75397f [file] [log] [blame]
// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.utils.Reporter;
import java.util.Map;
/**
* The LibraryAmender is responsible in amending the library so that desugared library can be
* applied. For example, it can insert missing methods which are not present in the library but are
* supported in desugared library.
*/
public class DesugaredLibraryAmender {
private final DexDefinitionSupplier definitions;
private final Reporter reporter;
private final ComputedApiLevel minAPILevel;
public static void run(AppView<?> appView) {
run(
appView.options().machineDesugaredLibrarySpecification.getAmendLibraryMethods(),
appView.options().machineDesugaredLibrarySpecification.getAmendLibraryFields(),
appView,
appView.options().reporter,
appView.computedMinApiLevel());
}
public static void run(
Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
Map<DexField, FieldAccessFlags> amendLibraryField,
DexDefinitionSupplier definitions,
Reporter reporter,
ComputedApiLevel minAPILevel) {
if (amendLibraryMethod.isEmpty() && amendLibraryField.isEmpty()) {
return;
}
new DesugaredLibraryAmender(definitions, reporter, minAPILevel)
.run(amendLibraryMethod, amendLibraryField);
}
private DesugaredLibraryAmender(
DexDefinitionSupplier definitions, Reporter reporter, ComputedApiLevel minAPILevel) {
this.definitions = definitions;
this.reporter = reporter;
this.minAPILevel = minAPILevel;
}
private void run(
Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
Map<DexField, FieldAccessFlags> amendLibraryField) {
amendLibraryMethod.forEach(this::amendLibraryMethod);
amendLibraryField.forEach(this::amendLibraryField);
}
private void amendLibraryField(DexField field, FieldAccessFlags fieldAccessFlags) {
DexLibraryClass libClass = getLibraryClass(field);
if (libClass == null) {
return;
}
if (libClass.lookupField(field) != null) {
return;
}
DexEncodedField encodedField =
DexEncodedField.syntheticBuilder()
.setField(field)
.setAccessFlags(fieldAccessFlags)
.setApiLevel(minAPILevel)
.build();
if (fieldAccessFlags.isStatic()) {
libClass.appendStaticField(encodedField);
} else {
libClass.appendInstanceField(encodedField);
}
}
private void amendLibraryMethod(DexMethod method, MethodAccessFlags methodAccessFlags) {
DexLibraryClass libClass = getLibraryClass(method);
if (libClass == null) {
return;
}
if (libClass.lookupMethod(method) != null) {
return;
}
DexEncodedMethod encodedMethod =
DexEncodedMethod.syntheticBuilder()
.setMethod(method)
.setAccessFlags(methodAccessFlags)
.setCode(null)
.setApiLevelForDefinition(minAPILevel)
.build();
libClass.getMethodCollection().addMethod(encodedMethod);
}
private DexLibraryClass getLibraryClass(DexReference reference) {
DexClass dexClass = definitions.contextIndependentDefinitionFor(reference.getContextType());
if (dexClass == null || !dexClass.isLibraryClass()) {
// Consider just throwing an error.
reporter.warning(
"Desugared library: Cannot amend library reference "
+ reference
+ " because the holder is not a library class"
+ (dexClass == null ? "(null)." : "."));
return null;
}
return dexClass.asLibraryClass();
}
}