Merge commit 'ff08464c7250505223ff73e4d752e06c52ee72a0' into dev-release
diff --git a/.gitignore b/.gitignore
index 76604f8..2b1d423 100644
--- a/.gitignore
+++ b/.gitignore
@@ -144,6 +144,8 @@
third_party/sample_libraries
third_party/sample_libraries.tar.gz
third_party/youtube/*
+third_party/youtube-developer/20200415
+third_party/youtube-developer/20200415.tar.gz
tmp/
tools/*.pyc
tools/*/art
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 3b9bb74..7c9cae3 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -72,14 +72,15 @@
contents = f.NewContents()
if (not contents) or (len(contents) == 0):
continue
- if not CopyRightInContents(contents):
+ if not CopyRightInContents(f, contents):
results.append(
output_api.PresubmitError('Could not find Copyright in file: %s' % f))
return results
-def CopyRightInContents(contents):
+def CopyRightInContents(f, contents):
+ expected = ('#' if f.LocalPath().endswith('.py') else '//') + ' Copyright'
for content_line in contents:
- if '// Copyright' in content_line:
+ if expected in content_line:
return True
return False
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index 8b35b54..3d4e92b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -12,6 +12,12 @@
DexEncodedMethod definitionFor(DexMethod method);
+ @SuppressWarnings("unchecked")
+ default <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ DexEncodedMember<D, R> definitionFor(DexMember<D, R> member) {
+ return (DexEncodedMember<D, R>) definitionFor((DexReference) member);
+ }
+
DexClass definitionFor(DexType type);
DexProgramClass definitionForProgramType(DexType type);
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 1b8e3a0..c91b62d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -144,6 +144,10 @@
unset(Constants.ACC_BRIDGE);
}
+ public void demoteFromBridge() {
+ demote(Constants.ACC_BRIDGE);
+ }
+
public boolean isVarargs() {
return isSet(Constants.ACC_VARARGS);
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index 061cbcf..b41e8fa 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -127,7 +127,7 @@
}
private boolean hasKotlincClinitAssertionCode(DexEncodedMethod method) {
- if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) {
+ if (method.holder() == dexItemFactory.kotlin.assertions.type) {
CfCode code = method.getCode().asCfCode();
for (int i = 1; i < code.instructions.size(); i++) {
CfInstruction instruction = code.instructions.get(i - 1);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index a356e8b..68453b8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -77,6 +78,7 @@
private final DexItemFactory dexItemFactory;
private final AssertionTransformation defaultTransformation;
private final List<ConfigurationEntryWithDexString> configuration;
+ private final AssertionsConfiguration.AssertionTransformation kotlinTransformation;
private final boolean enabled;
public AssertionsRewriter(AppView<?> appView) {
@@ -86,6 +88,7 @@
if (!enabled) {
defaultTransformation = null;
configuration = null;
+ kotlinTransformation = null;
return;
}
// Convert the assertion transformation to the representation used for this rewriter.
@@ -94,6 +97,8 @@
appView.options().assertionsConfiguration.assertionsConfigurations.stream()
.map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory()))
.collect(Collectors.toList());
+ kotlinTransformation =
+ getTransformationForType(appView.dexItemFactory().kotlin.assertions.type);
}
// Static method used by other analyses to see if additional analysis is required to support
@@ -104,6 +109,10 @@
}
private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) {
+ return getTransformationForType(method.holder());
+ }
+
+ private AssertionTransformation getTransformationForType(DexType type) {
AssertionTransformation transformation = defaultTransformation;
for (ConfigurationEntryWithDexString entry : configuration) {
switch (entry.entry.getScope()) {
@@ -112,18 +121,18 @@
break;
case PACKAGE:
if (entry.value.size == 0) {
- if (!method.holder().descriptor.contains(dexItemFactory.descriptorSeparator)) {
+ if (!type.descriptor.contains(dexItemFactory.descriptorSeparator)) {
transformation = entry.entry.getTransformation();
}
- } else if (method.holder().descriptor.startsWith(entry.value)) {
+ } else if (type.descriptor.startsWith(entry.value)) {
transformation = entry.entry.getTransformation();
}
break;
case CLASS:
- if (method.holder().descriptor.equals(entry.value)) {
+ if (type.descriptor.equals(entry.value)) {
transformation = entry.entry.getTransformation();
}
- if (isDescriptorForClassOrInnerClass(entry.value, method.holder().descriptor)) {
+ if (isDescriptorForClassOrInnerClass(entry.value, type.descriptor)) {
transformation = entry.entry.getTransformation();
}
break;
@@ -317,10 +326,10 @@
}
clinit = clazz.getClassInitializer();
}
- if (clinit == null || !clinit.getOptimizationInfo().isInitializerEnablingJavaVmAssertions()) {
- return;
- }
-
+ // For javac generated code it is assumed that the code in <clinit> will tell if the code
+ // in other methods of the class can have assertion checks.
+ boolean isInitializerEnablingJavaVmAssertions =
+ clinit != null && clinit.getOptimizationInfo().isInitializerEnablingJavaVmAssertions();
// This code will process the assertion code in all methods including <clinit>.
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
@@ -328,7 +337,7 @@
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
- if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) {
+ if (method.holder() == dexItemFactory.kotlin.assertions.type) {
rewriteKotlinAssertionEnable(code, transformation, iterator, invoke);
} else {
iterator.replaceCurrentInstruction(code.createIntConstant(0));
@@ -341,10 +350,18 @@
}
} else if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
- if (staticGet.getField().name == dexItemFactory.assertionsDisabled) {
+ // Rewrite $assertionsDisabled getter (only if the initializer enabled assertions).
+ if (isInitializerEnablingJavaVmAssertions
+ && staticGet.getField().name == dexItemFactory.assertionsDisabled) {
iterator.replaceCurrentInstruction(
code.createIntConstant(transformation == AssertionTransformation.DISABLE ? 1 : 0));
}
+ // Rewrite kotlin._Assertions.ENABLED getter.
+ if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) {
+ iterator.replaceCurrentInstruction(
+ code.createIntConstant(
+ kotlinTransformation == AssertionTransformation.DISABLE ? 0 : 1));
+ }
}
}
}
@@ -362,7 +379,7 @@
Instruction nextInstruction = iterator.next();
if (nextInstruction.isStaticPut()
&& nextInstruction.asStaticPut().getField().holder
- == dexItemFactory.kotlin.kotlinAssertions
+ == dexItemFactory.kotlin.assertions.type
&& nextInstruction.asStaticPut().getField().name == dexItemFactory.enabledFieldName
&& invoke.outValue().numberOfUsers() == 1
&& invoke.outValue().numberOfPhiUsers() == 0
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 55f42ab..40f9b7f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1041,10 +1041,10 @@
// The synthetic and bridge flags are maintained only if the inlinee has also these flags.
if (context.accessFlags.isBridge() && !inlinee.code.method.accessFlags.isBridge()) {
- context.accessFlags.unsetBridge();
+ context.accessFlags.demoteFromBridge();
}
if (context.accessFlags.isSynthetic() && !inlinee.code.method.accessFlags.isSynthetic()) {
- context.accessFlags.unsetSynthetic();
+ context.accessFlags.demoteFromSynthetic();
}
context.copyMetadata(singleTarget);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 4b50b6e..e25949d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -72,7 +72,9 @@
public static boolean shouldRun(AppView<?> appView, IRCode code) {
return appView.options().enableRedundantFieldLoadElimination
- && (code.metadata().mayHaveFieldGet() || code.metadata().mayHaveInitClass());
+ && (code.metadata().mayHaveFieldGet() || code.metadata().mayHaveInitClass())
+ // TODO(b/154064966): Remove workaround.
+ && code.blocks.size() < 20000;
}
private interface FieldValue {
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index b4308b9..6fe2c18 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexClass;
+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.DexString;
@@ -35,10 +36,10 @@
public final DexItemFactory factory;
- public final DexType kotlinAssertions;
public final Functional functional;
public final Intrinsics intrinsics;
public final Metadata metadata;
+ public final _Assertions assertions;
public static final class ClassClassifiers {
@@ -51,10 +52,10 @@
public Kotlin(DexItemFactory factory) {
this.factory = factory;
- this.kotlinAssertions = factory.createType(addKotlinPrefix("_Assertions;"));
this.functional = new Functional();
this.intrinsics = new Intrinsics();
this.metadata = new Metadata();
+ this.assertions = new _Assertions();
// See {@link org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite}
this.knownTypeConversion =
@@ -172,6 +173,13 @@
public final DexString extraInt = factory.createString("xi");
}
+ public final class _Assertions {
+ public final DexType type = factory.createType(addKotlinPrefix("_Assertions;"));
+ public final DexString enabledFieldName = factory.createString("ENABLED");
+ public final DexField enabledField =
+ factory.createField(type, factory.booleanType, enabledFieldName);
+ }
+
// kotlin.jvm.internal.Intrinsics class
public final class Intrinsics {
public final DexType type = factory.createType(addKotlinPrefix("jvm/internal/Intrinsics;"));
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 176f908..8c79dd7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2308,6 +2308,13 @@
: info.isWritten();
}
+ public boolean isMemberLive(DexEncodedMember<?, ?> member) {
+ assert member != null;
+ return member.isDexEncodedField()
+ ? liveFields.contains(member.asDexEncodedField())
+ : liveMethods.contains(member.asDexEncodedMethod());
+ }
+
public boolean isMethodLive(DexEncodedMethod method) {
return liveMethods.contains(method);
}
@@ -3164,12 +3171,33 @@
}
private void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
+ consequentRootSet.forEachClassWithDependentItems(
+ appView,
+ clazz -> {
+ if (isTypeLive(clazz)) {
+ consequentRootSet.forEachDependentInstanceConstructor(
+ clazz, appView, this::enqueueHolderWithDependentInstanceConstructor);
+ consequentRootSet.forEachDependentStaticMember(
+ clazz, appView, this::enqueueDependentItem);
+ if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
+ clazz)) {
+ consequentRootSet.forEachDependentNonStaticMember(
+ clazz, appView, this::enqueueDependentItem);
+ }
+ compatEnqueueHolderIfDependentNonStaticMember(
+ clazz, consequentRootSet.getDependentKeepClassCompatRule(clazz.type));
+ }
+ });
+ consequentRootSet.forEachMemberWithDependentItems(
+ appView,
+ member -> {
+ if (isMemberLive(member)) {
+ enqueueRootItems(consequentRootSet.getDependentItems(member));
+ }
+ });
// TODO(b/132600955): This modifies the root set. Should the consequent be persistent?
rootSet.addConsequentRootSet(consequentRootSet, addNoShrinking);
enqueueRootItems(consequentRootSet.noShrinking);
- // TODO(b/132828740): Seems incorrect that the precondition is not always met here.
- consequentRootSet.dependentNoShrinking.forEach(
- (precondition, dependentItems) -> enqueueRootItems(dependentItems));
// Check for compatibility rules indicating that the holder must be implicitly kept.
if (forceProguardCompatibility) {
consequentRootSet.dependentKeepClassCompatRule.forEach(
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 c89dd04..d8c8b07 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,8 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import static com.android.tools.r8.shaking.ReprocessClassInitializerRule.Type.ALWAYS;
-import static com.android.tools.r8.shaking.ReprocessClassInitializerRule.Type.NEVER;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
@@ -1260,15 +1259,120 @@
}
}
- public static class RootSet {
+ abstract static class RootSetBase {
- public final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
- private final Set<DexReference> noObfuscation;
+ final Set<DexMethod> neverInline;
+ final Set<DexType> neverClassInline;
+ final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
+ final Set<DexReference> noObfuscation;
+ final Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>> dependentNoShrinking;
+ final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
+ final List<DelayedRootSetActionItem> delayedRootSetActionItems;
+
+ RootSetBase(
+ Set<DexMethod> neverInline,
+ Set<DexType> neverClassInline,
+ Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
+ Set<DexReference> noObfuscation,
+ Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>> dependentNoShrinking,
+ Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
+ List<DelayedRootSetActionItem> delayedRootSetActionItems) {
+ this.neverInline = neverInline;
+ this.neverClassInline = neverClassInline;
+ this.noShrinking = noShrinking;
+ this.noObfuscation = noObfuscation;
+ this.dependentNoShrinking = dependentNoShrinking;
+ this.dependentKeepClassCompatRule = dependentKeepClassCompatRule;
+ this.delayedRootSetActionItems = delayedRootSetActionItems;
+ }
+
+ public void forEachClassWithDependentItems(
+ DexDefinitionSupplier definitions, Consumer<DexProgramClass> consumer) {
+ for (DexReference reference : dependentNoShrinking.keySet()) {
+ if (reference.isDexType()) {
+ DexType type = reference.asDexType();
+ DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(type));
+ if (clazz != null) {
+ consumer.accept(clazz);
+ }
+ }
+ }
+ }
+
+ public void forEachMemberWithDependentItems(
+ DexDefinitionSupplier definitions, Consumer<DexEncodedMember<?, ?>> consumer) {
+ for (DexReference reference : dependentNoShrinking.keySet()) {
+ if (reference.isDexMember()) {
+ DexEncodedMember<?, ?> definition = definitions.definitionFor(reference.asDexMember());
+ if (definition != null) {
+ consumer.accept(definition);
+ }
+ }
+ }
+ }
+
+ public void forEachDependentInstanceConstructor(
+ DexProgramClass clazz,
+ AppView<?> appView,
+ Consumer3<DexProgramClass, DexEncodedMethod, Set<ProguardKeepRuleBase>> fn) {
+ getDependentItems(clazz)
+ .forEach(
+ (reference, reasons) -> {
+ DexDefinition definition = appView.definitionFor(reference);
+ if (definition != null
+ && definition.isDexEncodedMethod()
+ && definition.asDexEncodedMethod().isInstanceInitializer()) {
+ fn.accept(clazz, definition.asDexEncodedMethod(), reasons);
+ }
+ });
+ }
+
+ public void forEachDependentNonStaticMember(
+ DexDefinition item,
+ AppView<?> appView,
+ Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
+ getDependentItems(item)
+ .forEach(
+ (reference, reasons) -> {
+ DexDefinition definition = appView.definitionFor(reference);
+ if (definition != null
+ && !definition.isDexClass()
+ && !definition.isStaticMember()) {
+ fn.accept(item, definition, reasons);
+ }
+ });
+ }
+
+ public void forEachDependentStaticMember(
+ DexDefinition item,
+ AppView<?> appView,
+ Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
+ getDependentItems(item)
+ .forEach(
+ (reference, reasons) -> {
+ DexDefinition definition = appView.definitionFor(reference);
+ if (definition != null && !definition.isDexClass() && definition.isStaticMember()) {
+ fn.accept(item, definition, reasons);
+ }
+ });
+ }
+
+ Map<DexReference, Set<ProguardKeepRuleBase>> getDependentItems(DexDefinition item) {
+ return Collections.unmodifiableMap(
+ dependentNoShrinking.getOrDefault(item.toReference(), Collections.emptyMap()));
+ }
+
+ Set<ProguardKeepRuleBase> getDependentKeepClassCompatRule(DexType type) {
+ return dependentKeepClassCompatRule.get(type);
+ }
+ }
+
+ public static class RootSet extends RootSetBase {
+
public final ImmutableList<DexReference> reasonAsked;
public final ImmutableList<DexReference> checkDiscarded;
public final Set<DexMethod> alwaysInline;
public final Set<DexMethod> forceInline;
- public final Set<DexMethod> neverInline;
public final Set<DexMethod> bypassClinitForInlining;
public final Set<DexMethod> whyAreYouNotInlining;
public final Set<DexMethod> keepConstantArguments;
@@ -1276,18 +1380,13 @@
public final Set<DexMethod> reprocess;
public final Set<DexMethod> neverReprocess;
public final PredicateSet<DexType> alwaysClassInline;
- public final Set<DexType> neverClassInline;
public final Set<DexType> neverMerge;
public final Set<DexReference> neverPropagateValue;
public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
public final Map<DexReference, ProguardMemberRule> noSideEffects;
public final Map<DexReference, ProguardMemberRule> assumedValues;
- private final Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>>
- dependentNoShrinking;
- private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
public final Set<DexReference> identifierNameStrings;
public final Set<ProguardIfRule> ifRules;
- public final List<DelayedRootSetActionItem> delayedRootSetActionItems;
private RootSet(
Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
@@ -1315,13 +1414,18 @@
Set<DexReference> identifierNameStrings,
Set<ProguardIfRule> ifRules,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
- this.noShrinking = noShrinking;
- this.noObfuscation = noObfuscation;
+ super(
+ neverInline,
+ neverClassInline,
+ noShrinking,
+ noObfuscation,
+ dependentNoShrinking,
+ dependentKeepClassCompatRule,
+ delayedRootSetActionItems);
this.reasonAsked = reasonAsked;
this.checkDiscarded = checkDiscarded;
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
- this.neverInline = neverInline;
this.bypassClinitForInlining = bypassClinitForInlining;
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
@@ -1329,17 +1433,13 @@
this.reprocess = reprocess;
this.neverReprocess = neverReprocess;
this.alwaysClassInline = alwaysClassInline;
- this.neverClassInline = neverClassInline;
this.neverMerge = neverMerge;
this.neverPropagateValue = neverPropagateValue;
this.mayHaveSideEffects = mayHaveSideEffects;
this.noSideEffects = noSideEffects;
this.assumedValues = assumedValues;
- this.dependentNoShrinking = dependentNoShrinking;
- this.dependentKeepClassCompatRule = dependentKeepClassCompatRule;
this.identifierNameStrings = Collections.unmodifiableSet(identifierNameStrings);
this.ifRules = Collections.unmodifiableSet(ifRules);
- this.delayedRootSetActionItems = delayedRootSetActionItems;
}
public void checkAllRulesAreUsed(InternalOptions options) {
@@ -1384,61 +1484,6 @@
.putAll(dependence));
}
- Set<ProguardKeepRuleBase> getDependentKeepClassCompatRule(DexType type) {
- return dependentKeepClassCompatRule.get(type);
- }
-
- Map<DexReference, Set<ProguardKeepRuleBase>> getDependentItems(DexDefinition item) {
- return Collections.unmodifiableMap(
- dependentNoShrinking.getOrDefault(item.toReference(), Collections.emptyMap()));
- }
-
- public void forEachDependentStaticMember(
- DexDefinition item,
- AppView<?> appView,
- Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
- getDependentItems(item)
- .forEach(
- (reference, reasons) -> {
- DexDefinition definition = appView.definitionFor(reference);
- if (definition != null && !definition.isDexClass() && definition.isStaticMember()) {
- fn.accept(item, definition, reasons);
- }
- });
- }
-
- public void forEachDependentNonStaticMember(
- DexDefinition item,
- AppView<?> appView,
- Consumer3<DexDefinition, DexDefinition, Set<ProguardKeepRuleBase>> fn) {
- getDependentItems(item)
- .forEach(
- (reference, reasons) -> {
- DexDefinition definition = appView.definitionFor(reference);
- if (definition != null
- && !definition.isDexClass()
- && !definition.isStaticMember()) {
- fn.accept(item, definition, reasons);
- }
- });
- }
-
- public void forEachDependentInstanceConstructor(
- DexProgramClass clazz,
- AppView<?> appView,
- Consumer3<DexProgramClass, DexEncodedMethod, Set<ProguardKeepRuleBase>> fn) {
- getDependentItems(clazz)
- .forEach(
- (reference, reasons) -> {
- DexDefinition definition = appView.definitionFor(reference);
- if (definition != null
- && definition.isDexEncodedMethod()
- && definition.asDexEncodedMethod().isInstanceInitializer()) {
- fn.accept(clazz, definition.asDexEncodedMethod(), reasons);
- }
- });
- }
-
public void copy(DexReference original, DexReference rewritten) {
if (noShrinking.containsKey(original)) {
noShrinking.put(rewritten, noShrinking.get(original));
@@ -1695,16 +1740,9 @@
// A partial RootSet that becomes live due to the enabled -if rule or the addition of interface
// keep rules.
- public static class ConsequentRootSet {
- final Set<DexMethod> neverInline;
- final Set<DexType> neverClassInline;
- final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
- final Set<DexReference> noObfuscation;
- final Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>> dependentNoShrinking;
- final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
- final List<DelayedRootSetActionItem> delayedRootSetActionItems;
+ public static class ConsequentRootSet extends RootSetBase {
- private ConsequentRootSet(
+ ConsequentRootSet(
Set<DexMethod> neverInline,
Set<DexType> neverClassInline,
Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
@@ -1712,13 +1750,14 @@
Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>> dependentNoShrinking,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
- this.neverInline = Collections.unmodifiableSet(neverInline);
- this.neverClassInline = Collections.unmodifiableSet(neverClassInline);
- this.noShrinking = Collections.unmodifiableMap(noShrinking);
- this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
- this.dependentNoShrinking = Collections.unmodifiableMap(dependentNoShrinking);
- this.dependentKeepClassCompatRule = Collections.unmodifiableMap(dependentKeepClassCompatRule);
- this.delayedRootSetActionItems = Collections.unmodifiableList(delayedRootSetActionItems);
+ super(
+ neverInline,
+ neverClassInline,
+ noShrinking,
+ noObfuscation,
+ dependentNoShrinking,
+ dependentKeepClassCompatRule,
+ delayedRootSetActionItems);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index b201ce6..c2fdaa1 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -52,17 +53,23 @@
private static final Map<KotlinTargetVersion, Path> kotlinClasses = new HashMap<>();
private final TestParameters parameters;
+ private final boolean kotlinStdlibAsLibrary;
- @Parameterized.Parameters(name = "{0},{1}")
+ @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ KotlinTargetVersion.values(),
+ BooleanUtils.values());
}
public AssertionConfigurationKotlinTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
+ TestParameters parameters,
+ KotlinTargetVersion targetVersion,
+ boolean kotlinStdlibAsClasspath) {
super(targetVersion);
this.parameters = parameters;
+ this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath;
}
@BeforeClass
@@ -76,21 +83,50 @@
}
}
+ private Path kotlinStdlibLibraryForRuntime() throws Exception {
+ Path kotlinStdlibCf = ToolHelper.getKotlinStdlibJar();
+ if (parameters.getRuntime().isCf()) {
+ return kotlinStdlibCf;
+ }
+
+ Path kotlinStdlibDex = temp.newFolder().toPath().resolve("kotlin-stdlib-dex.jar");
+ testForD8()
+ .addProgramFiles(kotlinStdlibCf)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip(kotlinStdlibDex);
+ return kotlinStdlibDex;
+ }
+
private void runD8Test(
ThrowableConsumer<D8TestBuilder> builderConsumer,
ThrowingConsumer<CodeInspector, RuntimeException> inspector,
List<String> outputLines)
throws Exception {
- testForD8()
- .addProgramFiles(ToolHelper.getKotlinStdlibJar())
- .addProgramFiles(kotlinClasses.get(targetVersion))
- .setMinApi(parameters.getApiLevel())
- .apply(builderConsumer)
- .run(
- parameters.getRuntime(),
- getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt")
- .inspect(inspector)
- .assertSuccessWithOutputLines(outputLines);
+ if (kotlinStdlibAsLibrary) {
+ testForD8()
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .addRunClasspathFiles(kotlinStdlibLibraryForRuntime())
+ .run(
+ parameters.getRuntime(),
+ getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt")
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ } else {
+ testForD8()
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .run(
+ parameters.getRuntime(),
+ getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt")
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ }
}
public void runR8Test(
@@ -108,21 +144,38 @@
boolean enableJvmAssertions)
throws Exception {
- testForR8(parameters.getBackend())
- .addProgramFiles(ToolHelper.getKotlinStdlibJar())
- .addProgramFiles(kotlinClasses.get(targetVersion))
- .addKeepMainRule(testClassKt)
- .addKeepClassAndMembersRules(class1, class2)
- .setMinApi(parameters.getApiLevel())
- .apply(builderConsumer)
- .noMinification()
- .allowDiagnosticWarningMessages()
- .compile()
- .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
- .enableRuntimeAssertions(enableJvmAssertions)
- .run(parameters.getRuntime(), testClassKt)
- .inspect(inspector)
- .assertSuccessWithOutputLines(outputLines);
+ if (kotlinStdlibAsLibrary) {
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .addKeepMainRule(testClassKt)
+ .addKeepClassAndMembersRules(class1, class2)
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .noMinification()
+ .addRunClasspathFiles(kotlinStdlibLibraryForRuntime())
+ .compile()
+ .enableRuntimeAssertions(enableJvmAssertions)
+ .run(parameters.getRuntime(), testClassKt)
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ } else {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .addKeepMainRule(testClassKt)
+ .addKeepClassAndMembersRules(class1, class2)
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .noMinification()
+ .allowDiagnosticWarningMessages()
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .enableRuntimeAssertions(enableJvmAssertions)
+ .run(parameters.getRuntime(), testClassKt)
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ }
}
private List<String> allAssertionsExpectedLines() {
@@ -136,6 +189,7 @@
private void checkAssertionCodeRemoved(ClassSubject subject, boolean isR8) {
assertThat(subject, isPresent());
if (subject.getOriginalName().equals("kotlin._Assertions")) {
+ assert !kotlinStdlibAsLibrary;
// With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as well,
// as is not used.
assertEquals(
@@ -151,9 +205,9 @@
.streamInstructions()
.anyMatch(InstructionSubject::isConstNumber));
} else {
- // In R8 the false (default) value of kotlin._Assertions.ENABLED is propagated.
- assertEquals(
- !isR8,
+ // The value of kotlin._Assertions.ENABLED is propagated from the assertions configuration
+ // for the class kotlin._Assertions.
+ assertFalse(
subject
.uniqueMethodWithName("m")
.streamInstructions()
@@ -168,6 +222,7 @@
private void checkAssertionCodeEnabled(ClassSubject subject, boolean isR8) {
assertThat(subject, isPresent());
if (subject.getOriginalName().equals("kotlin._Assertions")) {
+ assert !kotlinStdlibAsLibrary;
// With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as is not used.
assertEquals(
(isR8 ? 1 : 2),
@@ -199,6 +254,7 @@
ClassSubject subject = inspector.clazz(clazz);
assertThat(subject, isPresent());
if (subject.getOriginalName().equals("kotlin._Assertions")) {
+ assert !kotlinStdlibAsLibrary;
// With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as is not used.
assertEquals(
(isR8 ? 1 : 2),
@@ -224,7 +280,7 @@
private void checkAssertionCodeRemoved(CodeInspector inspector, boolean isR8) {
if (isR8) {
assertThat(inspector.clazz("kotlin._Assertions"), not(isPresent()));
- } else {
+ } else if (!kotlinStdlibAsLibrary) {
checkAssertionCodeRemoved(inspector, "kotlin._Assertions", isR8);
}
checkAssertionCodeRemoved(inspector, class1, isR8);
@@ -234,7 +290,7 @@
private void checkAssertionCodeEnabled(CodeInspector inspector, boolean isR8) {
if (isR8) {
assertThat(inspector.clazz("kotlin._Assertions"), not(isPresent()));
- } else {
+ } else if (!kotlinStdlibAsLibrary) {
checkAssertionCodeEnabled(inspector, "kotlin._Assertions", isR8);
}
checkAssertionCodeEnabled(inspector, class1, isR8);
@@ -242,7 +298,9 @@
}
private void checkAssertionCodeLeft(CodeInspector inspector, boolean isR8) {
- checkAssertionCodeLeft(inspector, "kotlin._Assertions", isR8);
+ if (!kotlinStdlibAsLibrary) {
+ checkAssertionCodeLeft(inspector, "kotlin._Assertions", isR8);
+ }
checkAssertionCodeLeft(inspector, class1, isR8);
checkAssertionCodeLeft(inspector, class2, isR8);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java
new file mode 100644
index 0000000..f65d00b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java
@@ -0,0 +1,80 @@
+// 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConsequentRootSetWithSatisfiedDependentItemsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ConsequentRootSetWithSatisfiedDependentItemsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ConsequentRootSetWithSatisfiedDependentItemsTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-if class " + A.class.getTypeName(),
+ "-keepclassmembers class " + A.class.getTypeName() + "{",
+ " <init>();",
+ "}")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertFalse(aClassSubject.getDexClass().isAbstract());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ Class<?> clazz = System.currentTimeMillis() > 0 ? A.class : null;
+ instantiate(clazz);
+ }
+
+ static A instantiate(Class<?> clazz) throws Exception {
+ return (A) clazz.getDeclaredConstructor().newInstance();
+ }
+ }
+
+ static class A {
+
+ int x;
+
+ A() {
+ this(42);
+ }
+
+ A(int x) {
+ this.x = x;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
index 28588e7..50d64f8 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnClassTest.java
@@ -217,6 +217,15 @@
return;
}
+ if (shrinker.isR8()) {
+ // The -keepclassmembers rule should not keep Dependent nor DependentUser since DependentUser
+ // is never referenced.
+ assertThat(codeInspector.clazz(EmptyMainClassForIfOnClassTests.class), isPresent());
+ assertThat(codeInspector.clazz(Precondition.class), isPresent());
+ assertEquals(2, codeInspector.allClasses().size());
+ return;
+ }
+
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
@@ -257,6 +266,15 @@
return;
}
+ if (shrinker.isR8()) {
+ // The -keepclassmembers rule should not keep Dependent nor DependentUser since DependentUser
+ // is never referenced.
+ assertThat(codeInspector.clazz(EmptyMainClassForIfOnClassTests.class), isPresent());
+ assertThat(codeInspector.clazz(Precondition.class), isPresent());
+ assertEquals(2, codeInspector.allClasses().size());
+ return;
+ }
+
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
@@ -346,5 +364,4 @@
FieldSubject f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
-
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java
new file mode 100644
index 0000000..02dde28
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java
@@ -0,0 +1,81 @@
+// 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NoLongerSatisfiedIfRuleTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NoLongerSatisfiedIfRuleTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(NoLongerSatisfiedIfRuleTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-if class " + A.class.getTypeName(),
+ "-keep class " + B.class.getTypeName() + " { void m(); }")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("B");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ // TODO(b/153910208): Should be absent since A is dead.
+ assertThat(bClassSubject.uniqueMethodWithName("m"), isPresent());
+ }
+
+ static class TestClass {
+
+ static boolean alwaysFalse = false;
+
+ public static void main(String[] args) {
+ if (alwaysFalse) {
+ System.out.println(new A());
+ }
+ System.out.println(new B());
+ }
+ }
+
+ static class A {}
+
+ static class B {
+
+ void m() {}
+
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSyntheticConstructorTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSyntheticConstructorTest.java
new file mode 100644
index 0000000..ff4f02d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSyntheticConstructorTest.java
@@ -0,0 +1,76 @@
+// 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NoLongerSyntheticConstructorTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NoLongerSyntheticConstructorTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, B.class)
+ .addProgramClassFileData(
+ transformer(A.class)
+ .setAccessFlags(A.class.getDeclaredConstructor(), AccessFlags::setSynthetic)
+ .transform())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-if class " + A.class.getTypeName() + " {",
+ " synthetic <init>();",
+ "}",
+ "-keep class " + B.class.getTypeName())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(new A());
+ }
+ }
+
+ static class A {
+
+ int x;
+
+ A() {
+ this(42);
+ }
+
+ A(int x) {
+ this.x = x;
+ }
+ }
+
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 7bc8279..38a6346 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.transformers.MethodTransformer.MethodContext;
import com.android.tools.r8.utils.DescriptorUtils;
import java.io.IOException;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -324,10 +325,19 @@
return setAccessFlags(method, AccessFlags::setSynthetic);
}
+ public ClassFileTransformer setAccessFlags(
+ Constructor<?> constructor, Consumer<MethodAccessFlags> setter) {
+ return setAccessFlags(Reference.methodFromMethod(constructor), setter);
+ }
+
public ClassFileTransformer setAccessFlags(Method method, Consumer<MethodAccessFlags> setter) {
+ return setAccessFlags(Reference.methodFromMethod(method), setter);
+ }
+
+ private ClassFileTransformer setAccessFlags(
+ MethodReference methodReference, Consumer<MethodAccessFlags> setter) {
return addClassTransformer(
new ClassTransformer() {
- final MethodReference methodReference = Reference.methodFromMethod(method);
@Override
public MethodVisitor visitMethod(
diff --git a/third_party/youtube-developer/20200415.tar.gz.sha1 b/third_party/youtube-developer/20200415.tar.gz.sha1
new file mode 100644
index 0000000..f44d810
--- /dev/null
+++ b/third_party/youtube-developer/20200415.tar.gz.sha1
@@ -0,0 +1 @@
+bfc2082c67a28dc43c975ccc3b0e10d3d31cae5d
\ No newline at end of file