Merge "Disable assertion in vertical class merger"
diff --git a/.gitignore b/.gitignore
index 14e24ec..c04d9dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,6 +63,8 @@
third_party/kotlin
third_party/nest/*
!third_party/nest/*.sha1
+third_party/opensource_apps
+third_party/opensource_apps.tar.gz
third_party/photos/*
!third_party/photos/*.sha1
third_party/proguard/*
diff --git a/build.gradle b/build.gradle
index f129fb5..4ce2e18 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,6 @@
ext {
androidSupportVersion = '25.4.0'
asmVersion = '6.2.1'
- autoValueVersion = '1.5'
espressoVersion = '3.0.0'
fastutilVersion = '7.2.0'
guavaVersion = '23.0'
@@ -66,7 +65,6 @@
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
- classpath "net.ltgt.gradle:gradle-apt-plugin:0.12"
classpath "com.gradle:build-scan-plugin:1.14"
}
}
@@ -81,7 +79,6 @@
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'net.ltgt.errorprone-base'
-apply plugin: "net.ltgt.apt"
if (project.hasProperty('with_code_coverage')) {
apply plugin: 'jacoco'
@@ -257,7 +254,6 @@
examplesCompile "com.google.guava:guava:$guavaVersion"
examplesCompile "junit:junit:$junitVersion"
examplesCompile "org.mockito:mockito-core:$mockitoVersion"
- examplesCompileOnly "com.google.auto.value:auto-value:$autoValueVersion"
supportLibs "com.android.support:support-v4:$androidSupportVersion"
supportLibs "junit:junit:$junitVersion"
supportLibs "com.android.support.test.espresso:espresso-core:$espressoVersion"
@@ -266,7 +262,6 @@
debugTestResourcesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
examplesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
- apt "com.google.auto.value:auto-value:$autoValueVersion"
}
def r8LibPath = "$buildDir/libs/r8lib.jar"
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 3eda1b7..a84b916 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -45,6 +45,10 @@
return opcode;
}
+ public boolean isInterface() {
+ return itf;
+ }
+
@Override
public void write(MethodVisitor visitor, NamingLens lens) {
String owner = lens.lookupInternalName(method.getHolder());
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d45fd83..f956b53 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -638,14 +638,15 @@
return builder.build();
}
- public DexEncodedMethod toForwardingMethod(DexClass holder, DexItemFactory itemFactory) {
+ public DexEncodedMethod toForwardingMethod(DexClass holder, AppInfo appInfo) {
checkIfObsolete();
// Clear the final flag, as this method is now overwritten. Do this before creating the builder
// for the forwarding method, as the forwarding method will copy the access flags from this,
// and if different forwarding methods are created in different subclasses the first could be
// final.
accessFlags.demoteFromFinal();
- DexMethod newMethod = itemFactory.createMethod(holder.type, method.proto, method.name);
+ DexMethod newMethod =
+ appInfo.dexItemFactory.createMethod(holder.type, method.proto, method.name);
Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
Builder builder = builder(this);
builder.setMethod(newMethod);
@@ -655,6 +656,7 @@
builder.accessFlags.setAbstract();
} else {
// Create code that forwards the call to the target.
+ DexClass target = appInfo.definitionFor(method.holder);
builder.setCode(
new SynthesizedCode(
callerPosition ->
@@ -665,7 +667,8 @@
accessFlags.isStatic() ? null : method.holder,
method,
type,
- callerPosition),
+ callerPosition,
+ target.isInterface()),
registry -> {
if (accessFlags.isStatic()) {
registry.registerInvokeStatic(method);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 14d7ffb..40756ae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.code;
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isVisibleFromOriginalContext;
-
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -21,7 +19,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -101,24 +98,6 @@
}
@Override
- public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
- // instance-get can be dead code as long as it cannot have any of the following:
- // * NoSuchFieldError (resolution failure)
- // * IllegalAccessError (not visible from the access context)
- // * NullPointerException (null receiver).
- // TODO(b/123857022): Should be possible to use definitionFor().
- DexEncodedField resolvedField = appInfo.resolveFieldOn(getField().getHolder(), getField());
- if (resolvedField == null) {
- return false;
- }
- if (code == null
- || !isVisibleFromOriginalContext(appInfo, code.method.method.getHolder(), resolvedField)) {
- return false;
- }
- return object().getTypeLattice().nullability().isDefinitelyNotNull();
- }
-
- @Override
public int maxInValueRegister() {
return Constants.U4BIT_MAX;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 80e74f3..b79d076 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isVisibleFromOriginalContext;
-
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -20,7 +18,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
@@ -94,25 +91,6 @@
}
@Override
- public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
- // static-get can be dead as long as it cannot have any of the following:
- // * NoSuchFieldError (resolution failure)
- // * IllegalAccessError (not visible from the access context)
- // * side-effects in <clinit>
- // TODO(b/123857022): Should be possible to use definitionFor().
- DexEncodedField resolvedField = appInfo.resolveFieldOn(getField().getHolder(), getField());
- if (resolvedField == null) {
- return false;
- }
- if (code == null
- || !isVisibleFromOriginalContext(appInfo, code.method.method.getHolder(), resolvedField)) {
- return false;
- }
- return appInfo.hasSubtyping()
- && !getField().getHolder().classInitializationMayHaveSideEffects(appInfo);
- }
-
- @Override
public int maxInValueRegister() {
return Constants.U8BIT_MAX;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 8a46e1e..743f2a5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfTryCatch;
@@ -21,6 +22,7 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -179,9 +181,23 @@
CodeRewriter.collapseTrivialGotos(method, code);
DexBuilder.removeRedundantDebugPositions(code);
CfCode code = buildCfCode();
+ assert verifyInvokeInterface(code, appInfo);
return code;
}
+ private static boolean verifyInvokeInterface(CfCode code, AppInfo appInfo) {
+ for (CfInstruction instruction : code.instructions) {
+ if (instruction instanceof CfInvoke) {
+ CfInvoke invoke = (CfInvoke) instruction;
+ if (invoke.getMethod().holder.isClassType()) {
+ DexClass holder = appInfo.definitionFor(invoke.getMethod().holder);
+ assert holder == null || holder.isInterface() == invoke.isInterface();
+ }
+ }
+ }
+ return true;
+ }
+
public DexField resolveField(DexField field) {
DexEncodedField resolvedField = appInfo.resolveFieldOn(field.clazz, field);
return resolvedField == null ? field : resolvedField.field;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 31b0ec6..3f355c1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -1405,19 +1405,6 @@
add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
}
- public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
- addInvoke(type, item, callSiteProto, arguments, false);
- }
-
- public void addInvoke(
- Type type,
- DexItem item,
- DexProto callSiteProto,
- List<ValueType> types,
- List<Integer> registers) {
- addInvoke(type, item, callSiteProto, types, registers, false);
- }
-
public void addInvoke(
Type type,
DexItem item,
@@ -1518,7 +1505,9 @@
registerIndex += constraint.requiredRegisters();
}
checkInvokeArgumentRegisters(registerIndex, argumentRegisterCount);
- addInvoke(type, method, callSiteProto, arguments);
+ // Note: We only call this register variant from DEX inputs where isInterface does not matter.
+ assert !isGeneratingClassFiles();
+ addInvoke(type, method, callSiteProto, arguments, false /* isInterface */);
}
public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
@@ -1538,7 +1527,7 @@
registerIndex += constraint.requiredRegisters();
}
checkInvokeArgumentRegisters(registerIndex, argumentCount);
- addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+ addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments, false /* isInterface */);
}
public void addMultiNewArray(DexType type, int dest, int[] dimensions) {
@@ -1547,7 +1536,7 @@
for (int dimension : dimensions) {
arguments.add(readRegister(dimension, ValueTypeConstraint.INT));
}
- addInvoke(Invoke.Type.MULTI_NEW_ARRAY, type, null, arguments);
+ addInvoke(Invoke.Type.MULTI_NEW_ARRAY, type, null, arguments, false /* isInterface */);
addMoveResult(dest);
}
@@ -1581,7 +1570,9 @@
register += valueTypeConstraint.requiredRegisters();
}
checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
- addInvoke(type, method, callSiteProto, arguments);
+ // Note: We only call this register variant from DEX inputs where isInterface does not matter.
+ assert !isGeneratingClassFiles();
+ addInvoke(type, method, callSiteProto, arguments, false /* isInterface */);
}
public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
@@ -1597,7 +1588,9 @@
register += constraint.requiredRegisters();
}
checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
- addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+ // Note: We only call this register variant from DEX inputs where isInterface does not matter.
+ assert !isGeneratingClassFiles();
+ addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments, false /* isInterface */);
}
private void checkInvokeArgumentRegisters(int expected, int actual) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 1387f75..93e5355 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -2864,7 +2864,13 @@
List<ValueType> argumentTypes = Arrays.asList(valueType(CLASS_TYPE), valueType(INT_ARRAY_TYPE));
List<Integer> argumentRegisters = Arrays.asList(classDestTemp, dimensionsDestTemp);
builder.ensureBlockForThrowingInstruction();
- builder.addInvoke(Invoke.Type.STATIC, newInstance, null, argumentTypes, argumentRegisters);
+ builder.addInvoke(
+ Invoke.Type.STATIC,
+ newInstance,
+ null,
+ argumentTypes,
+ argumentRegisters,
+ false /* isInterface */);
// Pop the temporaries and push the final result.
state.pop(); // classDestTemp.
state.pop(); // dimensionsDestTemp.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index 3554806..81f93a7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -109,8 +109,17 @@
}
// Method call to the original impl-method.
- add(builder -> builder.addInvoke(inferInvokeType(),
- implMethod, implMethod.proto, argValueTypes, argRegisters));
+ // Mirroring assert in constructor, we never need accessors to interfaces.
+ assert !descriptor().implHandle.type.isInvokeInterface();
+ add(
+ builder ->
+ builder.addInvoke(
+ inferInvokeType(),
+ implMethod,
+ implMethod.proto,
+ argValueTypes,
+ argRegisters,
+ false /* isInterface */));
// Does the method have return value?
if (proto.returnType == factory().voidType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index d2a233b..bff3ca1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -100,10 +100,10 @@
private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
DexMethod method = defaultMethod.method;
+ DexClass target = rewriter.findDefinitionFor(method.holder);
// NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
// even if this results in invalid code, these classes are never desugared.
- assert rewriter.findDefinitionFor(method.holder) != null
- && !rewriter.findDefinitionFor(method.holder).isLibraryClass();
+ assert target != null && !target.isLibraryClass();
// New method will have the same name, proto, and also all the flags of the
// default method, including bridge flag.
DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
@@ -124,7 +124,8 @@
null /* static method */,
rewriter.defaultAsMethodOfCompanionClass(method),
Invoke.Type.STATIC,
- callerPosition)));
+ callerPosition,
+ target.isInterface())));
}
// For a given class `clazz` inspects all interfaces it implements directly or
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 955abf0..466dc83 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -175,7 +175,8 @@
method.method,
Invoke.Type.VIRTUAL,
callerPosition,
- true)));
+ false /* isInterface */,
+ true /* castResult */)));
// Optimize to generate DexCode instead of SynthesizedCode.
converter.optimizeSynthesizedMethod(newVirtualMethod);
return newVirtualMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 01b647b..fd875af 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -252,7 +252,8 @@
null,
origMethod,
Type.STATIC,
- callerPosition)));
+ callerPosition,
+ true /* isInterface */)));
newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
dispatchMethods.add(newEncodedMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
index 7e5cd81..9507471 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
@@ -46,8 +46,15 @@
}
// Method call to the main functional interface method.
- add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
- this.mainMethod, this.mainMethod.proto, argValueTypes, argRegisters));
+ add(
+ builder ->
+ builder.addInvoke(
+ Invoke.Type.VIRTUAL,
+ this.mainMethod,
+ this.mainMethod.proto,
+ argValueTypes,
+ argRegisters,
+ false /* isInterface */));
// Does the method have return value?
if (proto.returnType == factory().voidType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
index 2aa4630..cec70bb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -24,12 +24,15 @@
// Create and initialize an instance.
int instance = nextRegister(ValueType.OBJECT);
add(builder -> builder.addNewInstance(instance, lambda.type));
- add(builder -> builder.addInvoke(
- Invoke.Type.DIRECT,
- lambda.constructor,
- lambda.constructor.proto,
- ImmutableList.of(ValueType.OBJECT),
- ImmutableList.of(instance)));
+ add(
+ builder ->
+ builder.addInvoke(
+ Invoke.Type.DIRECT,
+ lambda.constructor,
+ lambda.constructor.proto,
+ ImmutableList.of(ValueType.OBJECT),
+ ImmutableList.of(instance),
+ false /* isInterface */));
// Assign to a field.
add(builder -> builder.addStaticPut(instance, lambda.instanceField));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
index 7f2acc3..b2b9486 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -23,8 +23,14 @@
protected void prepareInstructions() {
// Super constructor call (always java.lang.Object.<init>()).
DexMethod objectInitMethod = lambda.rewriter.objectInitMethod;
- add(builder -> builder.addInvoke(Invoke.Type.DIRECT, objectInitMethod,
- objectInitMethod.proto, Collections.singletonList(getReceiverValue())));
+ add(
+ builder ->
+ builder.addInvoke(
+ Invoke.Type.DIRECT,
+ objectInitMethod,
+ objectInitMethod.proto,
+ Collections.singletonList(getReceiverValue()),
+ false /* isInterface */));
// Assign capture fields.
DexType[] capturedTypes = captures();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index fb06f31..99112d7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -224,8 +224,15 @@
}
// Method call to the method implementing lambda or method-ref.
- add(builder -> builder.addInvoke(target.invokeType,
- methodToCall, methodToCall.proto, argValueTypes, argRegisters));
+ add(
+ builder ->
+ builder.addInvoke(
+ target.invokeType,
+ methodToCall,
+ methodToCall.proto,
+ argValueTypes,
+ argRegisters,
+ false /* isInterface */));
// Does the method have return value?
if (enforcedReturnType.isVoidType()) {
@@ -446,8 +453,15 @@
List<ValueType> argValueTypes = ImmutableList.of(ValueType.OBJECT);
List<Integer> argRegisters = Collections.singletonList(register);
- add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
- method, method.proto, argValueTypes, argRegisters));
+ add(
+ builder ->
+ builder.addInvoke(
+ Invoke.Type.VIRTUAL,
+ method,
+ method.proto,
+ argValueTypes,
+ argRegisters,
+ false /* isInterface */));
ValueType valueType = ValueType.fromDexType(primitiveType);
int result = nextRegister(valueType);
@@ -469,8 +483,15 @@
ValueType valueType = ValueType.fromDexType(primitiveType);
List<ValueType> argValueTypes = ImmutableList.of(valueType);
List<Integer> argRegisters = Collections.singletonList(register);
- add(builder -> builder.addInvoke(Invoke.Type.STATIC,
- method, method.proto, argValueTypes, argRegisters));
+ add(
+ builder ->
+ builder.addInvoke(
+ Invoke.Type.STATIC,
+ method,
+ method.proto,
+ argValueTypes,
+ argRegisters,
+ false /* isInterface */));
int result = nextRegister(ValueType.OBJECT);
add(builder -> builder.addMoveResult(result));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
index 5177e4a..c6aecbb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
@@ -41,18 +41,27 @@
List<ValueType> argTypes = Lists.newArrayList(ValueType.OBJECT, ValueType.INT);
List<Integer> argRegisters = Lists.newArrayList(instance, lambdaId);
- group.forEachLambda(info -> {
- DexType lambda = info.clazz.type;
- if (group.isSingletonLambda(lambda)) {
- int id = group.lambdaId(lambda);
- add(builder -> builder.addNewInstance(instance, groupClassType));
- add(builder -> builder.addConst(TypeLatticeElement.INT, lambdaId, id));
- add(builder -> builder.addInvoke(Type.DIRECT,
- lambdaConstructorMethod, lambdaConstructorMethod.proto, argTypes, argRegisters));
- add(builder -> builder.addStaticPut(
- instance, group.getSingletonInstanceField(factory, id)));
- }
- });
+ group.forEachLambda(
+ info -> {
+ DexType lambda = info.clazz.type;
+ if (group.isSingletonLambda(lambda)) {
+ int id = group.lambdaId(lambda);
+ add(builder -> builder.addNewInstance(instance, groupClassType));
+ add(builder -> builder.addConst(TypeLatticeElement.INT, lambdaId, id));
+ add(
+ builder ->
+ builder.addInvoke(
+ Type.DIRECT,
+ lambdaConstructorMethod,
+ lambdaConstructorMethod.proto,
+ argTypes,
+ argRegisters,
+ false /* isInterface*/));
+ add(
+ builder ->
+ builder.addStaticPut(instance, group.getSingletonInstanceField(factory, id)));
+ }
+ });
assert this.nextInstructionIndex() > 0 : "no single field initialized";
add(IRBuilder::addReturn);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index e8fcd40..2bdc441 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -215,8 +215,15 @@
@Override
void prepareSuperConstructorCall(int receiverRegister) {
- add(builder -> builder.addInvoke(Type.DIRECT, objectInitializer, objectInitializer.proto,
- Lists.newArrayList(ValueType.OBJECT), Lists.newArrayList(receiverRegister)));
+ add(
+ builder ->
+ builder.addInvoke(
+ Type.DIRECT,
+ objectInitializer,
+ objectInitializer.proto,
+ Lists.newArrayList(ValueType.OBJECT),
+ Lists.newArrayList(receiverRegister),
+ false /* isInterface */));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index f677b46..ee0b800 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -233,9 +233,15 @@
void prepareSuperConstructorCall(int receiverRegister) {
int arityRegister = nextRegister(ValueType.INT);
add(builder -> builder.addConst(TypeLatticeElement.INT, arityRegister, arity));
- add(builder -> builder.addInvoke(Type.DIRECT, lambdaInitializer, lambdaInitializer.proto,
- Lists.newArrayList(ValueType.OBJECT, ValueType.INT),
- Lists.newArrayList(receiverRegister, arityRegister)));
+ add(
+ builder ->
+ builder.addInvoke(
+ Type.DIRECT,
+ lambdaInitializer,
+ lambdaInitializer.proto,
+ Lists.newArrayList(ValueType.OBJECT, ValueType.INT),
+ Lists.newArrayList(receiverRegister, arityRegister),
+ false /* isInterface */));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
index 82acf49..bcd0a75 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
@@ -85,16 +85,18 @@
offsets[i] = nextInstructionIndex();
// Emit fake call on `this` receiver.
- add(builder -> {
- if (arguments.isEmpty()) {
- // Late initialization of argument list.
- arguments.add(getReceiverValue());
- for (int index = 0; index < paramCount; index++) {
- arguments.add(getParamValue(index));
- }
- }
- builder.addInvoke(Type.VIRTUAL, impl.method, impl.method.proto, arguments);
- });
+ add(
+ builder -> {
+ if (arguments.isEmpty()) {
+ // Late initialization of argument list.
+ arguments.add(getReceiverValue());
+ for (int index = 0; index < paramCount; index++) {
+ arguments.add(getParamValue(index));
+ }
+ }
+ builder.addInvoke(
+ Type.VIRTUAL, impl.method, impl.method.proto, arguments, false /* isInterface */);
+ });
// Handle return value if needed.
if (returnsValue) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index 37341fb..76dba16 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -23,6 +23,7 @@
private final DexMethod target;
private final Invoke.Type invokeType;
private final boolean castResult;
+ private final boolean isInterface;
public ForwardMethodSourceCode(
DexType receiver,
@@ -31,7 +32,8 @@
DexType targetReceiver,
DexMethod target,
Type invokeType,
- Position callerPosition) {
+ Position callerPosition,
+ boolean isInterface) {
this(
receiver,
method,
@@ -40,6 +42,7 @@
target,
invokeType,
callerPosition,
+ isInterface,
false);
}
@@ -51,6 +54,7 @@
DexMethod target,
Type invokeType,
Position callerPosition,
+ boolean isInterface,
boolean castResult) {
super(receiver, method, callerPosition, originalMethod);
assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
@@ -58,6 +62,7 @@
this.target = target;
this.targetReceiver = targetReceiver;
this.invokeType = invokeType;
+ this.isInterface = isInterface;
this.castResult = castResult;
assert checkSignatures();
@@ -119,8 +124,15 @@
}
// Method call to the target method.
- add(builder -> builder.addInvoke(this.invokeType,
- this.target, this.target.proto, argValueTypes, argRegisters));
+ add(
+ builder ->
+ builder.addInvoke(
+ this.invokeType,
+ this.target,
+ this.target.proto,
+ argValueTypes,
+ argRegisters,
+ this.isInterface));
// Does the method return value?
if (proto.returnType.isVoidType()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 60aec09..3ff88b9 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
-import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -192,7 +191,7 @@
findHolderForInterfaceMethodBridge(originalClass, targetClass.type);
assert bridgeHolder != null;
assert bridgeHolder != targetClass;
- DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo.dexItemFactory);
+ DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo);
bridgeHolder.addMethod(bridgeMethod);
assert lookupTarget.apply(method) == bridgeMethod;
return bridgeMethod;
@@ -243,8 +242,7 @@
DexProgramClass bridgeHolder =
findHolderForVisibilityBridge(originalClass, targetClass, packageDescriptor);
assert bridgeHolder != null;
- DexEncodedMethod bridgeMethod =
- target.toForwardingMethod(bridgeHolder, appInfo.dexItemFactory);
+ DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appInfo);
bridgeHolder.addMethod(bridgeMethod);
assert lookupTarget.apply(method) == bridgeMethod;
return bridgeMethod;
@@ -292,15 +290,14 @@
Set<DexEncodedMethod> contexts = fieldsWithContexts.get(field);
if (target != null && target.field != field
&& contexts.stream().allMatch(context ->
- isVisibleFromOriginalContext(appInfo, context.method.getHolder(), target))) {
+ isVisibleFromOriginalContext(context.method.getHolder(), target))) {
builder.map(field,
lense.lookupField(validTargetFor(target.field, field, lookupTargetOnClass)));
}
}
}
- public static boolean isVisibleFromOriginalContext(
- AppInfo appInfo, DexType context, DexEncodedField field) {
+ private boolean isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
DexType holderType = field.field.getHolder();
DexClass holder = appInfo.definitionFor(holderType);
if (holder == null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index cabfb70..75bb98b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1191,7 +1191,8 @@
newMethod,
graphLense.getOriginalMethodSignature(method.method),
invocationTarget.method,
- invocationTarget.isPrivateMethod() ? DIRECT : STATIC);
+ invocationTarget.isPrivateMethod() ? DIRECT : STATIC,
+ target.isInterface());
// Add the bridge to the list of synthesized bridges such that the method signatures will
// be updated by the end of vertical class merging.
@@ -1884,13 +1885,19 @@
private DexMethod originalMethod;
private DexMethod invocationTarget;
private Type type;
+ private final boolean isInterface;
public SynthesizedBridgeCode(
- DexMethod method, DexMethod originalMethod, DexMethod invocationTarget, Type type) {
+ DexMethod method,
+ DexMethod originalMethod,
+ DexMethod invocationTarget,
+ Type type,
+ boolean isInterface) {
this.method = method;
this.originalMethod = originalMethod;
this.invocationTarget = invocationTarget;
this.type = type;
+ this.isInterface = isInterface;
}
// By the time the synthesized code object is created, vertical class merging still has not
@@ -1919,7 +1926,8 @@
type == DIRECT ? method.holder : null,
invocationTarget,
type,
- callerPosition);
+ callerPosition,
+ isInterface);
}
@Override
diff --git a/src/test/examples/autovalue/SimpleAutoValue.java b/src/test/examples/autovalue/SimpleAutoValue.java
deleted file mode 100644
index 2c0b2c6..0000000
--- a/src/test/examples/autovalue/SimpleAutoValue.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 autovalue;
-
-import com.google.auto.value.AutoValue;
-import javax.annotation.Nullable;
-
-public class SimpleAutoValue {
-
- @AutoValue
- static abstract class Pair {
-
- Pair() {
- // Intentionally left empty.
- }
-
- abstract int getOne();
-
- @Nullable
- abstract String getOther();
-
- abstract String getRequiredOther();
-
- static Builder builder() {
- return new AutoValue_SimpleAutoValue_Pair.Builder();
- }
-
- @AutoValue.Builder
- abstract static class Builder {
-
- abstract Builder setOne(int value);
-
- abstract Builder setOther(String value);
-
- abstract Builder setRequiredOther(String value);
-
- abstract Pair build();
- }
- }
-
- public static void main(String... args) {
- Pair.Builder builder = Pair.builder();
- builder.setOne(42);
- builder.setRequiredOther("123");
- System.out.println(builder.build());
- builder = Pair.builder();
- try {
- builder.build();
- } catch (Exception e) {
- System.out.println(e.getMessage());
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index ea8ed0b..33f28fc 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -462,7 +462,7 @@
main.addMainMethod(
".limit stack 2",
".limit locals 1",
- " getstatic Empty/aField I",
+ " getstatic Empty/aMethod I",
" return");
ensureRuntimeException(builder, NoSuchFieldError.class);
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java b/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
deleted file mode 100644
index ac1fd90..0000000
--- a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.shaking;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.jasmin.JasminBuilder;
-import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
-import com.android.tools.r8.jasmin.JasminTestBase;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.util.Iterator;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class FieldReadsJasminTest extends JasminTestBase {
- private static final String CLS = "Empty";
- private static final String MAIN = "Main";
- private final Backend backend;
-
- @Parameterized.Parameters(name = "Backend: {0}")
- public static Object[] data() {
- return Backend.values();
- }
-
- public FieldReadsJasminTest(Backend backend) {
- this.backend = backend;
- }
-
- @Test
- public void testInstanceGet_nonNullReceiver() throws Exception {
- JasminBuilder builder = new JasminBuilder();
-
- ClassBuilder empty = builder.addClass(CLS);
- empty.addField("protected", "aField", "I", null);
-
- ClassBuilder main = builder.addClass(MAIN);
- main.addMainMethod(
- ".limit stack 2",
- ".limit locals 1",
- " new Empty",
- " dup",
- " invokespecial Empty/<init>()V",
- " getfield Empty/aField I",
- " return");
-
- ensureNoFields(builder, empty);
- }
-
- @Test
- public void testStaticGet_noSideEffect() throws Exception {
- JasminBuilder builder = new JasminBuilder();
-
- ClassBuilder empty = builder.addClass(CLS);
- empty.addStaticField("sField", "I");
-
- ClassBuilder main = builder.addClass(MAIN);
- main.addMainMethod(
- ".limit stack 2",
- ".limit locals 1",
- " getstatic Empty/sField I",
- " return");
-
- ensureNoFields(builder, empty);
- }
-
- private void ensureNoFields(JasminBuilder app, ClassBuilder clazz) throws Exception {
- testForR8(backend)
- .addProgramClassFileData(app.buildClasses())
- .addKeepRules("-keep class * { <methods>; }")
- .compile()
- .inspect(inspector -> {
- ClassSubject classSubject = inspector.clazz(clazz.name);
- assertThat(classSubject, isPresent());
- classSubject.forAllFields(foundFieldSubject -> {
- fail("Expect not to see any fields.");
- });
- });
- }
-
- @Test
- public void testInstanceGet_nullableReceiver() throws Exception {
- JasminBuilder builder = new JasminBuilder();
-
- ClassBuilder empty = builder.addClass(CLS);
- empty.addDefaultConstructor();
- empty.addField("protected", "aField", "I", null);
- MethodSignature foo = empty.addStaticMethod("foo", ImmutableList.of("L" + CLS + ";"), "V",
- ".limit stack 2",
- ".limit locals 1",
- " aload 0",
- " getfield Empty/aField I",
- " return");
-
- ClassBuilder main = builder.addClass(MAIN);
- main.addMainMethod(
- ".limit stack 2",
- ".limit locals 1",
- " aconst_null",
- " invokestatic Empty/foo(L" + CLS + ";)V",
- " return");
-
- ensureFieldExists(builder, empty, foo, "aField");
- }
-
- @Test
- public void testStaticGet_nonTrivialClinit() throws Exception {
- JasminBuilder builder = new JasminBuilder();
-
- ClassBuilder empty = builder.addClass(CLS);
- empty.addDefaultConstructor();
- empty.addStaticField("sField", "I");
- empty.addClassInitializer(
- ".limit stack 3",
- ".limit locals 1",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " ldc \"hello\"",
- " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
- " return");
-
- ClassBuilder main = builder.addClass(MAIN);
- MethodSignature mainMethod = main.addMainMethod(
- ".limit stack 2",
- ".limit locals 1",
- " getstatic Empty/sField I",
- " return");
-
- ensureFieldExists(builder, main, mainMethod, "sField");
- }
-
- @Test
- public void testStaticGet_allocation() throws Exception {
- JasminBuilder builder = new JasminBuilder();
-
- ClassBuilder empty = builder.addClass(CLS);
- empty.addDefaultConstructor();
- empty.addStaticField("sField", "Ljava/lang/String;", "\"8\"");
-
- ClassBuilder main = builder.addClass(MAIN);
- MethodSignature mainMethod = main.addMainMethod(
- ".limit stack 2",
- ".limit locals 1",
- " getstatic Empty/sField Ljava/lang/String;",
- " return");
-
- ensureFieldExists(builder, main, mainMethod, "sField");
- }
-
- private void ensureFieldExists(
- JasminBuilder app, ClassBuilder clazz, MethodSignature method, String fieldName)
- throws Exception {
- testForR8(backend)
- .addProgramClassFileData(app.buildClasses())
- .addKeepRules("-keep class * { <methods>; }")
- .compile()
- .inspect(inspector -> {
- ClassSubject classSubject = inspector.clazz(clazz.name);
- assertThat(classSubject, isPresent());
- MethodSubject methodSubject = classSubject.uniqueMethodWithName(method.name);
- assertThat(methodSubject, isPresent());
- Iterator<InstructionSubject> it =
- methodSubject.iterateInstructions(InstructionSubject::isFieldAccess);
- assertTrue(it.hasNext());
- assertEquals(
- inspector.clazz(CLS).uniqueFieldWithName(fieldName).getFinalName(),
- it.next().getField().name.toString());
- });
- }
-
-}
diff --git a/third_party/opensource_apps.tar.gz.sha1 b/third_party/opensource_apps.tar.gz.sha1
new file mode 100644
index 0000000..9fb2bb2
--- /dev/null
+++ b/third_party/opensource_apps.tar.gz.sha1
@@ -0,0 +1 @@
+64b0689bce8f6789320b34da4e91c6dbcc1296e9
\ No newline at end of file
diff --git a/tools/download_all_benchmark_dependencies.py b/tools/download_all_benchmark_dependencies.py
new file mode 100755
index 0000000..43ec0ab
--- /dev/null
+++ b/tools/download_all_benchmark_dependencies.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+# Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Utility script to make it easier to update what golem builds.
+
+import gradle
+import sys
+import utils
+
+BUILD_TARGETS = ['downloadDeps', 'downloadAndroidCts', 'downloadDx']
+
+def Main():
+ gradle.RunGradle(BUILD_TARGETS)
+ # Download opensource_apps and place in build.
+ utils.DownloadFromX20(utils.OPENSOURCE_APPS_SHA_FILE)
+
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 105e958..55c8857 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -5,6 +5,7 @@
import apk_masseur
import apk_utils
+import golem
import gradle
import os
import optparse
@@ -17,15 +18,20 @@
import as_utils
-SHRINKERS = ['r8', 'r8-minified', 'r8full', 'r8full-minified', 'proguard']
-WORKING_DIR = utils.BUILD
+SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full', 'pg']
+WORKING_DIR = os.path.join(utils.BUILD, 'opensource_apps')
-if 'R8_BENCHMARK_DIR' in os.environ and os.path.isdir(os.environ['R8_BENCHMARK_DIR']):
+if ('R8_BENCHMARK_DIR' in os.environ
+ and os.path.isdir(os.environ['R8_BENCHMARK_DIR'])):
WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
+# For running on Golem all APPS are bundled as an x20-dependency and then copied
+# to WORKING_DIR. To make it easier to update the app-bundle, remove the folder
+# WORKING_DIR and then run run_on_as_app.py --download-only.
APPS = {
# 'app-name': {
# 'git_repo': ...
+ # 'revision': ...,
# 'app_module': ... (default app)
# 'archives_base_name': ... (default same as app_module)
# 'flavor': ... (default no flavor)
@@ -34,6 +40,7 @@
'AnExplorer': {
'app_id': 'dev.dworks.apps.anexplorer.pro',
'git_repo': 'https://github.com/christofferqa/AnExplorer',
+ 'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
'flavor': 'googleMobilePro',
'signed-apk-name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
'min_sdk': 17
@@ -41,6 +48,7 @@
'AntennaPod': {
'app_id': 'de.danoeh.antennapod',
'git_repo': 'https://github.com/christofferqa/AntennaPod.git',
+ 'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
'flavor': 'play',
'min_sdk': 14,
'compile_sdk': 26
@@ -48,58 +56,70 @@
'apps-android-wikipedia': {
'app_id': 'org.wikipedia',
'git_repo': 'https://github.com/christofferqa/apps-android-wikipedia',
+ 'revision': '686e8aa5682af8e6a905054b935dd2daa57e63ee',
'flavor': 'prod',
- 'signed-apk-name': 'app-prod-universal-release.apk'
+ 'signed-apk-name': 'app-prod-universal-release.apk',
},
'chanu': {
- 'app_id': 'com.chanapps.four.activity',
- 'git_repo': 'https://github.com/mkj-gram/chanu.git',
+ 'app_id': 'com.chanapps.four.activity',
+ 'git_repo': 'https://github.com/mkj-gram/chanu.git',
+ 'revision': '04ade1e9c33d707f0850d5eb9d6fa5e8af814a26',
},
'friendlyeats-android': {
'app_id': 'com.google.firebase.example.fireeats',
- 'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git'
+ 'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git',
+ 'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
},
'Instabug-Android': {
'app_id': 'com.example.instabug',
'git_repo': 'https://github.com/christofferqa/Instabug-Android.git',
+ 'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f'
},
'KISS': {
'app_id': 'fr.neamar.kiss',
'git_repo': 'https://github.com/christofferqa/KISS',
+ 'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
},
'materialistic': {
'app_id': 'io.github.hidroh.materialistic',
'git_repo': 'https://github.com/christofferqa/materialistic',
+ 'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
},
'Minimal-Todo': {
'app_id': 'com.avjindersinghsekhon.minimaltodo',
'git_repo': 'https://github.com/christofferqa/Minimal-Todo',
+ 'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
},
'NewPipe': {
'app_id': 'org.schabi.newpipe',
'git_repo': 'https://github.com/christofferqa/NewPipe',
+ 'revision': 'ed543099c7823be00f15d9340f94bdb7cb37d1e6',
},
'rover-android': {
- 'app_id': 'io.rover.app.debug',
- 'app_module': 'debug-app',
- 'git_repo': 'https://github.com/mkj-gram/rover-android.git',
+ 'app_id': 'io.rover.app.debug',
+ 'app_module': 'debug-app',
+ 'git_repo': 'https://github.com/mkj-gram/rover-android.git',
+ 'revision': 'd2e876e597b3af7eab406e38a0e08327a38bd942',
},
'Signal-Android': {
- 'app_id': 'org.thoughtcrime.securesms',
- 'app_module': '',
- 'flavor': 'play',
- 'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
- 'releaseTarget': 'assemblePlayRelease',
- 'signed-apk-name': 'Signal-play-release-4.32.7.apk',
+ 'app_id': 'org.thoughtcrime.securesms',
+ 'app_module': '',
+ 'flavor': 'play',
+ 'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
+ 'revision': '85e1a10993e5e9ffe923f0798b26cbc44068ba31',
+ 'releaseTarget': 'assemblePlayRelease',
+ 'signed-apk-name': 'Signal-play-release-4.32.7.apk',
},
'Simple-Calendar': {
'app_id': 'com.simplemobiletools.calendar.pro',
'git_repo': 'https://github.com/christofferqa/Simple-Calendar',
+ 'revision': '82dad8c203eea5a0f0ddb513506d8f1de986ef2b',
'signed-apk-name': 'calendar-release.apk'
},
'sqldelight': {
'app_id': 'com.example.sqldelight.hockey',
'git_repo': 'https://github.com/christofferqa/sqldelight.git',
+ 'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
'app_module': 'sample/android',
'archives_base_name': 'android',
'min_sdk': 14,
@@ -108,6 +128,7 @@
'tachiyomi': {
'app_id': 'eu.kanade.tachiyomi',
'git_repo': 'https://github.com/sgjesse/tachiyomi.git',
+ 'revision': 'b15d2fe16864645055af6a745a62cc5566629798',
'flavor': 'standard',
'releaseTarget': 'app:assembleRelease',
'min_sdk': 16
@@ -115,21 +136,25 @@
'tivi': {
'app_id': 'app.tivi',
'git_repo': 'https://github.com/sgjesse/tivi.git',
+ 'revision': '7d7f591d6f39d7caeb88dd13bf476c0c06accdfb',
'min_sdk': 23,
'compile_sdk': 28,
},
'Tusky': {
- 'app_id': 'com.keylesspalace.tusky',
- 'git_repo': 'https://github.com/mkj-gram/Tusky.git',
- 'flavor': 'blue'
+ 'app_id': 'com.keylesspalace.tusky',
+ 'git_repo': 'https://github.com/mkj-gram/Tusky.git',
+ 'revision': 'b794f3ab90388add98461ffe70edb65c39351c33',
+ 'flavor': 'blue'
},
'Vungle-Android-SDK': {
- 'app_id': 'com.publisher.vungle.sample',
- 'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
+ 'app_id': 'com.publisher.vungle.sample',
+ 'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
+ 'revision': '3e231396ea7ce97b2655e03607497c75730e45f6',
},
# This does not build yet.
'muzei': {
'git_repo': 'https://github.com/sgjesse/muzei.git',
+ 'revision': 'bed2a5f79c6e08b0a21e3e3f9242232d0848ef74',
'app_module': 'main',
'archives_base_name': 'muzei',
'skip': True,
@@ -161,17 +186,21 @@
return '~~R8' in subprocess.check_output(cmd).strip()
def IsMinifiedR8(shrinker):
- return shrinker == 'r8-minified' or shrinker == 'r8full-minified'
+ return 'nolib' not in shrinker
def IsTrackedByGit(file):
return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
-def GitClone(git_url):
- return subprocess.check_output(['git', 'clone', git_url]).strip()
-
-def GitPull():
- # Use --no-edit to accept the auto-generated merge message, if any.
- return subprocess.call(['git', 'pull', '--no-edit']) == 0
+def GitClone(git_url, revision, checkout_dir, options):
+ result = subprocess.check_output(
+ ['git', 'clone', git_url, checkout_dir]).strip()
+ head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
+ if revision == head_rev:
+ return result
+ warn('Target revision is not head in {}.'.format(checkout_dir))
+ with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
+ subprocess.check_output(['git', 'reset', '--hard', revision])
+ return result
def GitCheckout(file):
return subprocess.check_output(['git', 'checkout', file]).strip()
@@ -230,26 +259,23 @@
return True
def GetResultsForApp(app, config, options, temp_dir):
- git_repo = config['git_repo']
-
# Checkout and build in the build directory.
checkout_dir = os.path.join(WORKING_DIR, app)
result = {}
- if not os.path.exists(checkout_dir):
+ if not os.path.exists(checkout_dir) and not options.golem:
with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
- GitClone(git_repo)
- elif options.pull:
- with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
- # Checkout build.gradle to avoid merge conflicts.
- if IsTrackedByGit('build.gradle'):
- GitCheckout('build.gradle')
+ GitClone(config['git_repo'], config['revision'], checkout_dir, options)
- if not GitPull():
- result['status'] = 'failed'
- result['error_message'] = 'Unable to pull from remote'
- return result
+ checkout_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
+ if config['revision'] != checkout_rev:
+ msg = 'Checkout is not target revision for {} in {}.'.format(
+ app, checkout_dir)
+ if options.ignore_versions:
+ warn(msg)
+ else:
+ raise Exception(msg)
result['status'] = 'success'
@@ -277,7 +303,7 @@
BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
temp_dir, options)
dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
- result['apk_dest'] = apk_dest,
+ result['apk_dest'] = apk_dest
result['build_status'] = 'success'
result['dex_size'] = dex_size
result['profile_dest_dir'] = profile_dest_dir
@@ -367,11 +393,8 @@
shrinker,
' for recompilation' if keepRuleSynthesisForRecompilation else ''))
- # Add/remove 'r8.jar' from top-level build.gradle.
- if options.disable_tot:
- as_utils.remove_r8_dependency(checkout_dir)
- else:
- as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
+ # Add 'r8.jar' from top-level build.gradle.
+ as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
app_module = config.get('app_module', 'app')
archives_base_name = config.get('archives_base_name', app_module)
@@ -398,7 +421,7 @@
# Value for property android.enableR8.
enableR8 = 'r8' in shrinker
# Value for property android.enableR8.fullMode.
- enableR8FullMode = shrinker == 'r8full' or shrinker == 'r8full-minified'
+ enableR8FullMode = shrinker == 'r8-full' or shrinker == 'r8-nolib-full'
# Build gradlew command line.
cmd = ['./gradlew', '--no-daemon', 'clean', releaseTarget,
'--profile', '--stacktrace',
@@ -516,10 +539,28 @@
def LogResultsForApps(result_per_shrinker_per_app, options):
print('')
for app, result_per_shrinker in sorted(
- result_per_shrinker_per_app.iteritems()):
+ result_per_shrinker_per_app.iteritems(), key=lambda s: s[0].lower()):
LogResultsForApp(app, result_per_shrinker, options)
def LogResultsForApp(app, result_per_shrinker, options):
+ if options.print_dexsegments:
+ LogSegmentsForApp(app, result_per_shrinker, options)
+ else:
+ LogComparisonResultsForApp(app, result_per_shrinker, options)
+
+def LogSegmentsForApp(app, result_per_shrinker, options):
+ for shrinker in SHRINKERS:
+ if shrinker not in result_per_shrinker:
+ continue
+ result = result_per_shrinker[shrinker];
+ benchmark_name = '{}-{}'.format(options.print_dexsegments, app)
+ utils.print_dexsegments(benchmark_name, [result.get('apk_dest')])
+ duration = sum(result.get('profile').values())
+ print('%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration * 1000))
+ print('%s-Total(CodeSize): %s' % (benchmark_name, result.get('dex_size')))
+
+
+def LogComparisonResultsForApp(app, result_per_shrinker, options):
print(app + ':')
if result_per_shrinker.get('status', 'success') != 'success':
@@ -527,7 +568,7 @@
print(' skipped ({})'.format(error_message))
return
- proguard_result = result_per_shrinker.get('proguard', {})
+ proguard_result = result_per_shrinker.get('pg', {})
proguard_dex_size = float(proguard_result.get('dex_size', -1))
proguard_duration = sum(proguard_result.get('profile', {}).values())
@@ -596,6 +637,21 @@
result.add_option('--app',
help='What app to run on',
choices=APPS.keys())
+ result.add_option('--download-only', '--download_only',
+ help='Whether to download apps without any compilation',
+ default=False,
+ action='store_true')
+ result.add_option('--golem',
+ help='Running on golem, do not download',
+ default=False,
+ action='store_true')
+ result.add_option('--gradle-flags', '--gradle_flags',
+ help='Flags to pass in to gradle')
+ result.add_option('--ignore-versions', '--ignore_versions',
+ help='Allow checked-out app to differ in revision from '
+ 'pinned',
+ default=False,
+ action='store_true')
result.add_option('--keystore',
help='Path to app.keystore',
default='app.keystore')
@@ -610,10 +666,23 @@
help='Number of events that the monkey should trigger',
default=250,
type=int)
- result.add_option('--pull',
- help='Whether to pull the latest version of each app',
+ result.add_option('--no-build', '--no_build',
+ help='Run without building ToT first (only when using ToT)',
default=False,
action='store_true')
+ result.add_option('--quiet',
+ help='Disable verbose logging',
+ default=False,
+ action='store_true')
+ result.add_option('--print-dexsegments',
+ metavar='BENCHMARKNAME',
+ help='Print the sizes of individual dex segments as ' +
+ '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
+ '<bytes>\'')
+ result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
+ help='Number of times R8 should be run on each app',
+ default=2,
+ type=int)
result.add_option('--sign-apks', '--sign_apks',
help='Whether the APKs should be signed',
default=False,
@@ -621,57 +690,50 @@
result.add_option('--shrinker',
help='The shrinkers to use (by default, all are run)',
action='append')
- result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
- help='Number of times R8 should be run on each app',
- default=2,
- type=int)
- result.add_option('--disable-tot', '--disable_tot',
- help='Whether to disable the use of the ToT version of R8',
- default=False,
- action='store_true')
- result.add_option('--no-build', '--no_build',
- help='Run without building ToT first (only when using ToT)',
- default=False,
- action='store_true')
- result.add_option('--gradle-flags', '--gradle_flags',
- help='Flags to pass in to gradle')
- result.add_option('--quiet',
- help='Disable verbose logging',
- default=False,
- action='store_true')
(options, args) = result.parse_args(argv)
- if options.disable_tot:
- # r8.jar is required for recompiling the generated APK
- options.r8_compilation_steps = 1
if options.shrinker:
for shrinker in options.shrinker:
assert shrinker in SHRINKERS
return (options, args)
+def download_apps(options):
+ # Download apps and place in build
+ with utils.ChangedWorkingDirectory(WORKING_DIR):
+ for app, config in APPS.iteritems():
+ app_dir = os.path.join(WORKING_DIR, app)
+ if not os.path.exists(app_dir):
+ GitClone(config['git_repo'], config['revision'], app_dir, options)
+
+
def main(argv):
global SHRINKERS
(options, args) = ParseOptions(argv)
+ if options.golem:
+ golem.link_third_party()
+ if os.path.exists(WORKING_DIR):
+ shutil.rmtree(WORKING_DIR)
+ shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
+
+ if not os.path.exists(WORKING_DIR):
+ os.makedirs(WORKING_DIR)
+
+ if options.download_only:
+ download_apps(options)
+ return
+
with utils.TempDir() as temp_dir:
- if options.disable_tot:
- # Cannot run r8 lib without adding r8lib.jar as an dependency
- SHRINKERS = [
- shrinker for shrinker in SHRINKERS
- if 'minified' not in shrinker]
- else:
- if not options.no_build:
- gradle.RunGradle(['r8', 'r8lib'])
+ if not options.no_build or options.golem:
+ gradle.RunGradle(['r8', 'r8lib'])
- assert os.path.isfile(utils.R8_JAR), (
- 'Cannot build from ToT without r8.jar')
- assert os.path.isfile(utils.R8LIB_JAR), (
- 'Cannot build from ToT without r8lib.jar')
+ assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
+ assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
- # Make a copy of r8.jar and r8lib.jar such that they stay the same for
- # the entire execution of this script.
- shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
- shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
+ # Make a copy of r8.jar and r8lib.jar such that they stay the same for
+ # the entire execution of this script.
+ shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
+ shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
result_per_shrinker_per_app = {}
@@ -679,7 +741,7 @@
result_per_shrinker_per_app[options.app] = GetResultsForApp(
options.app, APPS.get(options.app), options, temp_dir)
else:
- for app, config in sorted(APPS.iteritems()):
+ for app, config in sorted(APPS.iteritems(), key=lambda s: s[0].lower()):
if not config.get('skip', False):
result_per_shrinker_per_app[app] = GetResultsForApp(
app, config, options, temp_dir)
diff --git a/tools/utils.py b/tools/utils.py
index aff96a3..08faec6 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -62,6 +62,10 @@
CF_SEGMENTS_TOOL = os.path.join(THIRD_PARTY, 'cf_segments')
PINNED_R8_JAR = os.path.join(REPO_ROOT, 'third_party/r8/r8.jar')
PINNED_PGR8_JAR = os.path.join(REPO_ROOT, 'third_party/r8/r8-pg6.0.1.jar')
+OPENSOURCE_APPS_SHA_FILE = os.path.join(
+ REPO_ROOT,
+ 'third_party/opensource_apps.tar.gz.sha1')
+OPENSOURCE_APPS_FOLDER = os.path.join(REPO_ROOT, 'third_party/opensource_apps/')
# Common environment setup.
@@ -184,9 +188,12 @@
return 'origin/master' in remotes
def get_HEAD_sha1():
+ return get_HEAD_sha1_for_checkout(REPO_ROOT)
+
+def get_HEAD_sha1_for_checkout(checkout):
cmd = ['git', 'rev-parse', 'HEAD']
PrintCmd(cmd)
- with ChangedWorkingDirectory(REPO_ROOT):
+ with ChangedWorkingDirectory(checkout):
return subprocess.check_output(cmd).strip()
def makedirs_if_needed(path):