Merge "Ignore -dontskipnonpubliclibraryclassmembers"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d273090..20ba8b1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -210,7 +210,7 @@
"Shrinking can't be performed because some library classes are missing.");
}
}
- rootSet = new RootSetBuilder(application, options.keepRules).run(executorService);
+ rootSet = new RootSetBuilder(application, appInfo, options.keepRules).run(executorService);
Enqueuer enqueuer = new Enqueuer(rootSet, appInfo);
appInfo = enqueuer.run(timing);
if (options.printSeeds) {
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 22ce470..7aef78c 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
@@ -155,22 +155,37 @@
}
}
+ private void removeLambdaDeserializationMethods() {
+ if (lambdaRewriter != null) {
+ lambdaRewriter.removeLambdaDeserializationMethods(application.classes());
+ }
+ }
+
+ private void synthesizeLambdaClasses(Builder builder) {
+ if (lambdaRewriter != null) {
+ lambdaRewriter.adjustAccessibility(builder);
+ lambdaRewriter.synthesizeLambdaClasses(builder);
+ }
+ }
+
+ private void desugarInterfaceMethods(
+ Builder builder, InterfaceMethodRewriter.Flavor includeAllResources) {
+ if (interfaceMethodRewriter != null) {
+ interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
+ }
+ }
+
public DexApplication convertToDex() {
+ removeLambdaDeserializationMethods();
+
convertClassesToDex(application.classes());
// Build a new application with jumbo string info,
Builder builder = new Builder(application);
builder.setHighestSortingString(highestSortingString);
- // Lambda rewriter
- if (lambdaRewriter != null) {
- lambdaRewriter.adjustAccessibility(builder);
- lambdaRewriter.synthesizeLambdaClasses(builder);
- }
-
- if (interfaceMethodRewriter != null) {
- interfaceMethodRewriter.desugarInterfaceMethods(builder, ExcludeDexResources);
- }
+ synthesizeLambdaClasses(builder);
+ desugarInterfaceMethods(builder, ExcludeDexResources);
return builder.build();
}
@@ -207,6 +222,8 @@
}
public DexApplication optimize(ExecutorService executorService) throws ExecutionException {
+ removeLambdaDeserializationMethods();
+
timing.begin("Build call graph");
callGraph = CallGraph.build(application, appInfo.withSubtyping(), graphLense);
timing.end();
@@ -265,15 +282,8 @@
}
}
- // Lambda rewriter.
- if (lambdaRewriter != null) {
- lambdaRewriter.adjustAccessibility(builder);
- lambdaRewriter.synthesizeLambdaClasses(builder);
- }
-
- if (interfaceMethodRewriter != null) {
- interfaceMethodRewriter.desugarInterfaceMethods(builder, IncludeAllResources);
- }
+ synthesizeLambdaClasses(builder);
+ desugarInterfaceMethods(builder, IncludeAllResources);
if (outliner != null) {
timing.begin("IR conversion phase 2");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 5644096..87241cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -46,9 +46,11 @@
private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
private static final String OBJECT_ARRAY_TYPE_DESCR = "[Ljava/lang/Object;";
private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
+ private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
private static final String METAFACTORY_METHOD_NAME = "metafactory";
private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+ private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
@@ -68,6 +70,9 @@
final DexString classConstructorName;
final DexString instanceFieldName;
+ final DexString deserializeLambdaMethodName;
+ final DexProto deserializeLambdaMethodProto;
+
// Maps call sites seen so far to inferred lambda descriptor. It is intended
// to help avoid re-matching call sites we already seen. Note that same call
// site may match one or several lambda classes.
@@ -116,6 +121,10 @@
this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
+
+ this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
+ this.deserializeLambdaMethodProto = factory.createProto(
+ factory.objectType, new DexType[] { factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR) });
}
/**
@@ -151,6 +160,35 @@
}
}
+ /** Remove lambda deserialization methods. */
+ public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+ for (DexProgramClass clazz : classes) {
+ // Search for a lambda deserialization method and remove it if found.
+ DexEncodedMethod[] directMethods = clazz.directMethods;
+ if (directMethods != null) {
+ int methodCount = directMethods.length;
+ for (int i = 0; i < methodCount; i++) {
+ DexEncodedMethod encoded = directMethods[i];
+ DexMethod method = encoded.method;
+ if (method.name == deserializeLambdaMethodName &&
+ method.proto == deserializeLambdaMethodProto) {
+ assert encoded.accessFlags.isStatic();
+ assert encoded.accessFlags.isPrivate();
+ assert encoded.accessFlags.isSynthetic();
+
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[methodCount - 1];
+ System.arraycopy(directMethods, 0, newMethods, 0, i);
+ System.arraycopy(directMethods, i + 1, newMethods, i, methodCount - i - 1);
+ clazz.directMethods = newMethods;
+
+ // We assume there is only one such method in the class.
+ break;
+ }
+ }
+ }
+ }
+ }
+
/**
* Adjust accessibility of referenced application symbols or
* creates necessary accessors.
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 7103abe..28b68b1 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -238,7 +238,13 @@
int end = liveRange.end;
Integer nextEnd;
while ((nextEnd = nextInRange(start, end, ends)) != null) {
- ranges.add(new LocalRange(value, getRegisterForValue(value, start), start, nextEnd));
+ // If an argument value has been split, we have disallowed argument reuse and therefore,
+ // the argument value is also in the argument register throughout the method. For debug
+ // information, we always use the argument register whenever a local corresponds to an
+ // argument value. That avoids ending and restarting locals whenever we move arguments
+ // to lower register.
+ int register = getRegisterForValue(value, value.isArgument() ? 0 : start);
+ ranges.add(new LocalRange(value, register, start, nextEnd));
Integer nextStart = nextInRange(nextEnd, end, starts);
if (nextStart == null) {
start = -1;
@@ -797,9 +803,6 @@
maxRegisterNumber = Math.max(maxRegisterNumber, maxRegister);
}
- // Select a spill register.
- // TODO(ager): At this point this always takes the next unused register number. We need a
- // more intelligent selection of spill registers.
private int getSpillRegister(LiveIntervals intervals) {
int registerNumber = nextUnusedRegisterNumber++;
maxRegisterNumber = registerNumber;
@@ -873,6 +876,18 @@
private boolean allocateSingleInterval(LiveIntervals unhandledInterval, ArgumentReuseMode mode) {
int registerConstraint = unhandledInterval.getRegisterLimit();
assert registerConstraint <= Constants.U16BIT_MAX;
+
+ assert unhandledInterval.requiredRegisters() <= 2;
+ boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
+
+ // Just use the argument register if an argument split has no register constraint. That will
+ // avoid move generation for the argument.
+ if (registerConstraint == Constants.U16BIT_MAX && unhandledInterval.isArgumentInterval()) {
+ int argumentRegister = unhandledInterval.getSplitParent().getRegister();
+ assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, argumentRegister);
+ return true;
+ }
+
if (registerConstraint < Constants.U16BIT_MAX) {
// We always have argument sentinels that will not actually occupy registers. Therefore, we
// allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit.
@@ -952,8 +967,6 @@
}
}
- assert unhandledInterval.requiredRegisters() <= 2;
- boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
// Attempt to use register hints.
if (useRegisterHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair)) {
return true;
@@ -1320,7 +1333,6 @@
splitRangesForSpilledConstant(splitChild, registerNumber);
} else {
splitRangesForSpilledInterval(splitChild, registerNumber);
-
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 6dbf79b..83647b7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -517,6 +517,8 @@
Log.verbose(getClass(), "Adding static field `%s` to live set.", encodedField.field);
}
liveFields.add(encodedField, reason);
+ // Add all dependent members to the workqueue.
+ enqueueRootItems(rootSet.getDependentItems(encodedField));
}
private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
@@ -526,6 +528,8 @@
Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
}
liveFields.add(field, reason);
+ // Add all dependent members to the workqueue.
+ enqueueRootItems(rootSet.getDependentItems(field));
}
private void markDirectStaticOrConstructorMethodAsLive(
@@ -832,6 +836,8 @@
processAnnotations(parameterAnnotation.annotations);
}
method.registerReachableDefinitions(new UseRegistry(method));
+ // Add all dependent members to the workqueue.
+ enqueueRootItems(rootSet.getDependentItems(method));
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 7e27fe0..2b086ae 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -375,6 +376,10 @@
} else if (acceptString("eswithmembernames")) {
builder.setType(ProguardKeepRuleType.KEEP_CLASSES_WITH_MEMBERS);
builder.getModifiersBuilder().allowsShrinking = true;
+ } else {
+ // The only path to here is through "-keep" followed by "class".
+ unacceptString("-keepclass");
+ throw parseError("Unknown option");
}
} else {
builder.setType(ProguardKeepRuleType.KEEP);
@@ -905,6 +910,15 @@
return contents.substring(start, end);
}
+ private void unacceptString(String expected) {
+ assert position >= expected.length();
+ position -= expected.length();
+ for (int i = 0; i < expected.length(); i++) {
+ assert expected.charAt(i) == contents.charAt(position + i);
+ }
+ }
+
+
private void checkNotNegatedPattern() throws ProguardRuleParserException {
skipWhitespace();
if (acceptChar('!')) {
@@ -947,9 +961,11 @@
for (int lineNumber = 0; lineNumber < lines.length; lineNumber++) {
String line = lines[lineNumber];
if (remaining <= line.length() || lineNumber == lines.length - 1) {
- return path.toString() + ":" + (lineNumber + 1) + ":" + remaining + "\n" + line;
+ String arrow = CharBuffer.allocate(remaining).toString().replace( '\0', ' ' ) + '^';
+ return path.toString() + ":" + (lineNumber + 1) + ":" + remaining + "\n" + line
+ + '\n' + arrow;
}
- remaining -= (line.length() + 1); // include newline.
+ remaining -= (line.length() + 1); // Include newline.
}
return path.toString();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 848a743..8afe151 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
@@ -38,6 +39,7 @@
public class RootSetBuilder {
private DexApplication application;
+ private final AppInfo appInfo;
private final List<ProguardConfigurationRule> rules;
private final Map<DexItem, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
private final Set<DexItem> noOptimization = Sets.newIdentityHashSet();
@@ -47,14 +49,15 @@
private final Set<ProguardConfigurationRule> rulesThatUseExtendsOrImplementsWrong =
Sets.newIdentityHashSet();
private final Set<DexItem> checkDiscarded = Sets.newIdentityHashSet();
- private final Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking =
+ private final Map<DexItem, Map<DexItem, ProguardKeepRule>> dependentNoShrinking =
new IdentityHashMap<>();
private final Map<DexItem, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
private final Map<DexItem, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
- public RootSetBuilder(DexApplication application,
+ public RootSetBuilder(DexApplication application, AppInfo appInfo,
List<ProguardConfigurationRule> rules) {
this.application = application;
+ this.appInfo = appInfo;
this.rules = rules;
}
@@ -409,6 +412,39 @@
addItemToSets(clazz, rule, null, null);
}
+ private void includeDescriptor(DexItem item, DexType type, ProguardKeepRule context) {
+ if (type.isArrayType()) {
+ type = type.toBaseType(application.dexItemFactory);
+ }
+ if (type.isPrimitiveType()) {
+ return;
+ }
+ DexClass definition = appInfo.definitionFor(type);
+ if (definition == null || definition.isLibraryClass()) {
+ return;
+ }
+ // Keep the type if the item is also kept.
+ dependentNoShrinking.computeIfAbsent(item, x -> new IdentityHashMap<>())
+ .put(definition, context);
+ // Unconditionally add to no-obfuscation, as that is only checked for surviving items.
+ noObfuscation.add(definition);
+ }
+
+ private void includeDescriptorClasses(DexItem item, ProguardKeepRule context) {
+ if (item instanceof DexEncodedMethod) {
+ DexMethod method = ((DexEncodedMethod) item).method;
+ includeDescriptor(item, method.proto.returnType, context);
+ for (DexType value : method.proto.parameters.values) {
+ includeDescriptor(item, value, context);
+ }
+ } else if (item instanceof DexEncodedField) {
+ DexField field = ((DexEncodedField) item).field;
+ includeDescriptor(item, field.type, context);
+ } else {
+ assert item instanceof DexClass;
+ }
+ }
+
private synchronized void addItemToSets(DexItem item, ProguardConfigurationRule context,
ProguardMemberRule rule, DexType onlyIfClassKept) {
if (context instanceof ProguardKeepRule) {
@@ -436,6 +472,9 @@
assert onlyIfClassKept == null;
keepPackageName.add(item);
}
+ if (modifiers.includeDescriptorClasses) {
+ includeDescriptorClasses(item, keepRule);
+ }
if (modifiers.checkDiscarded) {
checkDiscarded.add(item);
}
@@ -456,14 +495,49 @@
public final Set<DexItem> checkDiscarded;
public final Map<DexItem, ProguardMemberRule> noSideEffects;
public final Map<DexItem, ProguardMemberRule> assumedValues;
- private final Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking;
+ private final Map<DexItem, Map<DexItem, ProguardKeepRule>> dependentNoShrinking;
+
+ private boolean legalNoObfuscationItem(DexItem item) {
+ if (!(item instanceof DexProgramClass
+ || item instanceof DexLibraryClass
+ || item instanceof DexEncodedMethod
+ || item instanceof DexEncodedField)) {
+ }
+ assert item instanceof DexProgramClass
+ || item instanceof DexLibraryClass
+ || item instanceof DexEncodedMethod
+ || item instanceof DexEncodedField;
+ return true;
+ }
+
+ private boolean legalNoObfuscationItems(Set<DexItem> items) {
+ items.forEach(this::legalNoObfuscationItem);
+ return true;
+ }
+
+ private boolean legalDependentNoShrinkingItem(DexItem item) {
+ if (!(item instanceof DexType
+ || item instanceof DexEncodedMethod
+ || item instanceof DexEncodedField)) {
+ }
+ assert item instanceof DexType
+ || item instanceof DexEncodedMethod
+ || item instanceof DexEncodedField;
+ return true;
+ }
+
+ private boolean legalDependentNoShrinkingItems(
+ Map<DexItem, Map<DexItem, ProguardKeepRule>> dependentNoShrinking) {
+ dependentNoShrinking.keySet().forEach(this::legalDependentNoShrinkingItem);
+ return true;
+ }
private RootSet(Map<DexItem, ProguardKeepRule> noShrinking,
Set<DexItem> noOptimization, Set<DexItem> noObfuscation, Set<DexItem> reasonAsked,
Set<DexItem> keepPackageName, Set<DexItem> checkDiscarded,
Map<DexItem, ProguardMemberRule> noSideEffects,
Map<DexItem, ProguardMemberRule> assumedValues,
- Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking) {
+ Map<DexItem, Map<DexItem, ProguardKeepRule>> dependentNoShrinking) {
this.noShrinking = Collections.unmodifiableMap(noShrinking);
this.noOptimization = Collections.unmodifiableSet(noOptimization);
this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
@@ -473,11 +547,16 @@
this.noSideEffects = Collections.unmodifiableMap(noSideEffects);
this.assumedValues = Collections.unmodifiableMap(assumedValues);
this.dependentNoShrinking = dependentNoShrinking;
+ assert legalNoObfuscationItems(noObfuscation);
+ assert legalDependentNoShrinkingItems(dependentNoShrinking);
}
- Map<DexItem, ProguardKeepRule> getDependentItems(DexType type) {
+ Map<DexItem, ProguardKeepRule> getDependentItems(DexItem item) {
+ assert item instanceof DexType
+ || item instanceof DexEncodedMethod
+ || item instanceof DexEncodedField;
return Collections
- .unmodifiableMap(dependentNoShrinking.getOrDefault(type, Collections.emptyMap()));
+ .unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index 9a2b9f2..3f69c2e 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -5,12 +5,12 @@
import com.android.tools.r8.CompilationException;
import java.io.BufferedReader;
-import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class FileUtils {
@@ -66,12 +66,11 @@
}
public static void writeTextFile(Path file, List<String> lines) throws IOException {
- try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
- for (String line : lines) {
- writer.write(line);
- writer.write("\n");
- }
- }
+ Files.write(file, lines);
+ }
+
+ public static void writeTextFile(Path file, String... lines) throws IOException {
+ Files.write(file, Arrays.asList(lines));
}
public static Path validateOutputFile(Path path) throws CompilationException {
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index d2e738d..8a1119a 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package lambdadesugaringnplus;
+import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -296,6 +297,23 @@
}
}
+ static class B62168701 {
+ interface I extends Serializable {
+ String getValue();
+ }
+
+ interface J {
+ static void dump() {
+ I i = () -> "B62168701 -- OK";
+ System.out.println(i.getValue());
+ }
+ }
+
+ static void test() {
+ J.dump();
+ }
+ }
+
static void z(Z p) {
System.out.println(p.foo(null));
}
@@ -401,5 +419,6 @@
B38306708.test();
B38308515.test();
B38302860.test();
+ B62168701.test();
}
}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 978f9de..a5e944a 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -5,6 +5,8 @@
package com.android.tools.r8;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unimplemented;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -86,8 +88,10 @@
D8Command command = builder.setOutputPath(out).build();
try {
ToolHelper.runD8(command, this::combinedOptionConsumer);
+ } catch (Unimplemented | CompilationError | InternalCompilerError re) {
+ throw re;
} catch (RuntimeException re) {
- throw re instanceof CompilationError ? re : re.getCause();
+ throw re.getCause() == null ? re : re.getCause();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index ea94040..6f51624 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -5,6 +5,8 @@
package com.android.tools.r8;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unimplemented;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.UnaryOperator;
@@ -32,8 +34,10 @@
D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
try {
ToolHelper.runD8(command, this::combinedOptionConsumer);
+ } catch (Unimplemented | CompilationError | InternalCompilerError re) {
+ throw re;
} catch (RuntimeException re) {
- throw re instanceof CompilationError ? re : re.getCause();
+ throw re.getCause() == null ? re : re.getCause();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
new file mode 100644
index 0000000..a93fb12
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2017, 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;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class TestBase {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ /**
+ * Write lines of text to a temporary file.
+ */
+ protected Path writeTextToTempFile(String... lines) throws IOException{
+ Path file = temp.newFile().toPath();
+ FileUtils.writeTextFile(file, lines);
+ return file;
+ }
+
+ /**
+ * Build an AndroidApp with the specified test classes.
+ */
+ protected static AndroidApp readClasses(Class... classes) throws IOException {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ for (Class clazz : classes) {
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Build an AndroidApp with the specified test classes.
+ */
+ protected static AndroidApp readClasses(List<Class> classes) throws IOException {
+ return readClasses(classes.toArray(new Class[classes.size()]));
+ }
+
+ /**
+ * Create a temporary JAR file containing the specified test classes.
+ */
+ protected Path jarTestClasses(Class... classes) throws IOException {
+ Path jar = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
+ try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar.toFile()))) {
+ for (Class clazz : classes) {
+ try (FileInputStream in =
+ new FileInputStream(ToolHelper.getClassFileForTestClass(clazz).toFile())) {
+ out.putNextEntry(new ZipEntry(clazz.getCanonicalName().replace('.', '/') + ".class"));
+ ByteStreams.copy(in, out);
+ out.closeEntry();
+ }
+ }
+ }
+ return jar;
+ }
+
+ /**
+ * Create a temporary JAR file containing the specified test classes.
+ */
+ protected Path jarTestClasses(List<Class> classes) throws IOException {
+ return jarTestClasses(classes.toArray(new Class[classes.size()]));
+ }
+
+ /**
+ * Compile an application with R8 using the supplied proguard configuration.
+ */
+ protected AndroidApp compileWithR8(List<Class> classes, Path proguardConfig)
+ throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+ return compileWithR8(readClasses(classes), proguardConfig);
+ }
+
+ /**
+ * Compile an application with R8 using the supplied proguard configuration.
+ */
+ protected AndroidApp compileWithR8(AndroidApp app, Path proguardConfig)
+ throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(app)
+ .addProguardConfigurationFiles(proguardConfig)
+ .build();
+ return ToolHelper.runR8(command);
+ }
+
+ /**
+ * Compile an application with R8 using the supplied proguard configuration.
+ */
+ protected AndroidApp compileWithR8(
+ AndroidApp app, Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
+ throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(app)
+ .addProguardConfigurationFiles(proguardConfig)
+ .build();
+ return ToolHelper.runR8(command, optionsConsumer);
+ }
+
+ /**
+ * Run application on Art with the specified main class.
+ */
+ protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
+ Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
+ app.writeToZip(out, true);
+ return ToolHelper.runArtNoVerificationErrors(
+ ImmutableList.of(out.toString()), mainClass.getCanonicalName(), null);
+ }
+
+ /**
+ * Run a single class application on Java.
+ */
+ protected String runOnJava(Class mainClass) throws Exception {
+ ProcessResult result = ToolHelper.runJava(mainClass);
+ if (result.exitCode != 0) {
+ System.out.println("Std out:");
+ System.out.println(result.stdout);
+ System.out.println("Std err:");
+ System.out.println(result.stderr);
+ assertEquals(0, result.exitCode);
+ }
+ return result.stdout;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 6e45d0b..a5213e2 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -405,6 +405,10 @@
.read();
}
+ public static R8Command.Builder prepareR8CommandBuilder(AndroidApp app) {
+ return R8Command.builder(app);
+ }
+
public static AndroidApp runR8(AndroidApp app)
throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
return runR8(R8Command.builder(app).build());
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 9fb98ae..115f1f9 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -10,16 +10,18 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.graph.DexAccessFlags;
import com.android.tools.r8.graph.DexItemFactory;
import java.io.IOException;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-public class ProguardConfigurationParserTest {
+public class ProguardConfigurationParserTest extends TestBase {
private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/";
private static final String INVALID_PROGUARD_DIR = "src/test/proguard/invalid/";
@@ -340,4 +342,17 @@
ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
parser.parse(Paths.get(PARSE_AND_SKIP_SINGLE_ARGUMENT));
}
+
+ @Test
+ public void parseInvalidKeepClassOption() throws IOException, ProguardRuleParserException {
+ thrown.expect(ProguardRuleParserException.class);
+ thrown.expectMessage("Unknown option at ");
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ Path proguardConfig = writeTextToTempFile(
+ "-keepclassx public class * { ",
+ " native <methods>; ",
+ "} "
+ );
+ parser.parse(proguardConfig);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/ClassWithNativeMethods.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/ClassWithNativeMethods.java
new file mode 100644
index 0000000..0c43035
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/ClassWithNativeMethods.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class ClassWithNativeMethods {
+ public static StaticFieldType staticField;
+ public InstanceFieldType instanceField;
+ public native void method1(NativeArgumentType a);
+ public native NativeReturnType method2();
+ public native NativeReturnType method3(NativeArgumentType a);
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
new file mode 100644
index 0000000..0e24d9b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -0,0 +1,240 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.DexInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import org.junit.Test;
+
+public class IncludeDescriptorClassesTest extends TestBase {
+
+ private static String PROGUARD = "third_party/proguard/proguard5.2.1/bin/proguard.sh";
+
+ private Path runProguard(Path inJar, Path config) throws IOException {
+ Path outJar = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
+ List<String> command = new ArrayList<>();
+ command.add(PROGUARD);
+ command.add("-forceprocessing"); // Proguard just checks the creation time on the in/out jars.
+ command.add("-injars");
+ command.add(inJar.toString());
+ command.add("-libraryjars");
+ command.add(ToolHelper.getDefaultAndroidJar());
+ command.add("@" + config);
+ command.add("-outjar");
+ command.add(outJar.toString());
+ command.add("-printmapping");
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ToolHelper.ProcessResult result = ToolHelper.runProcess(builder);
+ if (result.exitCode != 0) {
+ fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+ }
+ return outJar;
+ }
+
+ private Set<String> readJarClasses(Path jar) throws IOException {
+ Set<String> result = new HashSet<>();
+ try (ZipInputStream in = new ZipInputStream(new FileInputStream(jar.toFile()))) {
+ ZipEntry entry = in.getNextEntry();
+ while (entry != null) {
+ String name = entry.getName();
+ if (name.endsWith(".class")) {
+ result.add(name.substring(0, name.length() - ".class".length()).replace('/', '.'));
+ }
+ entry = in.getNextEntry();
+ }
+ }
+ return result;
+ }
+
+ private String keepMain(Class clazz) {
+ return "-keep public class " + clazz.getCanonicalName() + " {\n"
+ + " public static void main(java.lang.String[]);\n"
+ + "}";
+ }
+
+ private class Result {
+ final DexInspector inspector;
+ final Set<String> classesAfterProguard;
+
+ Result(DexInspector inspector, Set<String> classesAfterProguard) {
+ this.inspector = inspector;
+ this.classesAfterProguard = classesAfterProguard;
+ }
+
+ void assertKept(Class clazz) {
+ assertTrue(inspector.clazz(clazz.getCanonicalName()).isPresent());
+ assertFalse(inspector.clazz(clazz.getCanonicalName()).isRenamed());
+ if (classesAfterProguard != null) {
+ assertTrue(classesAfterProguard.contains(clazz.getCanonicalName()));
+ }
+ }
+
+ void assertRemoved(Class clazz) {
+ assertFalse(inspector.clazz(clazz.getCanonicalName()).isPresent());
+ // TODO(sgjesse): Also check that it was not just renamed...
+ if (classesAfterProguard != null) {
+ assertFalse(classesAfterProguard.contains(clazz.getCanonicalName()));
+ }
+ }
+
+ void assertRenamed(Class clazz) {
+ assertTrue(inspector.clazz(clazz.getCanonicalName()).isPresent());
+ assertTrue(inspector.clazz(clazz.getCanonicalName()).isRenamed());
+ // TODO(sgjesse): Also check that it was actually renamed...
+ if (classesAfterProguard != null) {
+ assertFalse(classesAfterProguard.contains(clazz.getCanonicalName()));
+ }
+ }
+
+ }
+
+ private List<Class> applicationClasses = ImmutableList.of(
+ ClassWithNativeMethods.class, NativeArgumentType.class, NativeReturnType.class,
+ StaticFieldType.class, InstanceFieldType.class);
+ private List<Class> mainClasses = ImmutableList.of(
+ MainCallMethod1.class, MainCallMethod2.class, MainCallMethod3.class);
+
+ Result runTest(Class mainClass, Path proguardConfig) throws Exception {
+ List<Class> classes = new ArrayList<>(applicationClasses);
+ classes.add(mainClass);
+
+ DexInspector inspector = new DexInspector(compileWithR8(classes, proguardConfig));
+
+ Set<String> classesAfterProguard = null;
+ // Actually running Proguard should only be during development.
+ if (false) {
+ Path proguardedJar = runProguard(jarTestClasses(classes), proguardConfig);
+ classesAfterProguard = readJarClasses(proguardedJar);
+ }
+
+ return new Result(inspector, classesAfterProguard);
+ }
+
+ @Test
+ public void testNoIncludesDescriptorClasses() throws Exception {
+ for (Class mainClass : mainClasses) {
+ List<Class> allClasses = new ArrayList<>(applicationClasses);
+ allClasses.add(mainClass);
+
+ Path proguardConfig = writeTextToTempFile(
+ keepMain(mainClass),
+ "-keepclasseswithmembers class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification "
+ );
+
+ Result result = runTest(mainClass, proguardConfig);
+
+ // Without includedescriptorclasses return type argument type and field type are removed.
+ result.assertKept(ClassWithNativeMethods.class);
+ result.assertRemoved(NativeArgumentType.class);
+ result.assertRemoved(NativeReturnType.class);
+ result.assertRemoved(InstanceFieldType.class);
+ result.assertRemoved(StaticFieldType.class);
+ }
+ }
+
+ @Test
+ public void testKeepClassesWithMembers() throws Exception {
+ for (Class mainClass : mainClasses) {
+ Path proguardConfig = writeTextToTempFile(
+ keepMain(mainClass),
+ "-keepclasseswithmembers,includedescriptorclasses class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification "
+ );
+
+ Result result = runTest(mainClass, proguardConfig);
+
+ // With includedescriptorclasses return type, argument type ad field type are not renamed.
+ result.assertKept(ClassWithNativeMethods.class);
+ result.assertKept(NativeArgumentType.class);
+ result.assertKept(NativeReturnType.class);
+ result.assertKept(InstanceFieldType.class);
+ result.assertKept(StaticFieldType.class);
+ }
+ }
+
+ @Test
+ public void testKeepClassMembers() throws Exception {
+ for (Class mainClass : mainClasses) {
+ Path proguardConfig = writeTextToTempFile(
+ keepMain(mainClass),
+ "-keepclassmembers,includedescriptorclasses class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification "
+ );
+
+ Result result = runTest(mainClass, proguardConfig);
+
+ // With includedescriptorclasses return type and argument type are not renamed.
+ result.assertRenamed(ClassWithNativeMethods.class);
+ result.assertKept(NativeArgumentType.class);
+ result.assertKept(NativeReturnType.class);
+ result.assertKept(InstanceFieldType.class);
+ result.assertKept(StaticFieldType.class);
+ }
+ }
+
+ @Test
+ public void testKeepClassMemberNames() throws Exception {
+ for (Class mainClass : mainClasses) {
+ Path proguardConfig = writeTextToTempFile(
+ keepMain(mainClass),
+ // same as -keepclassmembers,allowshrinking,includedescriptorclasses
+ "-keepclassmembernames,includedescriptorclasses class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification "
+ );
+
+ Result result = runTest(mainClass, proguardConfig);
+
+ boolean useNativeArgumentType =
+ mainClass == MainCallMethod1.class || mainClass == MainCallMethod3.class;
+ boolean useNativeReturnType =
+ mainClass == MainCallMethod2.class || mainClass == MainCallMethod3.class;
+
+ result.assertRenamed(ClassWithNativeMethods.class);
+ if (useNativeArgumentType) {
+ result.assertKept(NativeArgumentType.class);
+ } else {
+ result.assertRemoved(NativeArgumentType.class);
+ }
+
+ if (useNativeReturnType) {
+ result.assertKept(NativeReturnType.class);
+ } else {
+ result.assertRemoved(NativeReturnType.class);
+ }
+
+ result.assertRemoved(InstanceFieldType.class);
+ result.assertRemoved(StaticFieldType.class);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/InstanceFieldType.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/InstanceFieldType.java
new file mode 100644
index 0000000..4e7fe34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/InstanceFieldType.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class InstanceFieldType {
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod1.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod1.java
new file mode 100644
index 0000000..a4ae00f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod1.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class MainCallMethod1 {
+ public static void main(String[] args) {
+ ClassWithNativeMethods cwnm = new ClassWithNativeMethods();
+ // Don't mention the argument type.
+ cwnm.method1(null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod2.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod2.java
new file mode 100644
index 0000000..f3b2fa6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class MainCallMethod2 {
+ public static void main(String[] args) {
+ ClassWithNativeMethods cwnm = new ClassWithNativeMethods();
+ // Don't mention the return type.
+ cwnm.method2();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod3.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod3.java
new file mode 100644
index 0000000..22d9b70
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/MainCallMethod3.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class MainCallMethod3 {
+ public static void main(String[] args) {
+ ClassWithNativeMethods cwnm = new ClassWithNativeMethods();
+ // Don't mention the argument or return type.
+ cwnm.method1(null);
+ cwnm.method2();
+ cwnm.method3(null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeArgumentType.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeArgumentType.java
new file mode 100644
index 0000000..e8af30b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeArgumentType.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class NativeArgumentType {
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java
new file mode 100644
index 0000000..f68a434
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class NativeReturnType {
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/StaticFieldType.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/StaticFieldType.java
new file mode 100644
index 0000000..0a83151
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/StaticFieldType.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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.shaking.includedescriptorclasses;
+
+public class StaticFieldType {
+
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 39e7dd2..4e661ba 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -271,6 +271,8 @@
public abstract String getOriginalDescriptor();
public abstract String getFinalDescriptor();
+
+ public abstract boolean isRenamed();
}
private class AbsentClassSubject extends ClassSubject {
@@ -322,6 +324,11 @@
public String getFinalDescriptor() {
return null;
}
+
+ @Override
+ public boolean isRenamed() {
+ return false;
+ }
}
public class FoundClassSubject extends ClassSubject {
@@ -456,6 +463,11 @@
public String getFinalDescriptor() {
return dexClass.type.descriptor.toString();
}
+
+ @Override
+ public boolean isRenamed() {
+ return naming == null || !getFinalDescriptor().equals(getOriginalDescriptor());
+ }
}
public abstract class MemberSubject extends Subject {
@@ -469,6 +481,8 @@
public abstract boolean isFinal();
public abstract Signature getOriginalSignature();
+
+ public abstract Signature getFinalSignature();
}
public abstract class MethodSubject extends MemberSubject {
@@ -485,6 +499,8 @@
Predicate<InstructionSubject> filter) {
return null;
}
+
+ public abstract boolean isRenamed();
}
public class AbsentMethodSubject extends MethodSubject {
@@ -495,6 +511,11 @@
}
@Override
+ public boolean isRenamed() {
+ return false;
+ }
+
+ @Override
public boolean hasAll(DexAccessFlags flags) {
return false;
}
@@ -528,6 +549,11 @@
public Signature getOriginalSignature() {
return null;
}
+
+ @Override
+ public Signature getFinalSignature() {
+ return null;
+ }
}
public class FoundMethodSubject extends MethodSubject {
@@ -546,6 +572,11 @@
}
@Override
+ public boolean isRenamed() {
+ return clazz.naming == null || !getFinalSignature().name.equals(getOriginalSignature().name);
+ }
+
+ @Override
public boolean hasAll(DexAccessFlags flags) {
return dexMethod.accessFlags.containsAllOf(flags);
}
@@ -577,13 +608,18 @@
@Override
public MethodSignature getOriginalSignature() {
- MethodSignature signature = MemberNaming.MethodSignature.fromDexMethod(dexMethod.method);
+ MethodSignature signature = getFinalSignature();
return clazz.naming != null ?
(MethodSignature) clazz.naming.lookup(signature).getOriginalSignature() :
signature;
}
@Override
+ public MethodSignature getFinalSignature() {
+ return MemberNaming.MethodSignature.fromDexMethod(dexMethod.method);
+ }
+
+ @Override
public Iterator<InstructionSubject> iterateInstructions() {
return new InstructionIterator(this);
}
@@ -633,6 +669,11 @@
}
@Override
+ public Signature getFinalSignature() {
+ return null;
+ }
+
+ @Override
public DexEncodedField getField() {
return null;
}
@@ -679,13 +720,18 @@
@Override
public FieldSignature getOriginalSignature() {
- FieldSignature signature = MemberNaming.FieldSignature.fromDexField(dexField.field);
+ FieldSignature signature = getFinalSignature();
return clazz.naming != null ?
(FieldSignature) clazz.naming.lookup(signature).getOriginalSignature() :
signature;
}
@Override
+ public FieldSignature getFinalSignature() {
+ return MemberNaming.FieldSignature.fromDexField(dexField.field);
+ }
+
+ @Override
public DexEncodedField getField() {
return dexField;
}