Custom conversion stubs
Change-Id: Ib2d67cf6c7e2a8cfb019c7913f96da47596a5075
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index cd8d6b7..14c62be 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -57,6 +57,13 @@
return field;
}
+ public CfFieldInstruction withField(DexField newField) {
+ if (field == newField) {
+ return this;
+ }
+ return new CfFieldInstruction(opcode, newField, newField);
+ }
+
public int getOpcode() {
return opcode;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 625430b..3237f29 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -77,6 +77,13 @@
return method;
}
+ public CfInvoke withMethod(DexMethod newMethod) {
+ if (method == newMethod) {
+ return this;
+ }
+ return new CfInvoke(opcode, newMethod, itf);
+ }
+
public int getOpcode() {
return opcode;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 9cc0945..af0ba94 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1419,7 +1419,7 @@
return new Builder(false);
}
- private static Builder builder(DexEncodedMethod from) {
+ public static Builder builder(DexEncodedMethod from) {
return new Builder(from.isD8R8Synthesized(), from);
}
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 5d49406..888707ff 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
@@ -53,9 +53,9 @@
throws ExecutionException {
List<DexProgramClass> classes = appView.appInfo().classes();
- CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
- new CfClassSynthesizerDesugaringEventConsumer();
- converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
+ CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
+ new CfClassSynthesizerDesugaringEventConsumer();
+ converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
if (!classSynthesizerEventConsumer.getSynthesizedClasses().isEmpty()) {
classes =
ImmutableList.<DexProgramClass>builder()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index 3f60ffe..feb1b51 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryCustomConversionEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
@@ -16,6 +18,7 @@
implements L8ProgramEmulatedInterfaceSynthesizerEventConsumer,
DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer,
+ DesugaredLibraryCustomConversionEventConsumer,
RecordDesugaringEventConsumer {
private Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
@@ -40,6 +43,11 @@
synthesizedClasses.add(clazz);
}
+ @Override
+ public void acceptCustomConversionClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
+
public Set<DexProgramClass> getSynthesizedClasses() {
return synthesizedClasses;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfCodeTypeRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/CfCodeTypeRewriter.java
new file mode 100644
index 0000000..8aa124c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfCodeTypeRewriter.java
@@ -0,0 +1,131 @@
+// 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;
+
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfTypeInstruction;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ListUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Rewrites all references to a type in a CfCode instance to another type.
+ *
+ * <p>The implementation is minimal for the needs of DesugaredLibraryCustomConversionRewriter.
+ */
+public class CfCodeTypeRewriter {
+
+ private final DexItemFactory factory;
+
+ public CfCodeTypeRewriter(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
+ public DexEncodedMethod rewriteCfCode(
+ DexEncodedMethod method, Map<DexType, DexType> replacement) {
+ if (method.getCode() == null) {
+ assert false;
+ return null;
+ }
+ return DexEncodedMethod.builder(method)
+ .setMethod(rewriteMethod(method.getReference(), replacement))
+ .setCode(rewriteCfCode(method.getCode(), replacement))
+ .build();
+ }
+
+ private Code rewriteCfCode(Code code, Map<DexType, DexType> replacement) {
+ assert code.isCfCode();
+ CfCode cfCode = code.asCfCode();
+ List<CfInstruction> desugaredInstructions =
+ ListUtils.map(
+ cfCode.getInstructions(),
+ instruction -> rewriteCfInstruction(instruction, replacement));
+ cfCode.setInstructions(desugaredInstructions);
+ return cfCode;
+ }
+
+ private CfInstruction rewriteCfInstruction(
+ CfInstruction instruction, Map<DexType, DexType> replacement) {
+ if (instruction.isInvoke()) {
+ CfInvoke cfInvoke = instruction.asInvoke();
+ DexMethod replacementMethod = rewriteMethod(cfInvoke.getMethod(), replacement);
+ return cfInvoke.withMethod(replacementMethod);
+ }
+ if (instruction.isTypeInstruction()) {
+ CfTypeInstruction typeInstruction = instruction.asTypeInstruction();
+ return typeInstruction.withType(rewriteType(typeInstruction.getType(), replacement));
+ }
+ if (instruction.isFieldInstruction()) {
+ CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
+ DexField field = rewriteField(fieldInstruction.getField(), replacement);
+ return fieldInstruction.withField(field);
+ }
+ if (instruction.isFrame()) {
+ CfFrame cfFrame = instruction.asFrame();
+ return rewriteFrame(cfFrame, replacement);
+ }
+ assert !instruction.isInvokeDynamic();
+ return instruction;
+ }
+
+ private CfFrame rewriteFrame(CfFrame cfFrame, Map<DexType, DexType> replacement) {
+ Int2ReferenceAVLTreeMap<FrameType> newLocals = new Int2ReferenceAVLTreeMap<>();
+ cfFrame
+ .getLocals()
+ .forEach((i, frameType) -> newLocals.put(i, rewriteFrameType(frameType, replacement)));
+ Deque<FrameType> newStack = new ArrayDeque<>();
+ cfFrame
+ .getStack()
+ .forEach(frameType -> newStack.addLast(rewriteFrameType(frameType, replacement)));
+ return new CfFrame(newLocals, newStack);
+ }
+
+ private FrameType rewriteFrameType(FrameType frameType, Map<DexType, DexType> replacement) {
+ if (frameType.isInitialized()) {
+ return FrameType.initialized(rewriteType(frameType.getInitializedType(), replacement));
+ }
+ if (frameType.isUninitializedNew()) {
+ FrameType.uninitializedNew(
+ frameType.getUninitializedLabel(),
+ rewriteType(frameType.getUninitializedNewType(), replacement));
+ }
+ return frameType;
+ }
+
+ private DexType rewriteType(DexType type, Map<DexType, DexType> replacement) {
+ return replacement.getOrDefault(type, type);
+ }
+
+ private DexField rewriteField(DexField field, Map<DexType, DexType> replacement) {
+ DexType newHolder = rewriteType(field.holder, replacement);
+ DexType newType = rewriteType(field.type, replacement);
+ return factory.createField(newHolder, newType, field.name);
+ }
+
+ private DexMethod rewriteMethod(DexMethod reference, Map<DexType, DexType> replacement) {
+ DexType newHolder = rewriteType(reference.holder, replacement);
+ DexType newReturnType = rewriteType(reference.getReturnType(), replacement);
+ DexType[] newParameters = new DexType[reference.getArity()];
+ for (int i = 0; i < reference.getArity(); i++) {
+ newParameters[i] = rewriteType(reference.getParameter(i), replacement);
+ }
+ return factory.createMethod(
+ newHolder, factory.createProto(newReturnType, newParameters), reference.getName());
+ }
+}
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 2493dd3..39d072a 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
@@ -100,6 +100,11 @@
}
@Override
+ public void acceptCustomConversionClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
assert false;
}
@@ -282,6 +287,11 @@
}
@Override
+ public void acceptCustomConversionClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -436,6 +446,11 @@
}
@Override
+ public void acceptCustomConversionClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
// Intentionally empty. The method will be hit by tracing if required.
}
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 8f5178e..486915a 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
@@ -118,6 +118,11 @@
public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
// Intentionally empty.
}
+
+ @Override
+ public void acceptCustomConversionClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
}
public static class R8PostProcessingDesugaringEventConsumer
@@ -191,5 +196,10 @@
public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
additions.addLiveClasspathClass(clazz);
}
+
+ @Override
+ public void acceptCustomConversionClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryCustomConversionRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryCustomConversionRewriter.java
new file mode 100644
index 0000000..2a800f8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryCustomConversionRewriter.java
@@ -0,0 +1,160 @@
+// 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 static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.vivifiedTypeFor;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizer.conversionEncodedMethod;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfCodeTypeRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryCustomConversionEventConsumer;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * L8 specific pass, rewrites or generates custom conversions.
+ *
+ * <p>In normal set-ups, custom conversions are provided with the types: type <-> rewrittenType.
+ * However the compiler uses internally the types: vivifiedType <-> type during compilation. This
+ * pass rewrites the custom conversions from type <-> rewrittenType to vivifiedType <-> type so the
+ * compilation can look-up and find the methods.
+ *
+ * <p>In broken set-ups, where the input is missing, instead generates the custom conversions on the
+ * classpath since they are assumed to be present in L8.
+ */
+public class DesugaredLibraryCustomConversionRewriter implements CfClassSynthesizerDesugaring {
+
+ private final AppView<?> appView;
+ private final DesugaredLibraryWrapperSynthesizer synthesizer;
+ private final DexString libraryPrefix;
+
+ public DesugaredLibraryCustomConversionRewriter(
+ AppView<?> appView, DesugaredLibraryWrapperSynthesizer synthesizer) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ this.appView = appView;
+ this.synthesizer = synthesizer;
+ this.libraryPrefix =
+ appView
+ .dexItemFactory()
+ .createString(
+ "L"
+ + appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getSynthesizedLibraryClassesPackagePrefix());
+ }
+
+ @Override
+ public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+ Map<DexProgramClass, Set<DexEncodedMethod>> methodsToProcessPerClass =
+ readConversionMethodsToConvert(eventConsumer);
+ methodsToProcessPerClass.forEach(
+ (conversionHolder, conversionsMethods) -> {
+ Set<DexEncodedMethod> convertedMethods = convertMethods(conversionsMethods);
+ conversionHolder.getMethodCollection().removeMethods(conversionsMethods);
+ conversionHolder.addDirectMethods(convertedMethods);
+ });
+ }
+
+ private Map<DexProgramClass, Set<DexEncodedMethod>> readConversionMethodsToConvert(
+ DesugaredLibraryCustomConversionEventConsumer eventConsumer) {
+ Map<DexProgramClass, Set<DexEncodedMethod>> methodsToProcessPerClass = new IdentityHashMap<>();
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getCustomConversions()
+ .forEach(
+ (convertedType, conversionHolder) -> {
+ DexClass dexClass = appView.definitionFor(conversionHolder);
+ if (dexClass == null || dexClass.isNotProgramClass()) {
+ generateMissingConversion(eventConsumer, convertedType, conversionHolder);
+ return;
+ }
+ DexProgramClass conversionProgramClass = dexClass.asProgramClass();
+ DexType rewrittenType = appView.rewritePrefix.rewrittenType(convertedType, appView);
+ Set<DexEncodedMethod> methods =
+ methodsToProcessPerClass.computeIfAbsent(
+ conversionProgramClass, ignored -> Sets.newIdentityHashSet());
+ methods.add(
+ conversionEncodedMethod(
+ conversionProgramClass,
+ rewrittenType,
+ convertedType,
+ appView.dexItemFactory()));
+ methods.add(
+ conversionEncodedMethod(
+ conversionProgramClass,
+ convertedType,
+ rewrittenType,
+ appView.dexItemFactory()));
+ });
+ return methodsToProcessPerClass;
+ }
+
+ private void generateMissingConversion(
+ DesugaredLibraryCustomConversionEventConsumer eventConsumer,
+ DexType convertedType,
+ DexType conversionHolder) {
+ appView
+ .options()
+ .reporter
+ .warning(
+ "Missing custom conversion "
+ + conversionHolder
+ + " from L8 compilation: Cannot convert "
+ + convertedType);
+ DexType vivifiedType = vivifiedTypeFor(convertedType, appView);
+ synthesizer.ensureClasspathCustomConversion(
+ vivifiedType, convertedType, eventConsumer, conversionHolder);
+ synthesizer.ensureClasspathCustomConversion(
+ convertedType, vivifiedType, eventConsumer, conversionHolder);
+ }
+
+ private Set<DexEncodedMethod> convertMethods(Set<DexEncodedMethod> conversionsMethods) {
+ Set<DexEncodedMethod> newMethods = Sets.newIdentityHashSet();
+ CfCodeTypeRewriter cfCodeTypeRewriter = new CfCodeTypeRewriter(appView.dexItemFactory());
+ for (DexEncodedMethod conversionMethod : conversionsMethods) {
+ Map<DexType, DexType> replacement = computeReplacement(conversionMethod.getReference());
+ newMethods.add(cfCodeTypeRewriter.rewriteCfCode(conversionMethod, replacement));
+ }
+ return newMethods;
+ }
+
+ private Map<DexType, DexType> computeReplacement(DexMethod reference) {
+ assert reference.getArity() == 1;
+ DexType argumentType = reference.getArgumentType(0, true);
+ if (isLibraryPrefixed(argumentType)) {
+ return ImmutableMap.of(
+ argumentType,
+ convertDesugaredLibraryToVivifiedType(argumentType, reference.getReturnType()));
+ }
+ assert isLibraryPrefixed(reference.getReturnType());
+ return ImmutableMap.of(
+ reference.getReturnType(),
+ convertDesugaredLibraryToVivifiedType(reference.getReturnType(), argumentType));
+ }
+
+ private boolean isLibraryPrefixed(DexType argumentType) {
+ return argumentType.getDescriptor().startsWith(libraryPrefix);
+ }
+
+ private DexType convertDesugaredLibraryToVivifiedType(DexType type, DexType oppositeType) {
+ if (!isLibraryPrefixed(type)) {
+ return type;
+ }
+ return vivifiedTypeFor(oppositeType, appView);
+ }
+}
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 126d020..a28f92a 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
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -26,6 +27,7 @@
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryCustomConversionEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterConstructorCfCodeProvider;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterThrowRuntimeExceptionCfCodeProvider;
@@ -124,13 +126,28 @@
DexType srcType,
DexType destType,
DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
- DexMethod customConversion = getCustomConversion(type, srcType, destType);
- if (customConversion != null) {
- return customConversion;
+ if (hasCustomConversion(type)) {
+ return getCustomConversion(type, srcType, destType, eventConsumer);
}
assert canGenerateWrapper(type) : type;
- DexClass clazz = getValidClassToWrap(type);
- WrapperConversions wrapperConversions = ensureWrappers(clazz, eventConsumer);
+ WrapperConversions wrapperConversions =
+ ensureWrappers(getValidClassToWrap(type), eventConsumer);
+ return extractConversion(type, srcType, destType, wrapperConversions);
+ }
+
+ public DexMethod getExistingProgramConversionMethod(
+ DexType type, DexType srcType, DexType destType) {
+ if (hasCustomConversion(type)) {
+ return getExistingProgramCustomConversion(type, srcType, destType);
+ }
+ assert canGenerateWrapper(type) : type;
+ WrapperConversions wrapperConversions =
+ getExistingProgramWrapperConversions(getValidClassToWrap(type));
+ return extractConversion(type, srcType, destType, wrapperConversions);
+ }
+
+ private DexMethod extractConversion(
+ DexType type, DexType srcType, DexType destType, WrapperConversions wrapperConversions) {
DexMethod conversion =
type == srcType
? wrapperConversions.getConversion()
@@ -140,32 +157,80 @@
return conversion;
}
- public DexMethod getExistingProgramConversionMethod(
- DexType type, DexType srcType, DexType destType) {
- DexMethod customConversion = getCustomConversion(type, srcType, destType);
- if (customConversion != null) {
- return customConversion;
- }
- WrapperConversions wrapperConversions =
- getExistingProgramWrapperConversions(getValidClassToWrap(type));
- DexMethod conversion =
- type == srcType
- ? wrapperConversions.getConversion()
- : wrapperConversions.getVivifiedConversion();
- assert srcType == conversion.getArgumentType(0, true);
- return conversion;
+ private boolean hasCustomConversion(DexType type) {
+ return appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type) != null;
}
- private DexMethod getCustomConversion(DexType type, DexType srcType, DexType destType) {
- // 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".
+ private DexMethod getCustomConversion(
+ DexType type,
+ DexType srcType,
+ DexType destType,
+ DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
+ assert hasCustomConversion(type);
DexType conversionHolder =
appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type);
- if (conversionHolder != null) {
- return factory.createMethod(
- conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
+ assert conversionHolder != null;
+ DexClass dexClass = appView.definitionFor(conversionHolder);
+ if (dexClass != null && dexClass.isProgramClass()) {
+ return getExistingProgramCustomConversion(type, srcType, destType);
}
- return null;
+ return ensureClasspathCustomConversion(srcType, destType, eventConsumer, conversionHolder)
+ .getReference();
+ }
+
+ public DexClassAndMethod ensureClasspathCustomConversion(
+ DexType srcType,
+ DexType destType,
+ DesugaredLibraryCustomConversionEventConsumer eventConsumer,
+ DexType conversionHolder) {
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClassFromTypeMethod(
+ factory.convertMethodName,
+ factory.createProto(destType, srcType),
+ SyntheticKind.CUSTOM_CONVERSION,
+ conversionHolder,
+ appView,
+ ignored -> {},
+ eventConsumer::acceptCustomConversionClasspathClass,
+ methodBuilder ->
+ methodBuilder
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC, false))
+ .setCode(null));
+ }
+
+ private DexMethod getExistingProgramCustomConversion(
+ DexType type, DexType srcType, DexType destType) {
+ assert hasCustomConversion(type);
+ DexType conversionHolder =
+ appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type);
+ assert conversionHolder != null;
+ DexClass dexClass = appView.definitionFor(conversionHolder);
+ assert dexClass != null;
+ // Note: DexClass can unfortunately be a non program class on broken set-ups.
+ DexMethod conversionMethod =
+ conversionMethod(dexClass.type, srcType, destType, appView.dexItemFactory());
+ assert conversionEncodedMethod(dexClass, srcType, destType, appView.dexItemFactory())
+ .getReference()
+ == conversionMethod;
+ return conversionMethod;
+ }
+
+ public static DexEncodedMethod conversionEncodedMethod(
+ DexClass conversionHolder, DexType srcType, DexType destType, DexItemFactory factory) {
+ DexMethod method = conversionMethod(conversionHolder.type, srcType, destType, factory);
+ DexEncodedMethod conversionMethod = conversionHolder.lookupDirectMethod(method);
+ assert conversionMethod != null;
+ return conversionMethod;
+ }
+
+ private static DexMethod conversionMethod(
+ DexType conversionHolder, DexType srcType, DexType destType, DexItemFactory factory) {
+ DexProto proto = factory.createProto(destType, srcType);
+ DexMethod method = factory.createMethod(conversionHolder, proto, factory.convertMethodName);
+ return method;
}
private boolean canConvert(DexType type) {
@@ -276,10 +341,12 @@
}
private DexMethod getConversion(DexClass wrapper, DexType returnType, DexType argType) {
- DexMethod convertMethod =
- factory.createMethod(
- wrapper.type, factory.createProto(returnType, argType), factory.convertMethodName);
- return wrapper.lookupDirectMethod(convertMethod).getReference();
+ DexMethod conversionMethod =
+ conversionMethod(wrapper.type, argType, returnType, appView.dexItemFactory());
+ assert conversionEncodedMethod(wrapper, argType, returnType, appView.dexItemFactory())
+ .getReference()
+ == conversionMethod;
+ return conversionMethod;
}
private DexEncodedField getWrapperUniqueEncodedField(DexClass wrapper) {
@@ -630,6 +697,7 @@
@Override
public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
DesugaredLibraryConfiguration conf = appView.options().desugaredLibraryConfiguration;
+ new DesugaredLibraryCustomConversionRewriter(appView, this).synthesizeClasses(eventConsumer);
List<DexProgramClass> validClassesToWrap = new ArrayList<>();
for (DexType type : conf.getWrapperConversions()) {
assert !conf.getCustomConversions().containsKey(type);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
index 212ad66..a4a894d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
@@ -15,7 +15,13 @@
void acceptWrapperProgramClass(DexProgramClass clazz);
}
- interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer {
+ interface DesugaredLibraryCustomConversionEventConsumer {
+
+ void acceptCustomConversionClasspathClass(DexClasspathClass clazz);
+ }
+
+ interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer
+ extends DesugaredLibraryCustomConversionEventConsumer {
void acceptWrapperClasspathClass(DexClasspathClass clazz);
}
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 7dc9de3..f136195 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
@@ -165,7 +165,6 @@
DesugaredLibraryConfiguration config = options.desugaredLibraryConfiguration;
BiConsumer<DexType, DexType> registerEntry = registerMapEntry(appInfo);
config.getEmulateLibraryInterface().forEach(registerEntry);
- config.getCustomConversions().forEach(registerEntry);
config.getRetargetCoreLibMember().forEach((method, types) -> types.forEach(registerEntry));
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 474d1c3..9e362ed 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -644,31 +644,86 @@
AppView<?> appView,
Consumer<SyntheticClasspathClassBuilder> classConsumer,
Consumer<DexClasspathClass> onCreationConsumer) {
+ // Obtain the outer synthesizing context in the case the context itself is synthetic.
+ // This is to ensure a flat input-type -> synthetic-item mapping.
SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
+ return internalEnsureDexClasspathClass(
+ kind, classConsumer, onCreationConsumer, outerContext, type, appView);
+ }
+
+ private DexClasspathClass internalEnsureDexClasspathClass(
+ SyntheticKind kind,
+ Consumer<SyntheticClasspathClassBuilder> classConsumer,
+ Consumer<DexClasspathClass> onCreationConsumer,
+ SynthesizingContext outerContext,
+ DexType type,
+ AppView<?> appView) {
DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (dexClass != null) {
assert dexClass.isClasspathClass();
return dexClass.asClasspathClass();
}
- synchronized (context) {
+ synchronized (type) {
dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (dexClass != null) {
assert dexClass.isClasspathClass();
return dexClass.asClasspathClass();
}
- // Obtain the outer synthesizing context in the case the context itself is synthetic.
- // This is to ensure a flat input-type -> synthetic-item mapping.
- SyntheticClasspathClassBuilder classBuilder =
- new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory());
- classConsumer.accept(classBuilder);
- DexClasspathClass clazz = classBuilder.build();
- addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
+ DexClasspathClass clazz =
+ internalCreateClasspathClass(
+ kind, classConsumer, outerContext, type, appView.dexItemFactory());
onCreationConsumer.accept(clazz);
return clazz;
}
}
+ private DexClasspathClass internalCreateClasspathClass(
+ SyntheticKind kind,
+ Consumer<SyntheticClasspathClassBuilder> fn,
+ SynthesizingContext outerContext,
+ DexType type,
+ DexItemFactory factory) {
+ SyntheticClasspathClassBuilder classBuilder =
+ new SyntheticClasspathClassBuilder(type, kind, outerContext, factory);
+ fn.accept(classBuilder);
+ DexClasspathClass clazz = classBuilder.build();
+ addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
+ return clazz;
+ }
+
+ public DexClasspathClass ensureFixedClasspathClassFromType(
+ SyntheticKind kind,
+ DexType contextType,
+ AppView<?> appView,
+ Consumer<SyntheticClasspathClassBuilder> classConsumer,
+ Consumer<DexClasspathClass> onCreationConsumer) {
+ SynthesizingContext outerContext = SynthesizingContext.fromType(contextType);
+ DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
+ return internalEnsureDexClasspathClass(
+ kind, classConsumer, onCreationConsumer, outerContext, type, appView);
+ }
+
+ public DexClassAndMethod ensureFixedClasspathClassFromTypeMethod(
+ DexString methodName,
+ DexProto methodProto,
+ SyntheticKind kind,
+ DexType contextType,
+ AppView<?> appView,
+ Consumer<SyntheticClasspathClassBuilder> classConsumer,
+ Consumer<DexClasspathClass> onCreationConsumer,
+ Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+ DexClasspathClass clazz =
+ ensureFixedClasspathClassFromType(
+ kind, contextType, appView, classConsumer, onCreationConsumer);
+ DexMethod methodReference =
+ appView.dexItemFactory().createMethod(clazz.getType(), methodProto, methodName);
+ DexEncodedMethod methodDefinition =
+ internalEnsureMethod(
+ methodReference, clazz, kind, appView, buildMethodCallback, emptyConsumer());
+ return DexClassAndMethod.create(clazz, methodDefinition);
+ }
+
public DexClassAndMethod ensureFixedClasspathClassMethod(
DexString methodName,
DexProto methodProto,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 0e45bd8..567b595 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -157,6 +157,10 @@
}
private Code getCodeObject(DexMethod methodSignature) {
+ if (codeGenerator == null) {
+ // The codeGenerator may be null on classpath classes.
+ return null;
+ }
return codeGenerator.generate(methodSignature);
}
}
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 0a05c0f..872fdee 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -28,6 +28,7 @@
ENUM_UNBOXING_LOCAL_UTILITY_CLASS("$EnumUnboxingLocalUtility", 24, false, true),
ENUM_UNBOXING_SHARED_UTILITY_CLASS("$EnumUnboxingSharedUtility", 25, false, true),
RECORD_TAG("", 1, false, true, true),
+ CUSTOM_CONVERSION("", 31, false, true),
COMPANION_CLASS("$-CC", 2, false, true),
EMULATED_INTERFACE_CLASS("$-EL", 3, false, true),
RETARGET_CLASS("RetargetClass", 20, false, true),