Relands "Desugared lib API conversion D8 Cf to Cf"
This reverts commit a7805655b434d945cdb5beb7825aba11388ceb10.
Bug: 193001902
Bug: 193002912
Bug: 193002915
Change-Id: Ib1a5f628410cd62c7e0d6715c50296e04ad1a47c
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index e8f5829..0100fd5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
import com.android.tools.r8.graph.DexDebugEvent.Default;
@@ -717,6 +718,44 @@
.put(doubleType, boxedDoubleType)
.build());
+ public final Map<DexType, DexMethod> unboxPrimitiveMethod =
+ ImmutableMap.<DexType, DexMethod>builder()
+ .put(boxedBooleanType, createUnboxMethod(booleanType, unboxBooleanMethodName))
+ .put(boxedByteType, createUnboxMethod(byteType, unboxByteMethodName))
+ .put(boxedCharType, createUnboxMethod(charType, unboxCharMethodName))
+ .put(boxedShortType, createUnboxMethod(shortType, unboxShortMethodName))
+ .put(boxedIntType, createUnboxMethod(intType, unboxIntMethodName))
+ .put(boxedLongType, createUnboxMethod(longType, unboxLongMethodName))
+ .put(boxedFloatType, createUnboxMethod(floatType, unboxFloatMethodName))
+ .put(boxedDoubleType, createUnboxMethod(doubleType, unboxDoubleMethodName))
+ .build();
+
+ private DexMethod createUnboxMethod(DexType primitiveType, DexString unboxMethodName) {
+ DexProto proto = createProto(primitiveType);
+ return createMethod(primitiveToBoxed.get(primitiveType), proto, unboxMethodName);
+ }
+
+ // Works both with the boxed and unboxed type.
+ public DexMethod getUnboxPrimitiveMethod(DexType type) {
+ DexType boxType = primitiveToBoxed.getOrDefault(type, type);
+ DexMethod unboxMethod = unboxPrimitiveMethod.get(boxType);
+ if (unboxMethod == null) {
+ throw new Unreachable("Invalid primitive type descriptor: " + type);
+ }
+ return unboxMethod;
+ }
+
+ // Works both with the boxed and unboxed type.
+ public DexMethod getBoxPrimitiveMethod(DexType type) {
+ DexType boxType = primitiveToBoxed.getOrDefault(type, type);
+ DexType primitive = getPrimitiveFromBoxed(boxType);
+ if (primitive == null) {
+ throw new Unreachable("Invalid primitive type descriptor: " + type);
+ }
+ DexProto proto = createProto(boxType, primitive);
+ return createMethod(boxType, proto, valueOfMethodName);
+ }
+
public DexType getBoxedForPrimitiveType(DexType primitive) {
assert primitive.isPrimitiveType();
return primitiveToBoxed.get(primitive);
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
index 28d6be7..c9788bd 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.Mode;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -30,33 +32,33 @@
converter.registerCallbackIfRequired(method);
}
- private void traceInvoke(DexMethod invokedMethod) {
- converter.registerWrappersForLibraryInvokeIfRequired(invokedMethod);
+ private void traceInvoke(DexMethod invokedMethod, Type invokeType, ProgramMethod context) {
+ converter.registerWrappersForLibraryInvokeIfRequired(invokedMethod, invokeType, context);
}
@Override
public void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
+ this.traceInvoke(invokedMethod, Type.STATIC, context);
}
@Override
public void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
+ this.traceInvoke(invokedMethod, Type.DIRECT, context);
}
@Override
public void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
+ this.traceInvoke(invokedMethod, Type.INTERFACE, context);
}
@Override
public void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
+ this.traceInvoke(invokedMethod, Type.SUPER, context);
}
@Override
public void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
+ this.traceInvoke(invokedMethod, Invoke.Type.VIRTUAL, context);
}
public ProgramMethodSet generateCallbackMethods() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 136d452..3129b82 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -76,6 +76,11 @@
D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer =
CfInstructionDesugaringEventConsumer.createForD8(methodProcessor);
+ // TODO(b/191656218): Move upfront the loop and use maybe the class event consumer.
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ converter.ensureWrappersForL8(instructionDesugaringEventConsumer);
+ }
+
// Process the wave and wait for all IR processing to complete.
methodProcessor.newWave();
ThreadUtils.processItems(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index cdf3974..4177124 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -96,7 +96,6 @@
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
@@ -229,8 +228,7 @@
this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
this.interfaceMethodRewriter = null;
- this.desugaredLibraryAPIConverter =
- new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
+ this.desugaredLibraryAPIConverter = null;
this.covariantReturnTypeAnnotationTransformer = null;
this.dynamicTypeOptimization = null;
this.classInliner = null;
@@ -322,10 +320,7 @@
this.identifierNameStringMarker = null;
this.devirtualizer = null;
this.typeChecker = null;
- this.desugaredLibraryAPIConverter =
- appView.rewritePrefix.isRewriting()
- ? new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS)
- : null;
+ this.desugaredLibraryAPIConverter = null;
this.serviceLoaderRewriter = null;
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
@@ -366,6 +361,13 @@
D8NestBasedAccessDesugaring::clearNestAttributes);
}
+ public void ensureWrappersForL8(
+ D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ instructionDesugaring.withDesugaredLibraryAPIConverter(
+ converter -> converter.ensureWrappersForL8(instructionDesugaringEventConsumer));
+ }
+
private void staticizeClasses(
OptimizationFeedback feedback, ExecutorService executorService, GraphLens applied)
throws ExecutionException {
@@ -431,7 +433,6 @@
new EmulatedInterfaceApplicationRewriter(appView).rewriteApplication(builder);
}
processCovariantReturnTypeAnnotations(builder);
- generateDesugaredLibraryAPIWrappers(builder, executor);
timing.end();
@@ -458,13 +459,14 @@
D8MethodProcessor methodProcessor, ExecutorService executorService)
throws ExecutionException {
D8CfPostProcessingDesugaringEventConsumer eventConsumer =
- CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor, appView);
+ CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor);
methodProcessor.newWave();
InterfaceMethodProcessorFacade interfaceDesugaring =
instructionDesugaring.getInterfaceMethodPostProcessingDesugaring(ExcludeDexResources);
CfPostProcessingDesugaringCollection.create(
appView, interfaceDesugaring, instructionDesugaring.getRetargetingInfo())
.postProcessingDesugaring(eventConsumer, executorService);
+ methodProcessor.awaitMethodProcessing();
eventConsumer.finalizeDesugaring();
}
@@ -482,6 +484,9 @@
rewriteEnclosingLambdaMethodAttributes(
appView, classConverterResult.getForcefullyMovedLambdaMethods());
+
+ instructionDesugaring.withDesugaredLibraryAPIConverter(
+ DesugaredLibraryAPIConverter::generateTrackingWarnings);
}
public void desugarClassesForD8(
@@ -590,32 +595,14 @@
}
}
- private boolean needsIRConversion(ProgramMethod method) {
+ private boolean needsIRConversion() {
if (appView.enableWholeProgramOptimizations()) {
return true;
}
if (options.testing.forceIRForCfToCfDesugar) {
return true;
}
- if (options.isDesugaredLibraryCompilation()) {
- return true;
- }
- if (!options.cfToCfDesugar) {
- return true;
- }
- if (desugaredLibraryAPIConverter != null
- && desugaredLibraryAPIConverter.shouldRegisterCallback(method)) {
- return true;
- }
- if (method.getDefinition().getCode() instanceof SynthesizedCode) {
- // SynthesizedCode needs IR to generate the code.
- return true;
- } else {
- NeedsIRDesugarUseRegistry useRegistry =
- new NeedsIRDesugarUseRegistry(method, appView, desugaredLibraryAPIConverter);
- method.registerCodeReferences(useRegistry);
- return useRegistry.needsDesugaring();
- }
+ return !options.cfToCfDesugar;
}
private void checkPrefixMerging(ProgramMethod method) {
@@ -830,9 +817,6 @@
runInterfaceDesugaringProcessorsForR8(IncludeAllResources, executorService);
feedback.updateVisibleOptimizationInfo();
- printPhase("Desugared library API Conversion finalization");
- generateDesugaredLibraryAPIWrappers(builder, executorService);
-
if (serviceLoaderRewriter != null) {
processSynthesizedServiceLoaderMethods(
serviceLoaderRewriter.getServiceLoadMethods(), executorService);
@@ -996,14 +980,6 @@
removeDeadCodeAndFinalizeIR(code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
- private void generateDesugaredLibraryAPIWrappers(
- DexApplication.Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
- if (desugaredLibraryAPIConverter != null) {
- desugaredLibraryAPIConverter.finalizeWrappers(builder, this, executorService);
- }
- }
-
private void clearDexMethodCompilationState() {
appView.appInfo().classes().forEach(this::clearDexMethodCompilationState);
}
@@ -1171,7 +1147,7 @@
options.testing.hookInIrConversion.run();
}
- if (!needsIRConversion(method) || options.skipIR) {
+ if (!needsIRConversion() || options.skipIR) {
feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
return Timing.empty();
}
@@ -1523,10 +1499,9 @@
previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
- // This pass has to be after interfaceMethodRewriter and BackportedMethodRewriter.
if (desugaredLibraryAPIConverter != null
- && (!appView.enableWholeProgramOptimizations()
- || methodProcessor.isPrimaryMethodProcessor())) {
+ && appView.enableWholeProgramOptimizations()
+ && methodProcessor.isPrimaryMethodProcessor()) {
timing.begin("Desugar library API");
desugaredLibraryAPIConverter.desugar(code);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
deleted file mode 100644
index 0d1c587..0000000
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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.ir.conversion;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
-
-class NeedsIRDesugarUseRegistry extends UseRegistry {
-
- private boolean needsDesugaring = false;
- private final ProgramMethod context;
- private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
-
- public NeedsIRDesugarUseRegistry(
- ProgramMethod method,
- AppView<?> appView,
- DesugaredLibraryAPIConverter desugaredLibraryAPIConverter) {
- super(appView.dexItemFactory());
- this.context = method;
- this.desugaredLibraryAPIConverter = desugaredLibraryAPIConverter;
- }
-
- public boolean needsDesugaring() {
- return needsDesugaring;
- }
-
- @Override
- public void registerInitClass(DexType type) {
- if (!needsDesugaring
- && desugaredLibraryAPIConverter != null
- && desugaredLibraryAPIConverter.canConvert(type)) {
- needsDesugaring = true;
- }
- }
-
- @Override
- public void registerInvokeVirtual(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInvokeDirect(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- private void registerDesugaredLibraryAPIConverter(DexMethod method) {
- if (!needsDesugaring) {
- needsDesugaring =
- desugaredLibraryAPIConverter != null
- && desugaredLibraryAPIConverter.shouldRewriteInvoke(method);
- }
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInvokeInterface(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method, boolean itf) {
- registerInvokeStatic(method);
- }
-
- @Override
- public void registerCallSite(DexCallSite callSite) {
- super.registerCallSite(callSite);
- needsDesugaring = true;
- }
-
- @Override
- public void registerInvokeSuper(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInstanceFieldRead(DexField field) {}
-
- @Override
- public void registerInstanceFieldWrite(DexField field) {}
-
- @Override
- public void registerNewInstance(DexType type) {}
-
- @Override
- public void registerStaticFieldRead(DexField field) {}
-
- @Override
- public void registerStaticFieldWrite(DexField field) {}
-
- @Override
- public void registerTypeReference(DexType type) {}
-
- @Override
- public void registerInstanceOf(DexType type) {}
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 8512734..b4f0bec 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -7,11 +7,13 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.function.Consumer;
/**
* Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to
@@ -64,4 +66,7 @@
Flavor flavor);
public abstract RetargetingInfo getRetargetingInfo();
+
+ public abstract void withDesugaredLibraryAPIConverter(
+ Consumer<DesugaredLibraryAPIConverter> consumer);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index d497332..bb2feb2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.conversion.ClassConverterResult;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
import com.android.tools.r8.ir.desugar.backports.BackportedMethodDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaringEventConsumer;
@@ -48,7 +49,8 @@
RecordDesugaringEventConsumer,
TwrCloseResourceDesugaringEventConsumer,
InterfaceMethodDesugaringEventConsumer,
- DesugaredLibraryRetargeterInstructionEventConsumer {
+ DesugaredLibraryRetargeterInstructionEventConsumer,
+ DesugaredLibraryAPIConverterEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
@@ -68,6 +70,21 @@
return new CfInstructionDesugaringEventConsumer() {
@Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAPIConversion(ProgramMethod method) {
+ assert false;
+ }
+
+ @Override
public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
assert false;
}
@@ -229,6 +246,21 @@
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
+ @Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptAPIConversion(ProgramMethod method) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
+
public List<ProgramMethod> finalizeDesugaring(
AppView<?> appView, ClassConverterResult.Builder classConverterResultBuilder) {
List<ProgramMethod> needsProcessing = new ArrayList<>();
@@ -348,6 +380,24 @@
}
@Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ // TODO(b/189912077): There should be nothing to do.
+ assert false;
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ // TODO(b/189912077): Should be added to live non program types.
+ assert false;
+ }
+
+ @Override
+ public void acceptAPIConversion(ProgramMethod method) {
+ // TODO(b/189912077): There should be nothing to do.
+ assert false;
+ }
+
+ @Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
// Intentionally empty. The backported method will be hit by the tracing in R8 as if it was
// present in the input code, and thus nothing needs to be done.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 986c6ea..367e0ca 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
@@ -47,10 +48,6 @@
AppView<?> appView,
InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
RetargetingInfo retargetingInfo) {
- if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- && interfaceMethodProcessorFacade == null) {
- return empty();
- }
ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
if (!appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView, retargetingInfo));
@@ -58,6 +55,18 @@
if (interfaceMethodProcessorFacade != null) {
desugarings.add(interfaceMethodProcessorFacade);
}
+ DesugaredLibraryAPIConverter desugaredLibraryAPIConverter =
+ appView.rewritePrefix.isRewriting() && !appView.enableWholeProgramOptimizations()
+ ? new DesugaredLibraryAPIConverter(appView, null)
+ : null;
+ // At this point the desugaredLibraryAPIConverter is required to be last to generate
+ // call-backs on the forwarding methods.
+ if (desugaredLibraryAPIConverter != null) {
+ desugarings.add(desugaredLibraryAPIConverter);
+ }
+ if (desugarings.isEmpty()) {
+ return empty();
+ }
return new NonEmptyCfPostProcessingDesugaringCollection(desugarings);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 5734cab..0ed1e16 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer.DesugaredLibraryAPIConverterPostProcessingEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
@@ -23,16 +24,12 @@
*/
public abstract class CfPostProcessingDesugaringEventConsumer
implements DesugaredLibraryRetargeterPostProcessingEventConsumer,
- InterfaceProcessingDesugaringEventConsumer {
- protected DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
-
- protected CfPostProcessingDesugaringEventConsumer(AppView<?> appView) {
- this.desugaredLibraryAPIConverter = new DesugaredLibraryAPIConverter(appView, null);
- }
+ InterfaceProcessingDesugaringEventConsumer,
+ DesugaredLibraryAPIConverterPostProcessingEventConsumer {
public static D8CfPostProcessingDesugaringEventConsumer createForD8(
- D8MethodProcessor methodProcessor, AppView<?> appView) {
- return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, appView);
+ D8MethodProcessor methodProcessor) {
+ return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor);
}
public static R8PostProcessingDesugaringEventConsumer createForR8(
@@ -49,9 +46,7 @@
// concurrently processing other methods.
private final ProgramMethodSet methodsToReprocess = ProgramMethodSet.createConcurrent();
- private D8CfPostProcessingDesugaringEventConsumer(
- D8MethodProcessor methodProcessor, AppView<?> appView) {
- super(appView);
+ private D8CfPostProcessingDesugaringEventConsumer(D8MethodProcessor methodProcessor) {
this.methodProcessor = methodProcessor;
}
@@ -92,15 +87,24 @@
methodProcessor.scheduleDesugaredMethodsForProcessing(methodsToReprocess);
methodProcessor.awaitMethodProcessing();
}
+
+ @Override
+ public void acceptAPIConversionCallback(ProgramMethod method) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
}
public static class R8PostProcessingDesugaringEventConsumer
extends CfPostProcessingDesugaringEventConsumer {
private final SyntheticAdditions additions;
- protected R8PostProcessingDesugaringEventConsumer(
- AppView<?> appView, SyntheticAdditions additions) {
- super(appView);
+ // The desugaredLibraryAPIConverter is required here because call-backs need to be generated
+ // once forwarding methods are generated. We should be able to remove it once the interface
+ // method desugaring and the API converter are moved cf to cf in R8.
+ private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
+
+ R8PostProcessingDesugaringEventConsumer(AppView<?> appView, SyntheticAdditions additions) {
+ this.desugaredLibraryAPIConverter = new DesugaredLibraryAPIConverter(appView, null);
this.additions = additions;
}
@@ -127,10 +131,7 @@
@Override
public void acceptForwardingMethod(ProgramMethod method) {
additions.addLiveMethod(method);
- ProgramMethod callback = desugaredLibraryAPIConverter.generateCallbackIfRequired(method);
- if (callback != null) {
- additions.addLiveMethod(callback);
- }
+ desugaredLibraryAPIConverter.generateCallbackIfRequired(method, this);
}
@Override
@@ -142,5 +143,10 @@
public void acceptEmulatedInterfaceMethod(ProgramMethod method) {
assert false : "TODO(b/183998768): Support Interface processing in R8";
}
+
+ @Override
+ public void acceptAPIConversionCallback(ProgramMethod method) {
+ additions.addLiveMethod(method);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index 7991ac3..17e219e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -7,11 +7,13 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.function.Consumer;
public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -73,4 +75,9 @@
public RetargetingInfo getRetargetingInfo() {
return null;
}
+
+ @Override
+ public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
+ // Intentionally empty.
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index 6975252..e0a9c07 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -407,7 +407,7 @@
}
DexType fromTypeAsPrimitive = factory.getPrimitiveFromBoxed(boxedType);
if (fromTypeAsPrimitive != null) {
- addPrimitiveUnboxing(fromTypeAsPrimitive, boxedType, instructions, factory);
+ addPrimitiveUnboxing(boxedType, instructions, factory);
addPrimitiveWideningConversion(fromTypeAsPrimitive, toType, instructions);
return;
}
@@ -424,7 +424,7 @@
|| (boxedFromType != factory.booleanType
&& boxedFromType != factory.charType
&& toType == factory.boxedNumberType)) {
- addPrimitiveBoxing(fromType, boxedFromType, instructions, factory);
+ addPrimitiveBoxing(boxedFromType, instructions, factory);
return;
}
}
@@ -513,55 +513,19 @@
"converted to " + toType.toSourceString() + " via primitive widening conversion.");
}
- private static DexMethod getUnboxMethod(byte primitive, DexType boxType, DexItemFactory factory) {
- DexProto proto;
- switch (primitive) {
- case 'Z': // byte
- proto = factory.createProto(factory.booleanType);
- return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
- case 'B': // byte
- proto = factory.createProto(factory.byteType);
- return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
- case 'S': // short
- proto = factory.createProto(factory.shortType);
- return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
- case 'C': // char
- proto = factory.createProto(factory.charType);
- return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
- case 'I': // int
- proto = factory.createProto(factory.intType);
- return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
- case 'J': // long
- proto = factory.createProto(factory.longType);
- return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
- case 'F': // float
- proto = factory.createProto(factory.floatType);
- return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
- case 'D': // double
- proto = factory.createProto(factory.doubleType);
- return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
- default:
- throw new Unreachable("Invalid primitive type descriptor: " + primitive);
- }
- }
-
private static void addPrimitiveUnboxing(
- DexType primitiveType,
DexType boxType,
Builder<CfInstruction> instructions,
DexItemFactory factory) {
- DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType, factory);
+ DexMethod method = factory.getUnboxPrimitiveMethod(boxType);
instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
}
private static void addPrimitiveBoxing(
- DexType primitiveType,
DexType boxType,
Builder<CfInstruction> instructions,
DexItemFactory factory) {
- // Generate factory method fo boxing.
- DexProto proto = factory.createProto(boxType, primitiveType);
- DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
+ DexMethod method = factory.getBoxPrimitiveMethod(boxType);
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index a7f1a33..25556ca 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.NonEmptyCfClassDesugaringCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
@@ -35,6 +36,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Consumer;
public class NonEmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -45,6 +47,7 @@
private final RecordRewriter recordRewriter;
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
+ private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
this.appView = appView;
@@ -53,6 +56,7 @@
this.recordRewriter = null;
this.desugaredLibraryRetargeter = null;
this.interfaceMethodRewriter = null;
+ this.desugaredLibraryAPIConverter = null;
return;
}
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
@@ -72,14 +76,25 @@
desugarings.add(new TwrInstructionDesugaring(appView));
}
// TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
- interfaceMethodRewriter =
- appView.options().isInterfaceMethodDesugaringEnabled()
- && !appView.enableWholeProgramOptimizations()
- ? new InterfaceMethodRewriter(
- appView, backportedMethodRewriter, desugaredLibraryRetargeter)
- : null;
- if (interfaceMethodRewriter != null) {
+ if (appView.options().isInterfaceMethodDesugaringEnabled()
+ && !appView.enableWholeProgramOptimizations()) {
+ interfaceMethodRewriter =
+ new InterfaceMethodRewriter(
+ appView, backportedMethodRewriter, desugaredLibraryRetargeter);
desugarings.add(interfaceMethodRewriter);
+ } else {
+ interfaceMethodRewriter = null;
+ }
+ desugaredLibraryAPIConverter =
+ appView.rewritePrefix.isRewriting() && !appView.enableWholeProgramOptimizations()
+ ? new DesugaredLibraryAPIConverter(
+ appView,
+ interfaceMethodRewriter,
+ desugaredLibraryRetargeter,
+ backportedMethodRewriter)
+ : null;
+ if (desugaredLibraryAPIConverter != null) {
+ desugarings.add(desugaredLibraryAPIConverter);
}
desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
@@ -332,4 +347,11 @@
}
return null;
}
+
+ @Override
+ public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
+ if (desugaredLibraryAPIConverter != null) {
+ consumer.accept(desugaredLibraryAPIConverter);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index b50ac86..c454af6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -4,10 +4,21 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary;
+import com.android.tools.r8.cf.code.CfArrayLoad;
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
@@ -17,6 +28,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -25,11 +37,24 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer.DesugaredLibraryAPIConverterPostProcessingEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.OptionalBool;
@@ -38,15 +63,16 @@
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
// I convert library calls with desugared parameters/return values so they can work normally.
// In the JSON of the desugared library, one can specify conversions between desugared and
@@ -62,7 +88,8 @@
// DesugarType is only a rewritten type (generated through rewriting of type).
// The type, from the library, may either be rewritten to the desugarType,
// or be a rewritten type (generated through rewriting of vivifiedType).
-public class DesugaredLibraryAPIConverter {
+public class DesugaredLibraryAPIConverter
+ implements CfInstructionDesugaring, CfPostProcessingDesugaring {
static final String VIVIFIED_PREFIX = "$-vivified-$.";
public static final String DESCRIPTOR_VIVIFIED_PREFIX = "L$-vivified-$/";
@@ -72,6 +99,11 @@
// For debugging only, allows to assert that synthesized code in R8 have been synthesized in the
// Enqueuer and not during IR processing.
private final Mode mode;
+ // This is used to filter out double desugaring on backported methods.
+ private final BackportedMethodRewriter backportedMethodRewriter;
+ private final InterfaceMethodRewriter interfaceMethodRewriter;
+ private final DesugaredLibraryRetargeter retargeter;
+
private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
private final Map<DexClass, Set<DexEncodedMethod>> callBackMethods = new IdentityHashMap<>();
private final Map<DexProgramClass, List<DexEncodedMethod>> pendingCallBackMethods =
@@ -85,9 +117,29 @@
}
public DesugaredLibraryAPIConverter(AppView<?> appView, Mode mode) {
+ this(appView, mode, null, null, null);
+ }
+
+ public DesugaredLibraryAPIConverter(
+ AppView<?> appView,
+ InterfaceMethodRewriter interfaceMethodRewriter,
+ DesugaredLibraryRetargeter retargeter,
+ BackportedMethodRewriter backportedMethodRewriter) {
+ this(appView, null, interfaceMethodRewriter, retargeter, backportedMethodRewriter);
+ }
+
+ private DesugaredLibraryAPIConverter(
+ AppView<?> appView,
+ Mode mode,
+ InterfaceMethodRewriter interfaceMethodRewriter,
+ DesugaredLibraryRetargeter retargeter,
+ BackportedMethodRewriter backportedMethodRewriter) {
this.appView = appView;
this.factory = appView.dexItemFactory();
this.mode = mode;
+ this.interfaceMethodRewriter = interfaceMethodRewriter;
+ this.retargeter = retargeter;
+ this.backportedMethodRewriter = backportedMethodRewriter;
this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView, this);
if (appView.options().testing.trackDesugaredAPIConversions) {
trackedCallBackAPIs = Sets.newConcurrentHashSet();
@@ -98,6 +150,86 @@
}
}
+ // TODO(b/191656218): Consider parallelizing post processing across classes instead of per
+ // implementor
+ // method.
+ @Override
+ public void postProcessingDesugaring(
+ CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService) {
+ assert noPendingWrappersOrConversions();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (!appView.isAlreadyLibraryDesugared(clazz)) {
+ ArrayList<DexEncodedMethod> callbacks = new ArrayList<>();
+ for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
+ if (shouldRegisterCallback(virtualProgramMethod)) {
+ if (trackedCallBackAPIs != null) {
+ trackedCallBackAPIs.add(virtualProgramMethod.getReference());
+ }
+ ProgramMethod callback =
+ generateCallbackMethod(
+ virtualProgramMethod.getDefinition(),
+ virtualProgramMethod.getHolder(),
+ eventConsumer);
+ callbacks.add(callback.getDefinition());
+ }
+ }
+ if (!callbacks.isEmpty()) {
+ clazz.addVirtualMethods(callbacks);
+ }
+ }
+ }
+ assert noPendingWrappersOrConversions();
+ generateTrackingWarnings();
+ }
+
+ private boolean noPendingWrappersOrConversions() {
+ for (DexProgramClass pendingSyntheticClass :
+ appView.getSyntheticItems().getPendingSyntheticClasses()) {
+ assert !isAPIConversionSyntheticType(pendingSyntheticClass.type);
+ }
+ return true;
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
+ assert !appView.enableWholeProgramOptimizations();
+ if (needsDesugaring(instruction, context)) {
+ assert instruction.isInvoke();
+ return rewriteLibraryInvoke(
+ instruction.asInvoke(),
+ methodProcessingContext,
+ localStackAllocator,
+ eventConsumer,
+ context);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ if (!instruction.isInvoke()) {
+ return false;
+ }
+ if (isAPIConversionSyntheticType(context.getHolderType())) {
+ return false;
+ }
+ CfInvoke invoke = instruction.asInvoke();
+ return shouldRewriteInvoke(
+ invoke.getMethod(), invoke.getInvokeType(context), invoke.isInterface(), context);
+ }
+
+ private boolean isAPIConversionSyntheticType(DexType type) {
+ return wrapperSynthesizor.isSyntheticWrapper(type)
+ || appView.getSyntheticItems().isSyntheticOfKind(type, SyntheticKind.API_CONVERSION);
+ }
+
public static boolean isVivifiedType(DexType type) {
return type.descriptor.toString().startsWith(DESCRIPTOR_VIVIFIED_PREFIX);
}
@@ -108,6 +240,8 @@
public void desugar(IRCode code) {
+ assert appView.enableWholeProgramOptimizations();
+
if (wrapperSynthesizor.isSyntheticWrapper(code.method().getHolderType())) {
return;
}
@@ -128,26 +262,32 @@
continue;
}
InvokeMethod invokeMethod = instruction.asInvokeMethod();
- DexMethod invokedMethod;
- if (invokeMethod.isInvokeSuper()) {
- DexClassAndMethod result =
- appView
- .appInfoForDesugaring()
- .lookupSuperTarget(invokeMethod.getInvokedMethod(), code.context());
- invokedMethod = result != null ? result.getReference() : null;
- } else {
- // TODO(b/192439456): Make a test to prove resolution is needed here and fix it.
- invokedMethod = invokeMethod.getInvokedMethod();
- }
+ DexMethod invokedMethod = invokeMethod.getInvokedMethod();
// Library methods do not understand desugared types, hence desugared types have to be
// converted around non desugared library calls for the invoke to resolve.
- if (invokedMethod != null && shouldRewriteInvoke(invokedMethod)) {
+ if (invokedMethod != null
+ && shouldRewriteInvoke(
+ invokedMethod,
+ invokeMethod.getType(),
+ invokeMethod.getInterfaceBit(),
+ code.context())) {
rewriteLibraryInvoke(code, invokeMethod, iterator, blockIterator);
}
}
}
}
+ private DexClassAndMethod getMethodForDesugaring(
+ DexMethod invokedMethod, boolean isInvokeSuper, boolean isInterface, ProgramMethod context) {
+ // TODO(b/191656218): Use lookupInvokeSpecial instead when this is all to Cf.
+ return isInvokeSuper
+ ? appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context)
+ : appView
+ .appInfoForDesugaring()
+ .resolveMethod(invokedMethod, isInterface)
+ .getResolutionPair();
+ }
+
private boolean validateCallbackWasGeneratedInEnqueuer(ProgramMethod method) {
if (!shouldRegisterCallback(method)) {
return true;
@@ -157,16 +297,76 @@
return true;
}
- public boolean shouldRewriteInvoke(DexMethod invokedMethod) {
- if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder, appView)
- || invokedMethod.holder.isArrayType()) {
+ // TODO(b/191656218): Consider caching the result.
+ private boolean shouldRewriteInvoke(
+ DexMethod unresolvedInvokedMethod,
+ Type invokeType,
+ boolean isInterface,
+ ProgramMethod context) {
+ DexClassAndMethod invokedMethod =
+ getMethodForDesugaring(
+ unresolvedInvokedMethod, invokeType == Type.SUPER, isInterface, context);
+ if (invokedMethod == null) {
+ // Implies a resolution/look-up failure, we do not convert to keep the runtime error.
return false;
}
- DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+ DexType holderType = invokedMethod.getHolderType();
+ if (appView.rewritePrefix.hasRewrittenType(holderType, appView) || holderType.isArrayType()) {
+ return false;
+ }
+ DexClass dexClass = appView.definitionFor(holderType);
if (dexClass == null || !dexClass.isLibraryClass()) {
return false;
}
- return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.proto, appView);
+ if (isEmulatedInterfaceOverride(invokedMethod)) {
+ return false;
+ }
+ if (isAlreadyDesugared(unresolvedInvokedMethod, invokeType, isInterface, context)) {
+ return false;
+ }
+ return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.getProto(), appView);
+ }
+
+ // The problem is that a method can resolve into a library method which is not present at runtime,
+ // the code relies in that case on emulated interface dispatch. We should not convert such API.
+ private boolean isEmulatedInterfaceOverride(DexClassAndMethod invokedMethod) {
+ if (interfaceMethodRewriter == null) {
+ return false;
+ }
+ if (!interfaceMethodRewriter.getEmulatedMethods().contains(invokedMethod.getName())) {
+ return false;
+ }
+ DexClassAndMethod interfaceResult =
+ appView
+ .appInfoForDesugaring()
+ .lookupMaximallySpecificMethod(invokedMethod.getHolder(), invokedMethod.getReference());
+ return interfaceResult != null
+ && appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsKey(interfaceResult.getHolderType());
+ }
+
+ private boolean isAlreadyDesugared(
+ DexMethod unresolvedInvokedMethod,
+ Type invokeType,
+ boolean isInterface,
+ ProgramMethod context) {
+ if (interfaceMethodRewriter != null
+ && interfaceMethodRewriter.needsRewriting(unresolvedInvokedMethod, invokeType, context)) {
+ return true;
+ }
+ if (retargeter != null
+ && retargeter.hasNewInvokeTarget(
+ unresolvedInvokedMethod, isInterface, invokeType == Type.SUPER, context)) {
+ return true;
+ }
+ if (backportedMethodRewriter != null
+ && backportedMethodRewriter.methodIsBackport(unresolvedInvokedMethod)) {
+ return true;
+ }
+ return false;
}
public void registerCallbackIfRequired(ProgramMethod method) {
@@ -175,16 +375,18 @@
}
}
- public ProgramMethod generateCallbackIfRequired(ProgramMethod method) {
+ public void generateCallbackIfRequired(
+ ProgramMethod method, DesugaredLibraryAPIConverterPostProcessingEventConsumer eventConsumer) {
if (!shouldRegisterCallback(method)) {
- return null;
+ return;
}
if (trackedCallBackAPIs != null) {
trackedCallBackAPIs.add(method.getReference());
}
- ProgramMethod callback = generateCallbackMethod(method.getDefinition(), method.getHolder());
- method.getHolder().addVirtualMethod(callback.getDefinition());
- return callback;
+ ProgramMethod callback =
+ generateCallbackMethod(method.getDefinition(), method.getHolder(), eventConsumer);
+ callback.getHolder().addVirtualMethod(callback.getDefinition());
+ assert noPendingWrappersOrConversions();
}
public boolean shouldRegisterCallback(ProgramMethod method) {
@@ -198,6 +400,7 @@
DexEncodedMethod definition = method.getDefinition();
if (definition.isPrivateMethod()
|| definition.isStatic()
+ || definition.isAbstract()
|| definition.isLibraryMethodOverride().isFalse()) {
return false;
}
@@ -322,19 +525,9 @@
return appView.dexItemFactory().createMethod(holder, newProto, originalMethod.name);
}
- public void finalizeWrappers(
- DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
- throws ExecutionException {
- // In D8, we generate the wrappers here. In R8, wrappers have already been generated in the
- // enqueuer, so nothing needs to be done.
- if (appView.enableWholeProgramOptimizations()) {
- return;
- }
- SortedProgramMethodSet callbacks = generateCallbackMethods();
- irConverter.processMethodsConcurrently(callbacks, executorService);
- if (appView.options().isDesugaredLibraryCompilation()) {
- wrapperSynthesizor.finalizeWrappersForL8();
- }
+ public void ensureWrappersForL8(CfInstructionDesugaringEventConsumer eventConsumer) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ wrapperSynthesizor.ensureWrappersForL8(eventConsumer);
}
public SortedProgramMethodSet generateCallbackMethods() {
@@ -345,7 +538,7 @@
List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
callbacks.forEach(
callback -> {
- ProgramMethod callbackMethod = generateCallbackMethod(callback, clazz);
+ ProgramMethod callbackMethod = generateCallbackMethod(callback, clazz, null);
newVirtualMethods.add(callbackMethod.getDefinition());
allCallbackMethods.add(callbackMethod);
});
@@ -369,12 +562,14 @@
}
private ProgramMethod generateCallbackMethod(
- DexEncodedMethod originalMethod, DexProgramClass clazz) {
+ DexEncodedMethod originalMethod,
+ DexProgramClass clazz,
+ DesugaredLibraryAPIConverterPostProcessingEventConsumer eventConsumer) {
DexMethod methodToInstall =
methodWithVivifiedTypeInSignature(originalMethod.getReference(), clazz.type, appView);
CfCode cfCode =
new APIConverterWrapperCfCodeProvider(
- appView, originalMethod.getReference(), null, this, clazz.isInterface())
+ appView, originalMethod.getReference(), null, this, clazz.isInterface(), null)
.generateCfCode();
DexEncodedMethod newMethod =
wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode);
@@ -382,7 +577,13 @@
if (originalMethod.isLibraryMethodOverride().isTrue()) {
newMethod.setLibraryMethodOverride(OptionalBool.TRUE);
}
- return new ProgramMethod(clazz, newMethod);
+ ProgramMethod callback = new ProgramMethod(clazz, newMethod);
+ if (eventConsumer != null) {
+ eventConsumer.acceptAPIConversionCallback(callback);
+ } else {
+ assert appView.enableWholeProgramOptimizations();
+ }
+ return callback;
}
private void generateTrackDesugaredAPIWarnings(Set<DexMethod> tracked, String inner) {
@@ -425,8 +626,11 @@
return vivifiedType;
}
- public void registerWrappersForLibraryInvokeIfRequired(DexMethod invokedMethod) {
- if (!shouldRewriteInvoke(invokedMethod)) {
+ public void registerWrappersForLibraryInvokeIfRequired(
+ DexMethod invokedMethod, Type invokeType, ProgramMethod context) {
+ // TODO(b/191656218): Once R8 support is done, use the isInterface bit instead of the inexact
+ // invokeType == Type.INTERFACE here.
+ if (!shouldRewriteInvoke(invokedMethod, invokeType, invokeType == Type.INTERFACE, context)) {
return;
}
if (trackedAPIs != null) {
@@ -443,6 +647,305 @@
}
}
+ private static DexType invalidType(
+ DexMethod invokedMethod,
+ DexMethod returnConversion,
+ DexMethod[] parameterConversions,
+ AppView<?> appView) {
+ DexMethod convertedMethod =
+ methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView);
+ if (invokedMethod.getReturnType() != convertedMethod.getReturnType()
+ && returnConversion == null) {
+ return invokedMethod.getReturnType();
+ }
+ for (int i = 0; i < invokedMethod.getArity(); i++) {
+ if (invokedMethod.getParameter(i) != convertedMethod.getParameter(i)
+ && parameterConversions[i] == null) {
+ return invokedMethod.getParameter(i);
+ }
+ }
+ return null;
+ }
+
+ public static DexMethod getConvertedAPI(
+ DexMethod invokedMethod,
+ DexMethod returnConversion,
+ DexMethod[] parameterConversions,
+ AppView<?> appView) {
+ DexType newReturnType =
+ returnConversion != null ? returnConversion.getParameter(0) : invokedMethod.getReturnType();
+ DexType[] newParameterTypes = new DexType[parameterConversions.length];
+ for (int i = 0; i < parameterConversions.length; i++) {
+ newParameterTypes[i] =
+ parameterConversions[i] != null
+ ? parameterConversions[i].getReturnType()
+ : invokedMethod.getParameter(i);
+ }
+ DexMethod convertedAPI =
+ appView
+ .dexItemFactory()
+ .createMethod(
+ invokedMethod.holder,
+ appView.dexItemFactory().createProto(newReturnType, newParameterTypes),
+ invokedMethod.name);
+ assert convertedAPI
+ == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView)
+ || invalidType(invokedMethod, returnConversion, parameterConversions, appView) != null;
+ return convertedAPI;
+ }
+
+ private DexMethod computeReturnConversion(
+ DexMethod invokedMethod, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ DexType returnType = invokedMethod.proto.returnType;
+ if (!appView.rewritePrefix.hasRewrittenType(returnType, appView)) {
+ return null;
+ }
+ if (canConvert(returnType)) {
+ DexType newReturnType = DesugaredLibraryAPIConverter.vivifiedTypeFor(returnType, appView);
+ return ensureConversionMethod(returnType, newReturnType, returnType, eventConsumer);
+ }
+ reportInvalidInvoke(returnType, invokedMethod, "return ");
+ return null;
+ }
+
+ private DexMethod[] computeParameterConversions(
+ DexMethod invokedMethod, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ DexMethod[] parameterConversions = new DexMethod[invokedMethod.getArity()];
+ DexType[] parameters = invokedMethod.proto.parameters.values;
+ for (int i = 0; i < parameters.length; i++) {
+ DexType argType = parameters[i];
+ if (appView.rewritePrefix.hasRewrittenType(argType, appView)) {
+ if (canConvert(argType)) {
+ DexType argVivifiedType = vivifiedTypeFor(argType, appView);
+ parameterConversions[i] =
+ ensureConversionMethod(argType, argType, argVivifiedType, eventConsumer);
+ } else {
+ reportInvalidInvoke(argType, invokedMethod, "parameter ");
+ }
+ }
+ }
+ return parameterConversions;
+ }
+
+ private Collection<CfInstruction> rewriteLibraryInvoke(
+ CfInvoke invoke,
+ MethodProcessingContext methodProcessingContext,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context) {
+ DexMethod invokedMethod = invoke.getMethod();
+ if (trackedAPIs != null) {
+ trackedAPIs.add(invokedMethod);
+ }
+ if (shouldOutlineAPIConversion(invoke, context)) {
+ DexMethod outlinedAPIConversion =
+ createOutlinedAPIConversion(invoke, methodProcessingContext, eventConsumer);
+ return Collections.singletonList(
+ new CfInvoke(Opcodes.INVOKESTATIC, outlinedAPIConversion, false));
+ }
+ return rewriteLibraryInvokeToInlineAPIConversion(
+ invoke, methodProcessingContext, localStackAllocator, eventConsumer);
+ }
+
+ // If the option is set, we try to outline API conversions as much as possible to reduce the
+ // number
+ // of soft verification failures. We cannot outline API conversions through super invokes, to
+ // instance initializers and to non public methods.
+ private boolean shouldOutlineAPIConversion(CfInvoke invoke, ProgramMethod context) {
+ if (invoke.isInvokeSuper(context.getHolderType())) {
+ return false;
+ }
+ if (invoke.getMethod().isInstanceInitializer(appView.dexItemFactory())) {
+ return false;
+ }
+ DexClassAndMethod methodForDesugaring =
+ getMethodForDesugaring(invoke.getMethod(), false, invoke.isInterface(), context);
+ assert methodForDesugaring != null;
+ return methodForDesugaring.getAccessFlags().isPublic();
+ }
+
+ private Collection<CfInstruction> rewriteLibraryInvokeToInlineAPIConversion(
+ CfInvoke invoke,
+ MethodProcessingContext methodProcessingContext,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+
+ DexMethod invokedMethod = invoke.getMethod();
+ DexMethod returnConversion = computeReturnConversion(invokedMethod, eventConsumer);
+ DexMethod[] parameterConversions = computeParameterConversions(invokedMethod, eventConsumer);
+
+ // If only the last 2 parameters require conversion, we do everything inlined.
+ // If other parameters require conversion, we outline the parameter conversion but keep the API
+ // call inlined.
+ // The returned value is always converted inlined.
+ boolean requireOutlinedParameterConversion = false;
+ for (int i = 0; i < parameterConversions.length - 2; i++) {
+ requireOutlinedParameterConversion |= parameterConversions[i] != null;
+ }
+
+ ArrayList<CfInstruction> cfInstructions = new ArrayList<>();
+ if (requireOutlinedParameterConversion) {
+ addOutlineParameterConversionInstructions(
+ parameterConversions,
+ cfInstructions,
+ methodProcessingContext,
+ invokedMethod,
+ localStackAllocator,
+ eventConsumer);
+ } else {
+ addInlineParameterConversionInstructions(parameterConversions, cfInstructions);
+ }
+
+ DexMethod convertedMethod =
+ getConvertedAPI(invokedMethod, returnConversion, parameterConversions, appView);
+ cfInstructions.add(new CfInvoke(invoke.getOpcode(), convertedMethod, invoke.isInterface()));
+
+ if (returnConversion != null) {
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, returnConversion, false));
+ }
+
+ return cfInstructions;
+ }
+
+ // The parameters are converted and returned in an array of converted parameters. The parameter
+ // array then needs to be unwrapped at the call site.
+ private void addOutlineParameterConversionInstructions(
+ DexMethod[] parameterConversions,
+ ArrayList<CfInstruction> cfInstructions,
+ MethodProcessingContext methodProcessingContext,
+ DexMethod invokedMethod,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+ localStackAllocator.allocateLocalStack(4);
+ DexProto newProto =
+ appView
+ .dexItemFactory()
+ .createProto(
+ appView.dexItemFactory().objectArrayType, invokedMethod.getParameters().values);
+ ProgramMethod parameterConversion =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.API_CONVERSION_PARAMETERS,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setProto(newProto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ methodSignature ->
+ computeParameterConversionCfCode(
+ methodSignature.holder, invokedMethod, parameterConversions)));
+ eventConsumer.acceptAPIConversion(parameterConversion);
+ cfInstructions.add(
+ new CfInvoke(Opcodes.INVOKESTATIC, parameterConversion.getReference(), false));
+ for (int i = 0; i < parameterConversions.length; i++) {
+ cfInstructions.add(new CfStackInstruction(Opcode.Dup));
+ cfInstructions.add(new CfConstNumber(i, ValueType.INT));
+ DexType parameterType =
+ parameterConversions[i] != null
+ ? parameterConversions[i].getReturnType()
+ : invokedMethod.getParameter(i);
+ cfInstructions.add(new CfArrayLoad(MemberType.OBJECT));
+ if (parameterType.isPrimitiveType()) {
+ cfInstructions.add(new CfCheckCast(factory.getBoxedForPrimitiveType(parameterType)));
+ DexMethod method = appView.dexItemFactory().getUnboxPrimitiveMethod(parameterType);
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
+ } else {
+ cfInstructions.add(new CfCheckCast(parameterType));
+ }
+ cfInstructions.add(new CfStackInstruction(Opcode.Swap));
+ }
+ cfInstructions.add(new CfStackInstruction(Opcode.Pop));
+ }
+
+ private CfCode computeParameterConversionCfCode(
+ DexType holder, DexMethod invokedMethod, DexMethod[] parameterConversions) {
+ ArrayList<CfInstruction> cfInstructions = new ArrayList<>();
+ cfInstructions.add(new CfConstNumber(parameterConversions.length, ValueType.INT));
+ cfInstructions.add(new CfNewArray(factory.objectArrayType));
+ int stackIndex = 0;
+ for (int i = 0; i < invokedMethod.getArity(); i++) {
+ cfInstructions.add(new CfStackInstruction(Opcode.Dup));
+ cfInstructions.add(new CfConstNumber(i, ValueType.INT));
+ DexType param = invokedMethod.getParameter(i);
+ cfInstructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
+ if (parameterConversions[i] != null) {
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, parameterConversions[i], false));
+ }
+ if (param.isPrimitiveType()) {
+ DexMethod method = appView.dexItemFactory().getBoxPrimitiveMethod(param);
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
+ }
+ cfInstructions.add(new CfArrayStore(MemberType.OBJECT));
+ if (param == appView.dexItemFactory().longType
+ || param == appView.dexItemFactory().doubleType) {
+ stackIndex++;
+ }
+ stackIndex++;
+ }
+ cfInstructions.add(new CfReturn(ValueType.OBJECT));
+ return new CfCode(
+ holder,
+ invokedMethod.getParameters().size() + 4,
+ invokedMethod.getParameters().size(),
+ cfInstructions);
+ }
+
+ private void addInlineParameterConversionInstructions(
+ DexMethod[] parameterConversions, ArrayList<CfInstruction> cfInstructions) {
+ if (parameterConversions.length > 0
+ && parameterConversions[parameterConversions.length - 1] != null) {
+ cfInstructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC, parameterConversions[parameterConversions.length - 1], false));
+ }
+ if (parameterConversions.length > 1
+ && parameterConversions[parameterConversions.length - 2] != null) {
+ cfInstructions.add(new CfStackInstruction(Opcode.Swap));
+ cfInstructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC, parameterConversions[parameterConversions.length - 2], false));
+ cfInstructions.add(new CfStackInstruction(Opcode.Swap));
+ }
+ }
+
+ private DexMethod createOutlinedAPIConversion(
+ CfInvoke invoke,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+ DexMethod invokedMethod = invoke.getMethod();
+ DexProto newProto =
+ invoke.isInvokeStatic()
+ ? invokedMethod.proto
+ : factory.prependTypeToProto(invokedMethod.getHolderType(), invokedMethod.getProto());
+ DexMethod returnConversion = computeReturnConversion(invokedMethod, eventConsumer);
+ DexMethod[] parameterConversions = computeParameterConversions(invokedMethod, eventConsumer);
+ ProgramMethod outline =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.API_CONVERSION,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setProto(newProto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ methodSignature ->
+ new APIConversionCfCodeProvider(
+ appView,
+ methodSignature.holder,
+ invoke,
+ returnConversion,
+ parameterConversions)
+ .generateCfCode()));
+ eventConsumer.acceptAPIConversion(outline);
+ return outline.getReference();
+ }
+
private void rewriteLibraryInvoke(
IRCode code,
InvokeMethod invokeMethod,
@@ -574,7 +1077,7 @@
private Instruction createParameterConversion(
IRCode code, DexType argType, DexType argVivifiedType, Value inValue) {
- DexMethod conversionMethod = ensureConversionMethod(argType, argType, argVivifiedType);
+ DexMethod conversionMethod = ensureConversionMethod(argType, argType, argVivifiedType, null);
// The value is null only if the input is null.
Value convertedValue =
createConversionValue(code, inValue.getType().nullability(), argVivifiedType, null);
@@ -583,7 +1086,8 @@
private Instruction createReturnConversionAndReplaceUses(
IRCode code, InvokeMethod invokeMethod, DexType returnType, DexType returnVivifiedType) {
- DexMethod conversionMethod = ensureConversionMethod(returnType, returnVivifiedType, returnType);
+ DexMethod conversionMethod =
+ ensureConversionMethod(returnType, returnVivifiedType, returnType, null);
Value outValue = invokeMethod.outValue();
Value convertedValue =
createConversionValue(code, Nullability.maybeNull(), returnType, outValue.getLocalInfo());
@@ -600,7 +1104,11 @@
}
}
- public DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
+ public DexMethod ensureConversionMethod(
+ DexType type,
+ DexType srcType,
+ DexType destType,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
// ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
// But everything is going to be rewritten, so we need to use vivifiedType and type".
DexType conversionHolder =
@@ -608,8 +1116,8 @@
if (conversionHolder == null) {
conversionHolder =
type == srcType
- ? wrapperSynthesizor.ensureTypeWrapper(type)
- : wrapperSynthesizor.ensureVivifiedTypeWrapper(type);
+ ? wrapperSynthesizor.ensureTypeWrapper(type, eventConsumer)
+ : wrapperSynthesizor.ensureVivifiedTypeWrapper(type, eventConsumer);
}
assert conversionHolder != null;
return factory.createMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverterEventConsumer.java
new file mode 100644
index 0000000..7af64bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverterEventConsumer.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface DesugaredLibraryAPIConverterEventConsumer {
+
+ void acceptWrapperProgramClass(DexProgramClass clazz);
+
+ void acceptWrapperClasspathClass(DexClasspathClass clazz);
+
+ void acceptAPIConversion(ProgramMethod method);
+
+ interface DesugaredLibraryAPIConverterPostProcessingEventConsumer {
+
+ void acceptAPIConversionCallback(ProgramMethod method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
index 1f8322f..02eb59b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -150,6 +150,7 @@
.setCode(
methodSig ->
new EmulateInterfaceSyntheticCfCodeProvider(
+ methodSig.getHolderType(),
emulatedDispatchMethod.getHolderType(),
desugarMethod,
itfMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index 6c1e889..7e2164f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -117,14 +117,16 @@
return appView.options().desugaredLibraryConfiguration.getWrapperConversions().contains(type);
}
- DexType ensureTypeWrapper(DexType type) {
- return ensureWrappers(type).getWrapper().type;
+ DexType ensureTypeWrapper(DexType type, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ return ensureWrappers(type, eventConsumer).getWrapper().type;
}
- DexType ensureVivifiedTypeWrapper(DexType type) {
- return ensureWrappers(type).getVivifiedWrapper().type;
+ DexType ensureVivifiedTypeWrapper(
+ DexType type, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ return ensureWrappers(type, eventConsumer).getVivifiedWrapper().type;
}
+
public void registerWrapper(DexType type) {
wrappersToGenerate.add(type);
assert getValidClassToWrap(type) != null;
@@ -161,13 +163,17 @@
}
}
- private Wrappers ensureWrappers(DexType type) {
+ private Wrappers ensureWrappers(
+ DexType type, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
assert canGenerateWrapper(type) : type;
DexClass dexClass = getValidClassToWrap(type);
- return ensureWrappers(dexClass, ignored -> {});
+ return ensureWrappers(dexClass, ignored -> {}, eventConsumer);
}
- private Wrappers ensureWrappers(DexClass context, Consumer<DexClasspathClass> creationCallback) {
+ private Wrappers ensureWrappers(
+ DexClass context,
+ Consumer<DexClasspathClass> creationCallback,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
DexType type = context.type;
DexClass wrapper;
DexClass vivifiedWrapper;
@@ -180,15 +186,20 @@
vivifiedTypeFor(type),
type,
programContext,
- wrapperField -> synthesizeVirtualMethodsForTypeWrapper(programContext, wrapperField));
+ eventConsumer,
+ wrapperField ->
+ synthesizeVirtualMethodsForTypeWrapper(
+ programContext, wrapperField, eventConsumer));
vivifiedWrapper =
ensureProgramWrapper(
SyntheticKind.VIVIFIED_WRAPPER,
type,
vivifiedTypeFor(type),
programContext,
+ eventConsumer,
wrapperField ->
- synthesizeVirtualMethodsForVivifiedTypeWrapper(programContext, wrapperField));
+ synthesizeVirtualMethodsForVivifiedTypeWrapper(
+ programContext, wrapperField, eventConsumer));
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
ensureProgramConversionMethod(
@@ -205,7 +216,9 @@
type,
classpathOrLibraryContext,
creationCallback,
- wrapperField -> synthesizeVirtualMethodsForTypeWrapper(context, wrapperField));
+ eventConsumer,
+ wrapperField ->
+ synthesizeVirtualMethodsForTypeWrapper(context, wrapperField, eventConsumer));
vivifiedWrapper =
ensureClasspathWrapper(
SyntheticKind.VIVIFIED_WRAPPER,
@@ -213,8 +226,10 @@
vivifiedTypeFor(type),
classpathOrLibraryContext,
creationCallback,
+ eventConsumer,
wrapperField ->
- synthesizeVirtualMethodsForVivifiedTypeWrapper(context, wrapperField));
+ synthesizeVirtualMethodsForVivifiedTypeWrapper(
+ context, wrapperField, eventConsumer));
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
ensureClasspathConversionMethod(
@@ -242,6 +257,7 @@
DexType wrappingType,
DexType wrappedType,
DexProgramClass programContext,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer,
Function<DexEncodedField, DexEncodedMethod[]> virtualMethodProvider) {
return appView
.getSyntheticItems()
@@ -252,9 +268,13 @@
builder -> buildWrapper(wrappingType, wrappedType, programContext, builder),
// The creation of virtual methods may require new wrappers, this needs to happen
// once the wrapper is created to avoid infinite recursion.
- wrapper ->
- wrapper.setVirtualMethods(
- virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper))));
+ wrapper -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptWrapperProgramClass(wrapper);
+ }
+ wrapper.setVirtualMethods(
+ virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper)));
+ });
}
private DexClasspathClass ensureClasspathWrapper(
@@ -263,6 +283,7 @@
DexType wrappedType,
ClasspathOrLibraryClass classpathOrLibraryContext,
Consumer<DexClasspathClass> creationCallback,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer,
Function<DexEncodedField, DexEncodedMethod[]> virtualMethodProvider) {
return appView
.getSyntheticItems()
@@ -276,6 +297,9 @@
// The creation of virtual methods may require new wrappers, this needs to happen
// once the wrapper is created to avoid infinite recursion.
wrapper -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptWrapperClasspathClass(wrapper);
+ }
wrapper.setVirtualMethods(
virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper)));
creationCallback.accept(wrapper);
@@ -382,7 +406,9 @@
}
private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
- DexClass dexClass, DexEncodedField wrapperField) {
+ DexClass dexClass,
+ DexEncodedField wrapperField,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Each method should use only types in their signature, but each method the wrapper forwards
@@ -421,7 +447,12 @@
} else {
cfCode =
new APIConverterVivifiedWrapperCfCodeProvider(
- appView, methodToInstall, wrapperField.getReference(), converter, isInterface)
+ appView,
+ methodToInstall,
+ wrapperField.getReference(),
+ converter,
+ isInterface,
+ eventConsumer)
.generateCfCode();
}
DexEncodedMethod newDexEncodedMethod =
@@ -432,7 +463,9 @@
}
private DexEncodedMethod[] synthesizeVirtualMethodsForTypeWrapper(
- DexClass dexClass, DexEncodedField wrapperField) {
+ DexClass dexClass,
+ DexEncodedField wrapperField,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Each method should use only vivified types in their signature, but each method the wrapper
@@ -465,7 +498,8 @@
dexEncodedMethod.getReference(),
wrapperField.getReference(),
converter,
- isInterface)
+ isInterface,
+ eventConsumer)
.generateCfCode();
}
DexEncodedMethod newDexEncodedMethod =
@@ -574,7 +608,7 @@
field, fieldAccessFlags, FieldTypeSignature.noSignature(), DexAnnotationSet.empty(), null);
}
- void finalizeWrappersForL8() {
+ void ensureWrappersForL8(DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
DesugaredLibraryConfiguration conf = appView.options().desugaredLibraryConfiguration;
for (DexType type : conf.getWrapperConversions()) {
assert !conf.getCustomConversions().containsKey(type);
@@ -582,7 +616,7 @@
// In broken set-ups we can end up having a json files containing wrappers of non desugared
// classes. Such wrappers are not required since the class won't be rewritten.
if (validClassToWrap.isProgramClass()) {
- ensureWrappers(validClassToWrap, ignored -> {});
+ ensureWrappers(validClassToWrap, ignored -> {}, eventConsumer);
}
}
}
@@ -599,7 +633,8 @@
classpathWrapper -> {
changed.set(true);
synthesizedCallback.accept(classpathWrapper);
- });
+ },
+ null);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index 1c29a09..c73f3e4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -130,9 +130,10 @@
.setProto(emulatedMethod.getProto())
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
- theMethod ->
+ emulatedInterfaceMethod ->
new EmulateInterfaceSyntheticCfCodeProvider(
- theMethod.getHolderType(),
+ emulatedInterfaceMethod.getHolderType(),
+ method.getHolderType(),
companionMethod,
libraryMethod,
extraDispatchCases,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 3e93691..640e740 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -214,6 +214,10 @@
}
}
+ public Set<DexString> getEmulatedMethods() {
+ return emulatedMethods;
+ }
+
private void initializeEmulatedInterfaceVariables() {
Map<DexType, DexType> emulateLibraryInterface =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index db93ced..214eab9 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -32,6 +32,8 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import java.util.ArrayList;
@@ -62,22 +64,25 @@
public static class APIConverterVivifiedWrapperCfCodeProvider
extends DesugaredLibraryAPIConversionCfCodeProvider {
- DexField wrapperField;
- DexMethod forwardMethod;
- DesugaredLibraryAPIConverter converter;
- boolean itfCall;
+ private final DexField wrapperField;
+ private final DexMethod forwardMethod;
+ private final DesugaredLibraryAPIConverter converter;
+ private final boolean itfCall;
+ private final DesugaredLibraryAPIConverterEventConsumer eventConsumer;
public APIConverterVivifiedWrapperCfCodeProvider(
AppView<?> appView,
DexMethod forwardMethod,
DexField wrapperField,
DesugaredLibraryAPIConverter converter,
- boolean itfCall) {
+ boolean itfCall,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
super(appView, wrapperField.holder);
this.forwardMethod = forwardMethod;
this.wrapperField = wrapperField;
this.converter = converter;
this.itfCall = itfCall;
+ this.eventConsumer = eventConsumer;
}
@Override
@@ -98,7 +103,8 @@
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.ensureConversionMethod(param, param, vivifiedTypeFor(param)),
+ converter.ensureConversionMethod(
+ param, param, vivifiedTypeFor(param), eventConsumer),
false));
newParameters[index - 1] = vivifiedTypeFor(param);
}
@@ -130,7 +136,7 @@
new CfInvoke(
Opcodes.INVOKESTATIC,
converter.ensureConversionMethod(
- returnType, vivifiedTypeFor(returnType), returnType),
+ returnType, vivifiedTypeFor(returnType), returnType, eventConsumer),
false));
}
if (returnType == factory.voidType) {
@@ -149,19 +155,22 @@
DexMethod forwardMethod;
DesugaredLibraryAPIConverter converter;
boolean itfCall;
+ private final DesugaredLibraryAPIConverterEventConsumer eventConsumer;
public APIConverterWrapperCfCodeProvider(
AppView<?> appView,
DexMethod forwardMethod,
DexField wrapperField,
DesugaredLibraryAPIConverter converter,
- boolean itfCall) {
+ boolean itfCall,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
// Var wrapperField is null if should forward to receiver.
super(appView, wrapperField == null ? forwardMethod.holder : wrapperField.holder);
this.forwardMethod = forwardMethod;
this.wrapperField = wrapperField;
this.converter = converter;
this.itfCall = itfCall;
+ this.eventConsumer = eventConsumer;
}
@Override
@@ -185,7 +194,8 @@
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.ensureConversionMethod(param, vivifiedTypeFor(param), param),
+ converter.ensureConversionMethod(
+ param, vivifiedTypeFor(param), param, eventConsumer),
false));
}
if (param == factory.longType || param == factory.doubleType) {
@@ -206,7 +216,7 @@
new CfInvoke(
Opcodes.INVOKESTATIC,
converter.ensureConversionMethod(
- returnType, returnType, vivifiedTypeFor(returnType)),
+ returnType, returnType, vivifiedTypeFor(returnType), eventConsumer),
false));
returnType = vivifiedTypeFor(returnType);
}
@@ -282,6 +292,70 @@
}
}
+ public static class APIConversionCfCodeProvider extends SyntheticCfCodeProvider {
+
+ private final CfInvoke initialInvoke;
+ private final DexMethod returnConversion;
+ private final DexMethod[] parameterConversions;
+
+ public APIConversionCfCodeProvider(
+ AppView<?> appView,
+ DexType holder,
+ CfInvoke initialInvoke,
+ DexMethod returnConversion,
+ DexMethod[] parameterConversions) {
+ super(appView, holder);
+ this.initialInvoke = initialInvoke;
+ this.returnConversion = returnConversion;
+ this.parameterConversions = parameterConversions;
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ DexMethod invokedMethod = initialInvoke.getMethod();
+ DexMethod convertedMethod =
+ DesugaredLibraryAPIConverter.getConvertedAPI(
+ invokedMethod, returnConversion, parameterConversions, appView);
+
+ List<CfInstruction> instructions = new ArrayList<>();
+
+ boolean isStatic = initialInvoke.getOpcode() == Opcodes.INVOKESTATIC;
+ if (!isStatic) {
+ instructions.add(new CfLoad(ValueType.fromDexType(invokedMethod.holder), 0));
+ }
+ int receiverShift = BooleanUtils.intValue(!isStatic);
+ int stackIndex = 0;
+ for (int i = 0; i < invokedMethod.getArity(); i++) {
+ DexType param = invokedMethod.getParameter(i);
+ instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex + receiverShift));
+ if (parameterConversions[i] != null) {
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, parameterConversions[i], false));
+ }
+ if (param == appView.dexItemFactory().longType
+ || param == appView.dexItemFactory().doubleType) {
+ stackIndex++;
+ }
+ stackIndex++;
+ }
+
+ // Actual call to converted value.
+ instructions.add(
+ new CfInvoke(initialInvoke.getOpcode(), convertedMethod, initialInvoke.isInterface()));
+
+ // Return conversion.
+ if (returnConversion != null) {
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, returnConversion, false));
+ }
+
+ if (invokedMethod.getReturnType().isVoidType()) {
+ instructions.add(new CfReturnVoid());
+ } else {
+ instructions.add(new CfReturn(ValueType.fromDexType(invokedMethod.getReturnType())));
+ }
+ return standardCfCodeFromInstructions(instructions);
+ }
+ }
+
public static class APIConverterConstructorCfCodeProvider extends SyntheticCfCodeProvider {
DexField wrapperField;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
index 7f26a19..e1ba3b2 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
@@ -36,12 +36,13 @@
private final List<Pair<DexType, DexMethod>> extraDispatchCases;
public EmulateInterfaceSyntheticCfCodeProvider(
+ DexType holder,
DexType interfaceType,
DexMethod companionMethod,
DexMethod libraryMethod,
List<Pair<DexType, DexMethod>> extraDispatchCases,
AppView<?> appView) {
- super(appView, interfaceType);
+ super(appView, holder);
this.interfaceType = interfaceType;
this.companionMethod = companionMethod;
this.libraryMethod = libraryMethod;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 4c1ac7c..216fcfc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -51,7 +51,9 @@
THROW_NSME("ThrowNSME", 16, true),
TWR_CLOSE_RESOURCE("TwrCloseResource", 17, true),
SERVICE_LOADER("ServiceLoad", 18, true),
- OUTLINE("Outline", 19, true);
+ OUTLINE("Outline", 19, true),
+ API_CONVERSION("APIConversion", 26, true),
+ API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true);
static {
assert verifyNoOverlappingIds();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index ddebca5..fdca331 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -126,7 +126,7 @@
Path desugaredLib =
getDesugaredLibraryInCF(parameters, this::configurationForLibraryCompilation);
- // Run on the JVM with desuagred library on classpath.
+ // Run on the JVM with desugared library on classpath.
testForJvm()
.addProgramFiles(jar)
.addRunClasspathFiles(desugaredLib)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
index 1f4d791..b3ac06f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
@@ -37,7 +37,7 @@
@RunWith(Parameterized.class)
public class DefaultMethodOverrideInLibraryTest extends DesugaredLibraryTestBase {
- static final String EXPECTED = StringUtils.lines("0", "42");
+ static final String EXPECTED = StringUtils.lines("0", "42", "0", "0", "42", "42");
private final TestParameters parameters;
@@ -127,16 +127,32 @@
static class MyIntegerArrayListWithoutOverride extends ArrayList<Integer>
implements MyIntegerList {
// No override of spliterator.
+
+ public Spliterator<Integer> superSpliteratorItf() {
+ return MyIntegerList.super.spliterator();
+ }
+
+ public Spliterator<Integer> superSpliterator() {
+ return super.spliterator();
+ }
}
// Derived list with an override of spliterator. The call must hit the classes override and that
- // will explictly call the custom default method.
+ // will explicitly call the custom default method.
static class MyIntegerArrayListWithOverride extends ArrayList<Integer> implements MyIntegerList {
@Override
public Spliterator<Integer> spliterator() {
return MyIntegerList.super.spliterator();
}
+
+ public Spliterator<Integer> superSpliteratorItf() {
+ return MyIntegerList.super.spliterator();
+ }
+
+ public Spliterator<Integer> superSpliterator() {
+ return super.spliterator();
+ }
}
static class Main {
@@ -144,6 +160,11 @@
public static void main(String[] args) {
System.out.println(new MyIntegerArrayListWithoutOverride().spliterator().estimateSize());
System.out.println(new MyIntegerArrayListWithOverride().spliterator().estimateSize());
+ System.out.println(new MyIntegerArrayListWithoutOverride().superSpliterator().estimateSize());
+ System.out.println(new MyIntegerArrayListWithOverride().superSpliterator().estimateSize());
+ System.out.println(
+ new MyIntegerArrayListWithoutOverride().superSpliteratorItf().estimateSize());
+ System.out.println(new MyIntegerArrayListWithOverride().superSpliteratorItf().estimateSize());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FreezePeriodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FreezePeriodTest.java
new file mode 100644
index 0000000..ac48549
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FreezePeriodTest.java
@@ -0,0 +1,208 @@
+// Copyright (c) 2019, 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.desugar.desugaredlibrary.conversiontests;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.time.MonthDay;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FreezePeriodTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "FP:--05-05;--06-06",
+ "FP:--05-05;--06-06",
+ "FP:--05-05;--06-0601",
+ "MFP:--05-05;--06-06");
+ private static Path CUSTOM_LIB;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public FreezePeriodTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ CUSTOM_LIB =
+ testForD8(getStaticTemp())
+ .addProgramClasses(FreezePeriod.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, MyFreezePeriod.class)
+ .addLibraryClasses(FreezePeriod.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testD8CfToCf() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Path jar =
+ testForD8(Backend.CF)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, MyFreezePeriod.class)
+ .addLibraryClasses(FreezePeriod.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .writeToZip();
+ String desugaredLibraryKeepRules = "";
+ if (shrinkDesugaredLibrary && keepRuleConsumer.get() != null) {
+ // Collection keep rules is only implemented in the DEX writer.
+ assertEquals(0, keepRuleConsumer.get().length());
+ desugaredLibraryKeepRules = "-keep class * { *; }";
+ }
+ if (parameters.getRuntime().isDex()) {
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ } else {
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(getDesugaredLibraryInCF(parameters, options -> {}))
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addProgramClasses(Executor.class, MyFreezePeriod.class)
+ .addLibraryClasses(FreezePeriod.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .allowStdoutMessages()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ testConversion2ArgsOrLess();
+ testConversion3ArgsOrMore();
+ testConversionWithValuesOnStack();
+ testConversionSuperInit();
+ }
+
+ private static void testConversionSuperInit() {
+ MyFreezePeriod myFreezePeriod = new MyFreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6));
+ System.out.println(myFreezePeriod.print());
+ }
+
+ private static void testConversionWithValuesOnStack() {
+ print(0, new FreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6)), 1);
+ }
+
+ private static void testConversion3ArgsOrMore() {
+ FreezePeriod freezePeriod2 = new FreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6), 0, 1);
+ System.out.println(freezePeriod2.print());
+ }
+
+ private static void testConversion2ArgsOrLess() {
+ FreezePeriod freezePeriod = new FreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6));
+ System.out.println(freezePeriod.print());
+ }
+
+ static void print(int i1, FreezePeriod freezePeriod, int i2) {
+ System.out.println(freezePeriod.print() + i1 + i2);
+ }
+ }
+
+ static class MyFreezePeriod extends FreezePeriod {
+
+ public MyFreezePeriod(MonthDay start, MonthDay end) {
+ super(start, end);
+ }
+
+ public String print() {
+ return "M" + super.print();
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ static class FreezePeriod {
+
+ private final MonthDay start;
+ private final MonthDay end;
+
+ public FreezePeriod(MonthDay start, MonthDay end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ public FreezePeriod(MonthDay start, MonthDay end, int extra1, Integer extra2) {
+ this.start = start;
+ this.end = end;
+ }
+
+ public String print() {
+ return "FP:" + start + ";" + end;
+ }
+ }
+}