Merge "Add tests for -if rules with <n> wildcards."
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 28bf331..39d916a 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.cf.code.CfBinop;
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfConstClass;
+import com.android.tools.r8.cf.code.CfConstMethodHandle;
+import com.android.tools.r8.cf.code.CfConstMethodType;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
@@ -20,6 +22,7 @@
import com.android.tools.r8.cf.code.CfInstanceOf;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfMonitor;
@@ -226,6 +229,13 @@
appendMethod(invoke.getMethod());
}
+ public void print(CfInvokeDynamic invoke) {
+ indent();
+ builder.append(opcodeName(Opcodes.INVOKEDYNAMIC)).append(' ');
+ builder.append(invoke.getCallSite().methodName);
+ builder.append(invoke.getCallSite().methodProto.toDescriptorString());
+ }
+
public void print(CfFrame frame) {
StringBuilder builder = new StringBuilder("frame: [");
String separator = "";
@@ -442,6 +452,18 @@
}
}
+ public void print(CfConstMethodHandle handle) {
+ indent();
+ builder.append("ldc ");
+ builder.append(handle.getHandle().toString());
+ }
+
+ public void print(CfConstMethodType type) {
+ indent();
+ builder.append("ldc ");
+ builder.append(type.getType().toString());
+ }
+
private String getLabel(CfLabel label) {
return labels != null ? labels.get(label) : "L?";
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
new file mode 100644
index 0000000..b63b4d5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexMethodHandle;
+import org.objectweb.asm.MethodVisitor;
+
+public class CfConstMethodHandle extends CfInstruction {
+
+ private DexMethodHandle handle;
+
+ public CfConstMethodHandle(DexMethodHandle handle) {
+ this.handle = handle;
+ }
+
+ public DexMethodHandle getHandle() {
+ return handle;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitLdcInsn(handle.toAsmHandle());
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
new file mode 100644
index 0000000..8ec1545
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexProto;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+public class CfConstMethodType extends CfInstruction {
+
+ private DexProto type;
+
+ public CfConstMethodType(DexProto type) {
+ this.type = type;
+ }
+
+ public DexProto getType() {
+ return type;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitLdcInsn(Type.getType(type.toDescriptorString()));
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+}
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 5a07602..86903fe 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
@@ -14,7 +14,7 @@
private final int opcode;
public CfInvoke(int opcode, DexMethod method) {
- assert Opcodes.INVOKEVIRTUAL <= opcode && opcode <= Opcodes.INVOKEDYNAMIC;
+ assert Opcodes.INVOKEVIRTUAL <= opcode && opcode <= Opcodes.INVOKEINTERFACE;
this.opcode = opcode;
this.method = method;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
new file mode 100644
index 0000000..43e6133
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueDouble;
+import com.android.tools.r8.graph.DexValue.DexValueFloat;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueLong;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import java.util.List;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+public class CfInvokeDynamic extends CfInstruction {
+
+ private final DexCallSite callSite;
+
+ public CfInvokeDynamic(DexCallSite callSite) {
+ this.callSite = callSite;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
+ List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+ Object[] bsmArgs = new Object[bootstrapArgs.size()];
+ for (int i = 0; i < bootstrapArgs.size(); i++) {
+ bsmArgs[i] = decodeBootstrapArgument(bootstrapArgs.get(i));
+ }
+ Handle bsmHandle = bootstrapMethod.toAsmHandle();
+ visitor.visitInvokeDynamicInsn(
+ callSite.methodName.toString(),
+ callSite.methodProto.toDescriptorString(),
+ bsmHandle,
+ bsmArgs);
+ }
+
+ private Object decodeBootstrapArgument(DexValue dexValue) {
+ if (dexValue instanceof DexValueInt) {
+ return ((DexValueInt) dexValue).getValue();
+ } else if (dexValue instanceof DexValueLong) {
+ return ((DexValueLong) dexValue).getValue();
+ } else if (dexValue instanceof DexValueFloat) {
+ return ((DexValueFloat) dexValue).getValue();
+ } else if (dexValue instanceof DexValueDouble) {
+ return ((DexValueDouble) dexValue).getValue();
+ } else if (dexValue instanceof DexValueString) {
+ return ((DexValueString) dexValue).getValue();
+ } else if (dexValue instanceof DexValueType) {
+ return Type.getType(((DexValueType) dexValue).value.toDescriptorString());
+ } else if (dexValue instanceof DexValueMethodType) {
+ return Type.getMethodType(((DexValueMethodType) dexValue).value.toDescriptorString());
+ } else if (dexValue instanceof DexValueMethodHandle) {
+ return ((DexValueMethodHandle) dexValue).value.toAsmHandle();
+ } else {
+ throw new Unreachable(
+ "Unsupported bootstrap argument of type " + dexValue.getClass().getSimpleName());
+ }
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ public DexCallSite getCallSite() {
+ return callSite;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 12ea053..ae8251d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
public class DexMethodHandle extends IndexedDexItem implements
PresortedComparable<DexMethodHandle> {
@@ -244,4 +247,56 @@
public int compareTo(DexMethodHandle other) {
return sortedCompareTo(other.getSortedIndex());
}
+
+ public Handle toAsmHandle() {
+ String owner;
+ String name;
+ String desc;
+ boolean itf;
+ if (isMethodHandle()) {
+ DexMethod method = asMethod();
+ owner = method.holder.getInternalName();
+ name = method.name.toString();
+ desc = method.proto.toDescriptorString();
+ if (method.holder.toDescriptorString().equals("Ljava/lang/invoke/LambdaMetafactory;")) {
+ itf = false;
+ } else {
+ itf = method.holder.isInterface();
+ }
+ } else {
+ assert isFieldHandle();
+ DexField field = asField();
+ owner = field.clazz.getInternalName();
+ name = field.name.toString();
+ desc = field.type.toDescriptorString();
+ itf = field.clazz.isInterface();
+ }
+ return new Handle(getAsmTag(), owner, name, desc, itf);
+ }
+
+ private int getAsmTag() {
+ switch (type) {
+ case INVOKE_STATIC:
+ return Opcodes.H_INVOKESTATIC;
+ case INVOKE_CONSTRUCTOR:
+ return Opcodes.H_NEWINVOKESPECIAL;
+ case INVOKE_INSTANCE:
+ return Opcodes.H_INVOKEVIRTUAL;
+ case INVOKE_SUPER:
+ case INVOKE_DIRECT:
+ return Opcodes.H_INVOKESPECIAL;
+ case STATIC_GET:
+ return Opcodes.H_GETSTATIC;
+ case STATIC_PUT:
+ return Opcodes.H_PUTSTATIC;
+ case INSTANCE_GET:
+ return Opcodes.H_GETFIELD;
+ case INSTANCE_PUT:
+ return Opcodes.H_PUTFIELD;
+ case INVOKE_INTERFACE:
+ return Opcodes.H_INVOKEINTERFACE;
+ default:
+ throw new Unreachable();
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 5feecec..7ad397a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -3,10 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.cf.code.CfConstMethodHandle;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.function.Function;
@@ -35,6 +40,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfConstMethodHandle(methodHandle));
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asConstMethodHandle().methodHandle == methodHandle;
}
@@ -85,4 +95,14 @@
AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodHandleType, false);
}
+
+ @Override
+ public DexType computeVerificationType(TypeVerificationHelper helper) {
+ return helper.getFactory().methodHandleType;
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ helper.storeOutValue(this, it);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index b687743..01bfeb6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -3,10 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.cf.code.CfConstMethodType;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.function.Function;
@@ -35,6 +40,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfConstMethodType(methodType));
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.asConstMethodType().methodType == methodType;
}
@@ -85,4 +95,14 @@
AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
return TypeLatticeElement.fromDexType(appInfo.dexItemFactory.methodTypeType, false);
}
+
+ @Override
+ public DexType computeVerificationType(TypeVerificationHelper helper) {
+ return helper.getFactory().methodTypeType;
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ helper.storeOutValue(this, it);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index d8e78e5..747f912 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -6,12 +6,14 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
public class DebugLocalRead extends Instruction {
+ private static final String ERROR_MESSAGE = "Unexpected attempt to emit debug-local read.";
public DebugLocalRead() {
super(null);
@@ -29,7 +31,12 @@
@Override
public void buildDex(DexBuilder builder) {
- throw new Unreachable("Unexpected attempt to emit debug-local read.");
+ throw new Unreachable(ERROR_MESSAGE);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ throw new Unreachable(ERROR_MESSAGE);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index c47dc54..68253c6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -138,13 +138,9 @@
return outValue.outType();
}
- public void buildDex(DexBuilder builder) {
- throw new Unreachable("Unexpected instruction when converting to DEX: " + getInstructionName());
- }
+ public abstract void buildDex(DexBuilder builder);
- public void buildCf(CfBuilder builder) {
- throw new Unimplemented("No support for building CF instructions for: " + getInstructionName());
- }
+ public abstract void buildCf(CfBuilder builder);
public void replaceValue(Value oldValue, Value newValue) {
for (int i = 0; i < inValues.size(); i++) {
@@ -1021,12 +1017,11 @@
public abstract Constraint inliningConstraint(AppInfoWithLiveness info,
DexType invocationContext);
- public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
- throw new Unimplemented("Implement load/store insertion for: " + getInstructionName());
- }
+ public abstract void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper);
public DexType computeVerificationType(TypeVerificationHelper helper) {
- throw new Unimplemented("Implement verification type computation for: " + getInstructionName());
+ assert outValue == null || !outValue.type.isObject();
+ throw new Unreachable("Instruction without object outValue cannot compute verification type");
}
public boolean hasInvariantVerificationType() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 38d83ed..0579362 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -3,9 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.code.InvokeCustomRange;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -70,6 +74,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfInvokeDynamic(getCallSite()));
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isInvokeCustom() && callSite == other.asInvokeCustom().callSite;
}
@@ -95,4 +104,30 @@
public Constraint inliningConstraint(AppInfoWithLiveness info, DexType invocationContext) {
return Constraint.NEVER;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ // Essentially the same as InvokeMethod but with call site's method proto
+ // instead of a static called method.
+ helper.loadInValues(this, it);
+ if (getCallSite().methodProto.returnType.isVoidType()) {
+ return;
+ }
+ if (outValue == null) {
+ helper.popOutType(getCallSite().methodProto.returnType, this, it);
+ } else {
+ assert outValue.isUsed();
+ helper.storeOutValue(this, it);
+ }
+ }
+
+ @Override
+ public boolean hasInvariantVerificationType() {
+ return true;
+ }
+
+ @Override
+ public DexType computeVerificationType(TypeVerificationHelper helper) {
+ return getCallSite().methodProto.returnType;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 17ebfb5..1a567ef 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -170,11 +170,11 @@
@Override
public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
helper.loadInValues(this, it);
- if (method.proto.returnType.isVoidType()) {
+ if (getReturnType().isVoidType()) {
return;
}
if (outValue == null) {
- helper.popOutType(method.proto.returnType, this, it);
+ helper.popOutType(getReturnType(), this, it);
} else {
assert outValue.isUsed();
helper.storeOutValue(this, it);
@@ -188,7 +188,7 @@
@Override
public DexType computeVerificationType(TypeVerificationHelper helper) {
- return getInvokedMethod().proto.returnType;
+ return getReturnType();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index c0e1a30..718d23c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -3,19 +3,25 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokePolymorphicRange;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.conversion.JarSourceCode;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import java.util.Collection;
import java.util.List;
+import org.objectweb.asm.Opcodes;
public class InvokePolymorphic extends InvokeMethod {
@@ -78,6 +84,21 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ DexMethod dexMethod = getInvokedMethod();
+ DexItemFactory factory = builder.getFactory();
+
+ if (dexMethod.holder.getInternalName().equals(JarSourceCode.INTERNAL_NAME_METHOD_HANDLE)) {
+ DexMethod method = factory.createMethod(dexMethod.holder, getProto(), dexMethod.name);
+ builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method));
+ } else {
+ assert dexMethod.holder.getInternalName().equals(JarSourceCode.INTERNAL_NAME_VAR_HANDLE);
+ // VarHandle is new in Java 9
+ throw new Unimplemented();
+ }
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokePolymorphic()) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index d3ef914..9e9a096 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeSuperRange;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -10,12 +11,14 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeEnvironment;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import org.objectweb.asm.Opcodes;
public class InvokeSuper extends InvokeMethodWithReceiver {
@@ -72,6 +75,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfInvoke(Opcodes.INVOKESPECIAL, getInvokedMethod()));
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isInvokeSuper()) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index 91526dd..e194d32 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -55,6 +56,11 @@
}
@Override
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable("This classfile-specific IR should not be inserted in the Dex backend.");
+ }
+
+ @Override
public void buildCf(CfBuilder builder) {
Value value = inValues.get(0);
builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)));
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index 48fa364..9ee9684 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -4,16 +4,21 @@
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import java.util.function.Function;
public class Move extends Instruction {
+ private static final String ERROR_MESSAGE =
+ "This DEX-specific instruction should not be seen in the CF backend";
public Move(Value dest, Value src) {
super(dest, src);
@@ -36,6 +41,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ throw new Unreachable(ERROR_MESSAGE);
+ }
+
+ @Override
public int maxInValueRegister() {
return Constants.U16BIT_MAX;
}
@@ -93,4 +103,9 @@
AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
return getLatticeElement.apply(src());
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ throw new Unreachable(ERROR_MESSAGE);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index 7a7566d..799c528 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.code.FillArrayData;
import com.android.tools.r8.code.FillArrayDataPayload;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -14,6 +17,8 @@
import java.util.Arrays;
public class NewArrayFilledData extends Instruction {
+ private static final String ERROR_MESSAGE =
+ "Conversion from DEX to classfile not supported for NewArrayFilledData";
public final int element_width;
public final long size;
@@ -42,6 +47,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ throw new Unreachable(ERROR_MESSAGE);
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
NewArrayFilledData o = other.asNewArrayFilledData();
return o.element_width == element_width
@@ -102,4 +112,9 @@
public Constraint inliningConstraint(AppInfoWithLiveness info, DexType invocationContext) {
return Constraint.ALWAYS;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ throw new Unreachable(ERROR_MESSAGE);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NonNull.java b/src/main/java/com/android/tools/r8/ir/code/NonNull.java
index 51ef234..8da9903 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NonNull.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NonNull.java
@@ -3,10 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -52,6 +54,11 @@
}
@Override
+ public void buildCf(CfBuilder builder) {
+ throw new Unreachable(ERROR_MESSAGE);
+ }
+
+ @Override
public int maxInValueRegister() {
throw new Unreachable(ERROR_MESSAGE);
}
@@ -92,4 +99,9 @@
}
return l;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ throw new Unreachable(ERROR_MESSAGE);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index c991ffc..d2e47fb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -3,10 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.code.CfPop;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -53,11 +55,21 @@
}
@Override
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable("This classfile-specific IR should not be inserted in the Dex backend.");
+ }
+
+ @Override
public void buildCf(CfBuilder builder) {
builder.add(new CfPop(inValues.get(0).type));
}
@Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ throw new Unreachable("This IR must not be inserted before load and store insertion.");
+ }
+
+ @Override
public boolean canBeDeadCode(IRCode code, InternalOptions options) {
// Pop cannot be dead code as it modifies the stack height.
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 0963b03..82c0eeb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -57,6 +58,11 @@
}
@Override
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable("This classfile-specific IR should not be inserted in the Dex backend.");
+ }
+
+ @Override
public void buildCf(CfBuilder builder) {
builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)));
}
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 83995bd..60f9c79 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
@@ -102,6 +102,10 @@
this.factory = factory;
}
+ public DexItemFactory getFactory() {
+ return factory;
+ }
+
public Code build(CodeRewriter rewriter, InternalOptions options, AppInfoWithSubtyping appInfo) {
try {
types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
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 894ba4f..26a94d2 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
@@ -141,8 +141,8 @@
"([Ljava/lang/Object;)Z";
// Various internal names.
- static final String INTERNAL_NAME_METHOD_HANDLE = "java/lang/invoke/MethodHandle";
- static final String INTERNAL_NAME_VAR_HANDLE = "java/lang/invoke/VarHandle";
+ public static final String INTERNAL_NAME_METHOD_HANDLE = "java/lang/invoke/MethodHandle";
+ public static final String INTERNAL_NAME_VAR_HANDLE = "java/lang/invoke/VarHandle";
// Language types.
static final Type CLASS_TYPE = Type.getObjectType("java/lang/Class");
@@ -2601,6 +2601,8 @@
default:
throw new Unreachable();
}
+ } else {
+ throw new Unreachable();
}
callSiteProto = application.getProto(insn.desc);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 22a4812..5cb4ce8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -44,58 +44,54 @@
private final Reporter reporter;
- private static final List<String> IGNORED_SINGLE_ARG_OPTIONS = ImmutableList
- .of("protomapping",
- "target"
- );
- private static final List<String> IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS = ImmutableList
- .of("keepdirectories",
- "runtype",
- "laststageoutput"
- );
- private static final List<String> IGNORED_FLAG_OPTIONS = ImmutableList
- .of("forceprocessing",
- "dontusemixedcaseclassnames",
- "dontpreverify",
- "experimentalshrinkunusedprotofields",
- "filterlibraryjarswithorginalprogramjars",
- "dontskipnonpubliclibraryclasses",
- "dontskipnonpubliclibraryclassmembers",
- "invokebasemethod",
- "android"
- );
- private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS = ImmutableList
- .of("isclassnamestring",
- "whyarenotsimple"
- );
+ private static final List<String> IGNORED_SINGLE_ARG_OPTIONS = ImmutableList.of(
+ "dontnote",
+ "protomapping",
+ "target");
- private static final List<String> WARNED_SINGLE_ARG_OPTIONS = ImmutableList
- .of("dontnote",
- "printconfiguration",
- // TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
- // should be reported as errors, not just as warnings!
- "outjars",
- "adaptresourcefilecontents"
- );
- private static final List<String> WARNED_FLAG_OPTIONS = ImmutableList
- .of(
- // TODO(b/73707846): add support -addconfigurationdebugging
- "addconfigurationdebugging"
- );
- private static final List<String> WARNED_CLASS_DESCRIPTOR_OPTIONS = ImmutableList
- .of(
- // TODO(b/73708157): add support -assumenoexternalsideeffects <class_spec>
- "assumenoexternalsideeffects",
- // TODO(b/73707404): add support -assumenoescapingparameters <class_spec>
- "assumenoescapingparameters",
- // TODO(b/73708085): add support -assumenoexternalreturnvalues <class_spec>
- "assumenoexternalreturnvalues"
- );
+ private static final List<String> IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS = ImmutableList.of(
+ "keepdirectories",
+ "runtype",
+ "laststageoutput");
+
+ private static final List<String> IGNORED_FLAG_OPTIONS = ImmutableList.of(
+ "forceprocessing",
+ "dontusemixedcaseclassnames",
+ "dontpreverify",
+ "experimentalshrinkunusedprotofields",
+ "filterlibraryjarswithorginalprogramjars",
+ "dontskipnonpubliclibraryclasses",
+ "dontskipnonpubliclibraryclassmembers",
+ "invokebasemethod",
+ "android");
+
+ private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS = ImmutableList.of(
+ "isclassnamestring",
+ "whyarenotsimple");
+
+ private static final List<String> WARNED_SINGLE_ARG_OPTIONS = ImmutableList.of(
+ "printconfiguration",
+ // TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
+ // should be reported as errors, not just as warnings!
+ "outjars",
+ "adaptresourcefilecontents");
+
+ private static final List<String> WARNED_FLAG_OPTIONS = ImmutableList.of(
+ // TODO(b/73707846): add support -addconfigurationdebugging
+ "addconfigurationdebugging");
+
+ private static final List<String> WARNED_CLASS_DESCRIPTOR_OPTIONS = ImmutableList.of(
+ // TODO(b/73708157): add support -assumenoexternalsideeffects <class_spec>
+ "assumenoexternalsideeffects",
+ // TODO(b/73707404): add support -assumenoescapingparameters <class_spec>
+ "assumenoescapingparameters",
+ // TODO(b/73708085): add support -assumenoexternalreturnvalues <class_spec>
+ "assumenoexternalreturnvalues");
// Those options are unsupported and are treated as compilation errors.
// Just ignoring them would produce outputs incompatible with user expectations.
- private static final List<String> UNSUPPORTED_FLAG_OPTIONS = ImmutableList
- .of("skipnonpubliclibraryclasses");
+ private static final List<String> UNSUPPORTED_FLAG_OPTIONS =
+ ImmutableList.of("skipnonpubliclibraryclasses");
public ProguardConfigurationParser(
DexItemFactory dexItemFactory, Reporter reporter) {
@@ -197,28 +193,10 @@
}
TextPosition optionStart = getPosition();
expectChar('-');
- String option;
- if (Iterables.any(IGNORED_SINGLE_ARG_OPTIONS, this::skipOptionWithSingleArg)
- || Iterables.any(
- IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS, this::skipOptionWithOptionalSingleArg)
- || Iterables.any(IGNORED_FLAG_OPTIONS, this::skipFlag)
- || Iterables.any(IGNORED_CLASS_DESCRIPTOR_OPTIONS, this::skipOptionWithClassSpec)
- || parseOptimizationOption()) {
+ if (parseIgnoredOption() ||
+ parseIgnoredOptionAndWarn(optionStart) ||
+ parseUnsupportedOptionAndErr(optionStart)) {
// Intentionally left empty.
- } else if (
- (option = Iterables.find(WARNED_SINGLE_ARG_OPTIONS,
- this::skipOptionWithSingleArg, null)) != null
- || (option = Iterables.find(WARNED_FLAG_OPTIONS,
- this::skipFlag, null)) != null
- || (option = Iterables.find(WARNED_CLASS_DESCRIPTOR_OPTIONS,
- this::skipOptionWithClassSpec, null)) != null) {
- warnIgnoringOptions(option, optionStart);
- } else if (
- (option = Iterables.find(UNSUPPORTED_FLAG_OPTIONS, this::skipFlag, null)) != null) {
- reporter.error(new StringDiagnostic(
- "Unsupported option: -" + option,
- origin,
- getPosition(optionStart)));
} else if (acceptString("renamesourcefileattribute")) {
skipWhitespace();
if (isOptionalArgumentGiven()) {
@@ -368,6 +346,41 @@
return true;
}
+ private boolean parseUnsupportedOptionAndErr(TextPosition optionStart) {
+ String option = Iterables.find(UNSUPPORTED_FLAG_OPTIONS, this::skipFlag, null);
+ if (option != null) {
+ reporter.error(new StringDiagnostic(
+ "Unsupported option: -" + option, origin, getPosition(optionStart)));
+ return true;
+ }
+ return false;
+ }
+
+ private boolean parseIgnoredOptionAndWarn(TextPosition optionStart) {
+ String option =
+ Iterables.find(WARNED_CLASS_DESCRIPTOR_OPTIONS, this::skipOptionWithClassSpec, null);
+ if (option == null) {
+ option = Iterables.find(WARNED_FLAG_OPTIONS, this::skipFlag, null);
+ if (option == null) {
+ option = Iterables.find(WARNED_SINGLE_ARG_OPTIONS, this::skipOptionWithSingleArg, null);
+ if (option == null) {
+ return false;
+ }
+ }
+ }
+ warnIgnoringOptions(option, optionStart);
+ return true;
+ }
+
+ private boolean parseIgnoredOption() {
+ return Iterables.any(IGNORED_SINGLE_ARG_OPTIONS, this::skipOptionWithSingleArg)
+ || Iterables.any(IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS,
+ this::skipOptionWithOptionalSingleArg)
+ || Iterables.any(IGNORED_FLAG_OPTIONS, this::skipFlag)
+ || Iterables.any(IGNORED_CLASS_DESCRIPTOR_OPTIONS, this::skipOptionWithClassSpec)
+ || parseOptimizationOption();
+ }
+
private void parseInclude() throws ProguardRuleParserException {
TextPosition start = getPosition();
Path included = parseFileName();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index a88041e..b8b7780 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -36,7 +36,7 @@
public static ProguardKeepRule buildFieldKeepRule(DexClass clazz, DexEncodedField field) {
assert clazz.type == field.field.getHolder();
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
- builder.setType(ProguardKeepRuleType.KEEP);
+ builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
builder.getModifiersBuilder().setAllowsObfuscation(true);
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.getClassAccessFlags().setPublic();
@@ -59,7 +59,7 @@
public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
assert clazz.type == method.method.getHolder();
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
- builder.setType(ProguardKeepRuleType.KEEP);
+ builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
builder.getModifiersBuilder().setAllowsObfuscation(true);
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.getClassAccessFlags().setPublic();
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index e46bef9..5b96201 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -18,11 +18,19 @@
*
* <p>Lines with a # prefix are ignored.
*
- * <p>We will do most specific matching, i.e., com.google.foobar.*:feature2 com.google.*:base will
- * put everything in the com.google namespace into base, except classes in com.google.foobar that
- * will go to feature2. Class based mappings takes precedence over packages (since they are more
- * specific): com.google.A:feature2 com.google.*:base Puts A into feature2, and all other classes
- * from com.google into base.
+ * <p>We will do most specific matching, i.e.,
+ * <pre>
+ * com.google.foobar.*:feature2
+ * com.google.*:base
+ * </pre>
+ * will put everything in the com.google namespace into base, except classes in com.google.foobar
+ * that will go to feature2. Class based mappings takes precedence over packages (since they are
+ * more specific):
+ * <pre>
+ * com.google.A:feature2
+ * com.google.*:base
+ * </pre>
+ * Puts A into feature2, and all other classes from com.google into base.
*
* <p>Note that this format does not allow specifying inter-module dependencies, this is simply a
* placement tool.
@@ -32,10 +40,12 @@
HashMap<String, String> parsedRules = new HashMap<>(); // Already parsed rules.
HashSet<FeaturePredicate> mappings = new HashSet<>();
+
Path mappingFile;
static final String COMMENT = "#";
static final String SEPARATOR = ":";
+ static final String BASE_NAME = "base";
public static FeatureClassMapping fromSpecification(Path file)
throws FeatureMappingException, IOException {
@@ -69,7 +79,7 @@
private FeatureClassMapping() {}
- public void addMapping(String clazz, String feature) throws FeatureMappingException {
+ private void addMapping(String clazz, String feature) throws FeatureMappingException {
addRule(clazz, feature, 0);
}
@@ -91,7 +101,7 @@
}
}
if (bestMatch == null) {
- throw new FeatureMappingException("Class: " + clazz + " is not mapped to any feature");
+ return BASE_NAME;
}
return bestMatch.feature;
}
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index 48a04cd..8ce1c09 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -38,6 +38,7 @@
errorCount++;
}
}
+
public void error(String message) {
error(new StringDiagnostic(message));
}
diff --git a/src/test/examples/dexsplitsample/Class1.java b/src/test/examplesAndroidN/dexsplitsample/Class1.java
similarity index 100%
rename from src/test/examples/dexsplitsample/Class1.java
rename to src/test/examplesAndroidN/dexsplitsample/Class1.java
diff --git a/src/test/examples/dexsplitsample/Class2.java b/src/test/examplesAndroidN/dexsplitsample/Class2.java
similarity index 100%
rename from src/test/examples/dexsplitsample/Class2.java
rename to src/test/examplesAndroidN/dexsplitsample/Class2.java
diff --git a/src/test/examples/dexsplitsample/Class3.java b/src/test/examplesAndroidN/dexsplitsample/Class3.java
similarity index 100%
rename from src/test/examples/dexsplitsample/Class3.java
rename to src/test/examplesAndroidN/dexsplitsample/Class3.java
diff --git a/src/test/examplesAndroidN/dexsplitsample/Class4.java b/src/test/examplesAndroidN/dexsplitsample/Class4.java
new file mode 100644
index 0000000..5b1607f
--- /dev/null
+++ b/src/test/examplesAndroidN/dexsplitsample/Class4.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2018, 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 dexsplitsample;
+
+public class Class4 {
+ public static void main(String[] args) {
+ new Class4().createLambda();
+ System.out.println("Class4");
+ }
+
+ private void useLambda(LambdaInterface toInvokeOn) {
+ toInvokeOn.foo(42);
+ }
+
+ private void createLambda() {
+ useLambda((a) -> { return a + 2;});
+ }
+
+ interface LambdaInterface {
+ int foo(int a);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2202332..f63f4f2 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -443,7 +443,12 @@
private static final String ANGLER_BOOT_IMAGE = ANGLER_DIR + "/system/framework/boot.art";
public static byte[] getClassAsBytes(Class clazz) throws IOException {
- return ByteStreams.toByteArray(clazz.getResourceAsStream(clazz.getSimpleName() + ".class"));
+ String s = clazz.getSimpleName() + ".class";
+ Class outer = clazz.getEnclosingClass();
+ if (outer != null) {
+ s = outer.getSimpleName() + '$' + s;
+ }
+ return ByteStreams.toByteArray(clazz.getResourceAsStream(s));
}
public static String getArtDir(DexVm version) {
@@ -883,13 +888,15 @@
return runJavaNoVerify(path, main);
}
- public static ProcessResult runJava(Path classpath, String mainClass) throws IOException {
- return runJava(ImmutableList.of(classpath), mainClass);
+ public static ProcessResult runJava(Path classpath, String... args) throws IOException {
+ return runJava(ImmutableList.of(classpath), args);
}
- public static ProcessResult runJava(List<Path> classpath, String mainClass) throws IOException {
+ public static ProcessResult runJava(List<Path> classpath, String... args) throws IOException {
String cp = classpath.stream().map(Path::toString).collect(Collectors.joining(PATH_SEPARATOR));
- ProcessBuilder builder = new ProcessBuilder(getJavaExecutable(), "-cp", cp, mainClass);
+ List<String> cmdline = new ArrayList<String>(Arrays.asList(getJavaExecutable(), "-cp", cp));
+ cmdline.addAll(Arrays.asList(args));
+ ProcessBuilder builder = new ProcessBuilder(cmdline);
return runProcess(builder);
}
@@ -958,6 +965,11 @@
return runArtRaw(Collections.singletonList(file), mainClass, null);
}
+ public static ProcessResult runArtRaw(
+ String file, String mainClass, Consumer<ArtCommandBuilder> extras) throws IOException {
+ return runArtRaw(Collections.singletonList(file), mainClass, extras);
+ }
+
public static ProcessResult runArtRaw(List<String> files, String mainClass,
Consumer<ArtCommandBuilder> extras)
throws IOException {
diff --git a/src/test/java/com/android/tools/r8/cf/LambdaTest.java b/src/test/java/com/android/tools/r8/cf/LambdaTest.java
new file mode 100644
index 0000000..983f1b7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/LambdaTest.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, 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.cf;
+
+import java.util.function.Function;
+
+public class LambdaTest {
+ public static void main(String[] args) {
+ twicePrint(args.length, i -> i + 21);
+ }
+
+ private static void twicePrint(int v, Function<Integer, Integer> f) {
+ System.out.println(f.andThen(f).apply(v));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java b/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
new file mode 100644
index 0000000..21d0292
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2018, 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.cf;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.JarCode;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.DexInspector;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public class LambdaTestRunner {
+
+ private static final Class<?> CLASS = LambdaTest.class;
+ private static final String METHOD = "main";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void test() throws Exception {
+ // Test that the InvokeDynamic instruction in LambdaTest.main()
+ // is not modified by the R8 compilation.
+ // First, extract the InvokeDynamic instruction from the input class.
+ byte[] inputClass = ToolHelper.getClassAsBytes(CLASS);
+ int opcode = Opcodes.INVOKEDYNAMIC;
+ InvokeDynamicInsnNode insnInput = findFirstInMethod(inputClass, opcode);
+ // Compile with R8 and extract the InvokeDynamic instruction from the output class.
+ AndroidAppConsumers appBuilder = new AndroidAppConsumers();
+ Path outPath = temp.getRoot().toPath().resolve("out.jar");
+ R8.run(
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+ .setProgramConsumer(appBuilder.wrapClassFileConsumer(new ArchiveConsumer(outPath)))
+ .addClassProgramData(inputClass, Origin.unknown())
+ .build());
+ AndroidApp app = appBuilder.build();
+ InvokeDynamicInsnNode insnOutput = findFirstInMethod(app, opcode);
+ // Check that the InvokeDynamic instruction is not modified.
+ assertEquals(insnInput.name, insnOutput.name);
+ assertEquals(insnInput.desc, insnOutput.desc);
+ assertEquals(insnInput.bsm, insnOutput.bsm);
+ assertArrayEquals(insnInput.bsmArgs, insnOutput.bsmArgs);
+ // Check that execution gives the same output.
+ ProcessResult inputResult =
+ ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getName());
+ ProcessResult outputResult = ToolHelper.runJava(outPath, CLASS.getName());
+ assertEquals(inputResult.toString(), outputResult.toString());
+ }
+
+ private InvokeDynamicInsnNode findFirstInMethod(AndroidApp app, int opcode) throws Exception {
+ String returnType = "void";
+ DexInspector inspector = new DexInspector(app);
+ List<String> args = Collections.singletonList(String[].class.getTypeName());
+ DexEncodedMethod method = inspector.clazz(CLASS).method(returnType, METHOD, args).getMethod();
+ JarCode jarCode = method.getCode().asJarCode();
+ MethodNode outputMethod = jarCode.getNode();
+ return (InvokeDynamicInsnNode) findFirstInstruction(outputMethod, opcode);
+ }
+
+ private InvokeDynamicInsnNode findFirstInMethod(byte[] clazz, int opcode) {
+ MethodNode[] method = {null};
+ new ClassReader(clazz)
+ .accept(
+ new ClassNode(Opcodes.ASM6) {
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ if (name.equals(METHOD)) {
+ method[0] = new MethodNode(access, name, desc, signature, exceptions);
+ return method[0];
+ } else {
+ return null;
+ }
+ }
+ },
+ 0);
+ return (InvokeDynamicInsnNode) findFirstInstruction(method[0], opcode);
+ }
+
+ private AbstractInsnNode findFirstInstruction(MethodNode node, int opcode) {
+ assert node != null;
+ InsnList asmInsns = node.instructions;
+ for (ListIterator<AbstractInsnNode> it = asmInsns.iterator(); it.hasNext(); ) {
+ AbstractInsnNode insn = it.next();
+ if (insn.getOpcode() == opcode) {
+ return insn;
+ }
+ }
+ throw new RuntimeException("Instruction not found");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java b/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java
new file mode 100644
index 0000000..c80047a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java
@@ -0,0 +1,189 @@
+// Copyright (c) 2018, 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.cf;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import org.objectweb.asm.*;
+
+// The method MethodHandleDump.transform() translates methods in MethodHandleTest that look like
+// MethodType.methodType(TYPES)
+// and
+// MethodHandles.lookup().findKIND(FOO.class, "METHOD", TYPE)
+// into LDC instructions.
+// This is necessary since there is no Java syntax that compiles to
+// LDC of a constant method handle or constant method type.
+//
+// The method dumpD() dumps a class equivalent to MethodHandleTest.D
+// that uses an LDC instruction instead of MethodHandles.lookup().findSpecial().
+// The LDC instruction loads an InvokeSpecial constant method handle to a C method,
+// so this LDC instruction must be in a subclass of C, and not directly on MethodHandleTest.
+public class MethodHandleDump implements Opcodes {
+
+ private static final Type viType = Type.getMethodType(Type.VOID_TYPE, Type.INT_TYPE);
+ private static final Type jiType = Type.getMethodType(Type.LONG_TYPE, Type.INT_TYPE);
+ private static final Type vicType =
+ Type.getMethodType(Type.VOID_TYPE, Type.INT_TYPE, Type.CHAR_TYPE);
+ private static final Type jicType =
+ Type.getMethodType(Type.LONG_TYPE, Type.INT_TYPE, Type.CHAR_TYPE);
+ private static final String cDesc = "com/android/tools/r8/cf/MethodHandleTest$C";
+ private static final String iDesc = "com/android/tools/r8/cf/MethodHandleTest$I";
+ private static final String viDesc = viType.getDescriptor();
+ private static final String jiDesc = jiType.getDescriptor();
+ private static final String vicDesc = vicType.getDescriptor();
+ private static final String jicDesc = jicType.getDescriptor();
+ private static final String intDesc = Type.INT_TYPE.getDescriptor();
+
+ public static byte[] transform(byte[] input) throws Exception {
+ ImmutableMap.Builder<String, Type> typesBuilder = ImmutableMap.builder();
+ ImmutableMap<String, Type> types =
+ typesBuilder
+ .put("viType", viType)
+ .put("jiType", jiType)
+ .put("vicType", vicType)
+ .put("jicType", jicType)
+ .build();
+
+ Builder<String, Handle> methodsBuilder = ImmutableMap.builder();
+ methodsBuilder
+ .put("scviMethod", new Handle(H_INVOKESTATIC, cDesc, "svi", viDesc, false))
+ .put("scjiMethod", new Handle(H_INVOKESTATIC, cDesc, "sji", jiDesc, false))
+ .put("scvicMethod", new Handle(H_INVOKESTATIC, cDesc, "svic", vicDesc, false))
+ .put("scjicMethod", new Handle(H_INVOKESTATIC, cDesc, "sjic", jicDesc, false))
+ .put("vcviMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vvi", viDesc, false))
+ .put("vcjiMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vji", jiDesc, false))
+ .put("vcvicMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vvic", vicDesc, false))
+ .put("vcjicMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vjic", jicDesc, false))
+ .put("siviMethod", new Handle(H_INVOKESTATIC, iDesc, "svi", viDesc, true))
+ .put("sijiMethod", new Handle(H_INVOKESTATIC, iDesc, "sji", jiDesc, true))
+ .put("sivicMethod", new Handle(H_INVOKESTATIC, iDesc, "svic", vicDesc, true))
+ .put("sijicMethod", new Handle(H_INVOKESTATIC, iDesc, "sjic", jicDesc, true))
+ .put("diviMethod", new Handle(H_INVOKEINTERFACE, iDesc, "dvi", viDesc, true))
+ .put("dijiMethod", new Handle(H_INVOKEINTERFACE, iDesc, "dji", jiDesc, true))
+ .put("divicMethod", new Handle(H_INVOKEINTERFACE, iDesc, "dvic", vicDesc, true))
+ .put("dijicMethod", new Handle(H_INVOKEINTERFACE, iDesc, "djic", jicDesc, true))
+ .put("vciSetField", new Handle(H_PUTFIELD, cDesc, "vi", intDesc, false))
+ .put("sciSetField", new Handle(H_PUTSTATIC, cDesc, "si", intDesc, false))
+ .put("vciGetField", new Handle(H_GETFIELD, cDesc, "vi", intDesc, false))
+ .put("sciGetField", new Handle(H_GETSTATIC, cDesc, "si", intDesc, false))
+ .put("iiSetField", new Handle(H_PUTSTATIC, iDesc, "ii", intDesc, true))
+ .put("iiGetField", new Handle(H_GETSTATIC, iDesc, "ii", intDesc, true))
+ .put("constructorMethod", new Handle(H_NEWINVOKESPECIAL, cDesc, "<init>", viDesc, false));
+ ImmutableMap<String, Handle> methods = methodsBuilder.build();
+ ClassReader cr = new ClassReader(input);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(ASM6, cw) {
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ switch (desc) {
+ case "()Ljava/lang/invoke/MethodType;":
+ {
+ Type type = types.get(name);
+ assert type != null : name;
+ assert access == ACC_PUBLIC + ACC_STATIC;
+ assert signature == null;
+ assert exceptions == null;
+ MethodVisitor mv = cw.visitMethod(access, name, desc, null, null);
+ mv.visitCode();
+ mv.visitLdcInsn(type);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ return null;
+ }
+ case "()Ljava/lang/invoke/MethodHandle;":
+ {
+ Handle method = methods.get(name);
+ assert access == ACC_PUBLIC + ACC_STATIC;
+ assert method != null : name;
+ assert signature == null;
+ assert exceptions == null;
+ MethodVisitor mv = cw.visitMethod(access, name, desc, null, null);
+ mv.visitCode();
+ mv.visitLdcInsn(method);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ return null;
+ }
+ default:
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+ }
+ },
+ 0);
+ return cw.toByteArray();
+ }
+
+ public static byte[] dumpD() throws Exception {
+
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ cw.visit(
+ V1_8,
+ ACC_PUBLIC + ACC_SUPER,
+ "com/android/tools/r8/cf/MethodHandleTest$D",
+ null,
+ "com/android/tools/r8/cf/MethodHandleTest$C",
+ null);
+
+ cw.visitInnerClass(
+ "com/android/tools/r8/cf/MethodHandleTest$D",
+ "com/android/tools/r8/cf/MethodHandleTest",
+ "D",
+ ACC_PUBLIC + ACC_STATIC);
+
+ cw.visitInnerClass(
+ "com/android/tools/r8/cf/MethodHandleTest$C",
+ "com/android/tools/r8/cf/MethodHandleTest",
+ "C",
+ ACC_PUBLIC + ACC_STATIC);
+
+ cw.visitInnerClass(
+ "java/lang/invoke/MethodHandles$Lookup",
+ "java/lang/invoke/MethodHandles",
+ "Lookup",
+ ACC_PUBLIC + ACC_FINAL + ACC_STATIC);
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(
+ INVOKESPECIAL, "com/android/tools/r8/cf/MethodHandleTest$C", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ {
+ mv =
+ cw.visitMethod(
+ ACC_PUBLIC + ACC_STATIC,
+ "vcviSpecialMethod",
+ "()Ljava/lang/invoke/MethodHandle;",
+ null,
+ null);
+ mv.visitCode();
+ mv.visitLdcInsn(new Handle(H_INVOKESPECIAL, cDesc, "vvi", viDesc, false));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "vvi", "(I)V", null, null);
+ mv.visitCode();
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 2);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java
new file mode 100644
index 0000000..d42d443
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java
@@ -0,0 +1,385 @@
+// Copyright (c) 2018, 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.cf;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class MethodHandleTest {
+
+ public static class C {
+ public C(int i) {
+ System.out.println("C " + i);
+ }
+
+ public C() {
+ System.out.println("C");
+ }
+
+ public int vi;
+ public static int si;
+
+ public static void svi(int i) {
+ System.out.println("svi " + i);
+ }
+
+ public static long sji(int i) {
+ System.out.println("sji " + i);
+ return 42L;
+ }
+
+ public static void svic(int i, char c) {
+ System.out.println("svic " + i);
+ }
+
+ public static long sjic(int i, char c) {
+ System.out.println("sjic " + i);
+ return 42L;
+ }
+
+ public void vvi(int i) {
+ System.out.println("vvi " + i);
+ }
+
+ public long vji(int i) {
+ System.out.println("vji " + i);
+ return 42L;
+ }
+
+ public void vvic(int i, char c) {
+ System.out.println("vvic " + i);
+ }
+
+ public long vjic(int i, char c) {
+ System.out.println("vjic " + i);
+ return 42L;
+ }
+ }
+
+ public static class D extends C {
+ public static MethodHandle vcviSpecialMethod() {
+ try {
+ return MethodHandles.lookup().findSpecial(C.class, "vvi", viType(), D.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void vvi(int i) {
+ // Overridden to output nothing.
+ }
+ }
+
+ public interface I {
+ int ii = 42;
+
+ static void svi(int i) {
+ System.out.println("svi " + i);
+ }
+
+ static long sji(int i) {
+ System.out.println("sji " + i);
+ return 42L;
+ }
+
+ static void svic(int i, char c) {
+ System.out.println("svic " + i);
+ }
+
+ static long sjic(int i, char c) {
+ System.out.println("sjic " + i);
+ return 42L;
+ }
+
+ default void dvi(int i) {
+ System.out.println("dvi " + i);
+ }
+
+ default long dji(int i) {
+ System.out.println("dji " + i);
+ return 42L;
+ }
+
+ default void dvic(int i, char c) {
+ System.out.println("dvic " + i);
+ }
+
+ default long djic(int i, char c) {
+ System.out.println("djic " + i);
+ return 42L;
+ }
+ }
+
+ public static class Impl implements I {}
+
+ public static void main(String[] args) {
+ // When MethodHandleTestRunner invokes this program with the JVM, "fail" is passed as arg.
+ // When invoked with Art, no arg is passed since interface fields may be modified on Art.
+ String expectedResult = args[0];
+ C c = new C(42);
+ I i = new Impl();
+ try {
+ scviMethod().invoke(1);
+ assertEquals(42L, (long) scjiMethod().invoke(2));
+ scvicMethod().invoke(3, 'x');
+ assertEquals(42L, (long) scjicMethod().invoke(4, 'x'));
+ vcviMethod().invoke(c, 5);
+ assertEquals(42L, (long) vcjiMethod().invoke(c, 6));
+ vcvicMethod().invoke(c, 7, 'x');
+ assertEquals(42L, (long) vcjicMethod().invoke(c, 8, 'x'));
+ siviMethod().invoke(9);
+ assertEquals(42L, (long) sijiMethod().invoke(10));
+ sivicMethod().invoke(11, 'x');
+ assertEquals(42L, (long) sijicMethod().invoke(12, 'x'));
+ diviMethod().invoke(i, 13);
+ assertEquals(42L, (long) dijiMethod().invoke(i, 14));
+ divicMethod().invoke(i, 15, 'x');
+ assertEquals(42L, (long) dijicMethod().invoke(i, 16, 'x'));
+ vciSetField().invoke(c, 17);
+ assertEquals(17, (int) vciGetField().invoke(c));
+ sciSetField().invoke(18);
+ assertEquals(18, (int) sciGetField().invoke());
+ String interfaceSetResult;
+ try {
+ iiSetField().invoke(19);
+ interfaceSetResult = "pass";
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof IllegalAccessException) {
+ interfaceSetResult = "exception";
+ } else {
+ throw e;
+ }
+ } catch (IllegalAccessError e) {
+ interfaceSetResult = "error";
+ }
+ if (!interfaceSetResult.equals(expectedResult)) {
+ throw new RuntimeException(
+ "Wrong outcome of iiSetField().invoke(): Expected "
+ + expectedResult
+ + " but got "
+ + interfaceSetResult);
+ }
+ assertEquals(interfaceSetResult.equals("pass") ? 19 : 42, (int) iiGetField().invoke());
+ MethodHandle methodHandle = D.vcviSpecialMethod();
+ methodHandle.invoke(new D(), 20);
+ constructorMethod().invoke(21);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void assertEquals(long l, long x) {
+ if (l != x) {
+ throw new AssertionError("Not equal: " + l + " != " + x);
+ }
+ }
+
+ private static void assertEquals(int l, int x) {
+ if (l != x) {
+ throw new AssertionError("Not equal: " + l + " != " + x);
+ }
+ }
+
+ public static MethodType viType() {
+ return MethodType.methodType(void.class, int.class);
+ }
+
+ public static MethodType jiType() {
+ return MethodType.methodType(long.class, int.class);
+ }
+
+ public static MethodType vicType() {
+ return MethodType.methodType(void.class, int.class, char.class);
+ }
+
+ public static MethodType jicType() {
+ return MethodType.methodType(long.class, int.class, char.class);
+ }
+
+ public static MethodHandle scviMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(C.class, "svi", viType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle scjiMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(C.class, "sji", jiType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle scvicMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(C.class, "svic", vicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle scjicMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(C.class, "sjic", jicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle vcviMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(C.class, "vvi", viType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle vcjiMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(C.class, "vji", jiType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle vcvicMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(C.class, "vvic", vicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle vcjicMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(C.class, "vjic", jicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle siviMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(I.class, "svi", viType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle sijiMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(I.class, "sji", jiType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle sivicMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(I.class, "svic", vicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle sijicMethod() {
+ try {
+ return MethodHandles.lookup().findStatic(I.class, "sjic", jicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle diviMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(I.class, "dvi", viType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle dijiMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(I.class, "dji", jiType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle divicMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(I.class, "dvic", vicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle dijicMethod() {
+ try {
+ return MethodHandles.lookup().findVirtual(I.class, "djic", jicType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle vciSetField() {
+ try {
+ return MethodHandles.lookup().findSetter(C.class, "vi", int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle sciSetField() {
+ try {
+ return MethodHandles.lookup().findStaticSetter(C.class, "si", int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle vciGetField() {
+ try {
+ return MethodHandles.lookup().findGetter(C.class, "vi", int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle sciGetField() {
+ try {
+ return MethodHandles.lookup().findStaticGetter(C.class, "si", int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle iiSetField() {
+ try {
+ return MethodHandles.lookup().findStaticSetter(I.class, "ii", int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle iiGetField() {
+ try {
+ return MethodHandles.lookup().findStaticGetter(I.class, "ii", int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MethodHandle constructorMethod() {
+ try {
+ return MethodHandles.lookup().findConstructor(C.class, viType());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
new file mode 100644
index 0000000..ba25396
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2018, 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.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.nio.file.Path;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class MethodHandleTestRunner {
+ static final Class<?> CLASS = MethodHandleTest.class;
+
+ private boolean ldc = false;
+
+ @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void testMethodHandlesLookup() throws Exception {
+ // Run test with dynamic method lookups, i.e. using MethodHandles.lookup().find*()
+ ldc = false;
+ test();
+ }
+
+ @Test
+ public void testLdcMethodHandle() throws Exception {
+ // Run test with LDC methods, i.e. without java.lang.invoke.MethodHandles
+ ldc = true;
+ test();
+ }
+
+ private final Class[] inputClasses = {
+ MethodHandleTest.class,
+ MethodHandleTest.C.class,
+ MethodHandleTest.I.class,
+ MethodHandleTest.Impl.class,
+ MethodHandleTest.D.class,
+ };
+
+ private void test() throws Exception {
+ ProcessResult runInput = runInput();
+ Path outCf = temp.getRoot().toPath().resolve("cf.jar");
+ build(new ClassFileConsumer.ArchiveConsumer(outCf));
+ Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+ build(new DexIndexedConsumer.ArchiveConsumer(outDex));
+
+ ProcessResult runCf =
+ ToolHelper.runJava(outCf, CLASS.getCanonicalName(), ldc ? "error" : "exception");
+ assertEquals(runInput.toString(), runCf.toString());
+ // TODO(mathiasr): Once we include a P runtime, change this to "P and above".
+ if (ToolHelper.getDexVm() != DexVm.ART_DEFAULT) {
+ return;
+ }
+ ProcessResult runDex =
+ ToolHelper.runArtRaw(
+ outDex.toString(),
+ CLASS.getCanonicalName(),
+ cmd -> cmd.appendProgramArgument(ldc ? "pass" : "exception"));
+ // Only compare stdout and exitCode since dex2oat prints to stderr.
+ if (runInput.exitCode != runDex.exitCode) {
+ System.out.println(runDex.stderr);
+ }
+ assertEquals(runInput.stdout, runDex.stdout);
+ assertEquals(runInput.exitCode, runDex.exitCode);
+ }
+
+ private void build(ProgramConsumer programConsumer) throws Exception {
+ // MethodHandle.invoke() only supported from Android O
+ // ConstMethodHandle only supported from Android P
+ AndroidApiLevel apiLevel = AndroidApiLevel.P;
+ Builder cfBuilder =
+ R8Command.builder()
+ .setMinApiLevel(apiLevel.getLevel())
+ .setMode(CompilationMode.DEBUG)
+ .addLibraryFiles(ToolHelper.getAndroidJar(apiLevel))
+ .setProgramConsumer(programConsumer);
+ for (Class<?> c : inputClasses) {
+ byte[] classAsBytes = getClassAsBytes(c);
+ cfBuilder.addClassProgramData(classAsBytes, Origin.unknown());
+ }
+ R8.run(cfBuilder.build());
+ }
+
+ private ProcessResult runInput() throws Exception {
+ Path out = temp.getRoot().toPath().resolve("input.jar");
+ ClassFileConsumer.ArchiveConsumer archiveConsumer = new ClassFileConsumer.ArchiveConsumer(out);
+ for (Class<?> c : inputClasses) {
+ archiveConsumer.accept(
+ getClassAsBytes(c), DescriptorUtils.javaTypeToDescriptor(c.getName()), null);
+ }
+ archiveConsumer.finished(null);
+ ProcessResult runInput = ToolHelper.runJava(out, CLASS.getName(), ldc ? "error" : "exception");
+ if (runInput.exitCode != 0) {
+ System.out.println(runInput);
+ }
+ assertEquals(0, runInput.exitCode);
+ return runInput;
+ }
+
+ private byte[] getClassAsBytes(Class<?> clazz) throws Exception {
+ if (ldc) {
+ if (clazz == MethodHandleTest.D.class) {
+ return MethodHandleDump.dumpD();
+ } else if (clazz == MethodHandleTest.class) {
+ return MethodHandleDump.transform(ToolHelper.getClassAsBytes(clazz));
+ }
+ }
+ return ToolHelper.getClassAsBytes(clazz);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 43e9d03..19a5ee2 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
@@ -40,11 +41,15 @@
public class DexSplitterTests {
- private static final String CLASS_DIR = ToolHelper.EXAMPLES_BUILD_DIR + "classes/dexsplitsample";
+ private static final String CLASS_DIR =
+ ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR + "classes/dexsplitsample";
private static final String CLASS1_CLASS = CLASS_DIR + "/Class1.class";
private static final String CLASS2_CLASS = CLASS_DIR + "/Class2.class";
private static final String CLASS3_CLASS = CLASS_DIR + "/Class3.class";
private static final String CLASS3_INNER_CLASS = CLASS_DIR + "/Class3$InnerClass.class";
+ private static final String CLASS4_CLASS = CLASS_DIR + "/Class4.class";
+ private static final String CLASS4_LAMBDA_INTERFACE = CLASS_DIR + "/Class4$LambdaInterface.class";
+
@Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -75,6 +80,8 @@
.addProgramFiles(Paths.get(CLASS2_CLASS))
.addProgramFiles(Paths.get(CLASS3_CLASS))
.addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
.build());
Path output = temp.getRoot().toPath().resolve("output");
@@ -135,6 +142,18 @@
} catch (AssertionError assertionError) {
// We expect this to throw since base is not in the path and Class3 depends on Class1
}
+
+ className = "Class4";
+ builder = new ArtCommandBuilder();
+ builder.appendClasspath(feature.toString());
+ builder.setMainClass("dexsplitsample." + className);
+ try {
+ ToolHelper.runArt(builder);
+ assertFalse(true);
+ } catch (AssertionError assertionError) {
+ // We expect this to throw since base is not in the path and Class4 includes a lambda that
+ // would have been pushed to base.
+ }
}
private Path createSplitSpec() throws FileNotFoundException, UnsupportedEncodingException {
@@ -143,7 +162,8 @@
out.write(
"dexsplitsample.Class1:base\n"
+ "dexsplitsample.Class2:feature1\n"
- + "dexsplitsample.Class3:feature1");
+ + "dexsplitsample.Class3:feature1\n"
+ + "dexsplitsample.Class4:feature1");
}
return splitSpec;
}
@@ -159,11 +179,13 @@
public void splitFilesFromJar()
throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
CompilationException, ExecutionException {
- splitFromJars(true);
- splitFromJars(false);
+ splitFromJars(true, true);
+ splitFromJars(false, true);
+ splitFromJars(true, false);
+ splitFromJars(false, false);
}
- private void splitFromJars(boolean useOptions)
+ private void splitFromJars(boolean useOptions, boolean explicitBase)
throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
ExecutionException, CompilationException {
// Initial normal compile to create dex files.
@@ -175,6 +197,8 @@
.addProgramFiles(Paths.get(CLASS2_CLASS))
.addProgramFiles(Paths.get(CLASS3_CLASS))
.addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
.build());
Path output = temp.getRoot().toPath().resolve("output");
@@ -200,26 +224,38 @@
featureStream.putNextEntry(new ZipEntry(name));
featureStream.write(Files.readAllBytes(Paths.get(CLASS3_INNER_CLASS)));
featureStream.closeEntry();
+ name = "dexsplitsample/Class4";
+ featureStream.putNextEntry(new ZipEntry(name));
+ featureStream.write(Files.readAllBytes(Paths.get(CLASS4_CLASS)));
+ featureStream.closeEntry();
+ name = "dexsplitsample/Class4$LambdaInterface";
+ featureStream.putNextEntry(new ZipEntry(name));
+ featureStream.write(Files.readAllBytes(Paths.get(CLASS4_LAMBDA_INTERFACE)));
+ featureStream.closeEntry();
featureStream.close();
if (useOptions) {
Options options = new Options();
options.inputArchives.add(inputZip.toString());
options.splitBaseName = output.toString();
- options.featureJars.add(baseJar.toString());
+ if (explicitBase) {
+ options.featureJars.add(baseJar.toString());
+ }
options.featureJars.add(featureJar.toString());
DexSplitter.run(options);
} else {
- DexSplitter.main(
- new String[]{
- "--input",
- inputZip.toString(),
- "--output",
- output.toString(),
- "--feature-jar",
- baseJar.toString(),
- "--feature-jar",
- featureJar.toString()
- });
+ List<String> args = Lists.newArrayList(
+ "--input",
+ inputZip.toString(),
+ "--output",
+ output.toString(),
+ "--feature-jar",
+ featureJar.toString());
+ if (explicitBase) {
+ args.add("--feature-jar");
+ args.add(baseJar.toString());
+ }
+
+ DexSplitter.main(args.toArray(new String[0]));
}
Path base = output.getParent().resolve("output.base.zip");
Path feature = output.getParent().resolve("output.feature1.zip");
@@ -241,6 +277,8 @@
.addProgramFiles(Paths.get(CLASS2_CLASS))
.addProgramFiles(Paths.get(CLASS3_CLASS))
.addProgramFiles(Paths.get(CLASS3_INNER_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_CLASS))
+ .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
.addLibraryFiles(ToolHelper.getDefaultAndroidJar())
.setProguardMapOutputPath(proguardMap)
.addProguardConfiguration(getProguardConf(), null)
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 85f2eb5..1419b95 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -119,8 +119,8 @@
protected static FieldSubject checkFieldIsPresent(ClassSubject classSubject, String fieldType,
String fieldName) {
FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
- assertNotNull(fieldSubject);
- assertTrue(fieldSubject.isPresent());
+ assertTrue("No field " + fieldName + " in " + classSubject.getOriginalName(),
+ fieldSubject.isPresent());
return fieldSubject;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 60ed129..df04931 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -4,12 +4,15 @@
package com.android.tools.r8.kotlin;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.kotlin.TestKotlinClass.AccessorKind;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.utils.AndroidApp;
@@ -19,6 +22,7 @@
import java.nio.file.Path;
import java.util.Collections;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
public class R8KotlinAccessorTest extends AbstractR8KotlinTestBase {
@@ -39,24 +43,38 @@
.addProperty("publicProp", JAVA_LANG_STRING, Visibility.PUBLIC)
.addProperty("primitiveProp", "int", Visibility.PUBLIC);
+ private static final TestKotlinCompanionClass COMPANION_LATE_INIT_PROPERTY_CLASS =
+ new TestKotlinCompanionClass("properties.CompanionLateInitProperties")
+ .addProperty("privateLateInitProp", JAVA_LANG_STRING, Visibility.PRIVATE)
+ .addProperty("internalLateInitProp", JAVA_LANG_STRING, Visibility.INTERNAL)
+ .addProperty("publicLateInitProp", JAVA_LANG_STRING, Visibility.PUBLIC);
+
+ private static final TestKotlinClass PROPERTY_ACCESS_FOR_INNER_CLASS =
+ new TestKotlinClass("accessors.PropertyAccessorForInnerClass")
+ .addProperty("privateProp", JAVA_LANG_STRING, Visibility.PRIVATE)
+ .addProperty("privateLateInitProp", JAVA_LANG_STRING, Visibility.PRIVATE);
+
+ private static final TestKotlinClass PROPERTY_ACCESS_FOR_LAMBDA_CLASS =
+ new TestKotlinClass("accessors.PropertyAccessorForLambda")
+ .addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE)
+ .addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE);
+
@Test
public void testCompanionProperty_primitivePropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrimitiveProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "primitiveProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, "int", propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getPrimitiveProp$cp", "int",
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setPrimitiveProp$cp", "void",
- Collections.singletonList("int"));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -72,22 +90,20 @@
@Test
public void testCompanionProperty_privatePropertyIsAlwaysInlined() throws Exception {
- String mainClass = addMainToClasspath(
- "properties.CompanionPropertiesKt", "companionProperties_usePrivateProp");
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
+ "companionProperties_usePrivateProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "privateProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getPrivateProp$cp", JAVA_LANG_STRING,
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setPrivateProp$cp", "void",
- Collections.singletonList(JAVA_LANG_STRING));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -104,22 +120,20 @@
@Test
public void testCompanionProperty_internalPropertyIsAlwaysInlined() throws Exception {
- String mainClass = addMainToClasspath(
- "properties.CompanionPropertiesKt", "companionProperties_useInternalProp");
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
+ "companionProperties_useInternalProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "internalProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getInternalProp$cp", JAVA_LANG_STRING,
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setInternalProp$cp", "void",
- Collections.singletonList(JAVA_LANG_STRING));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -135,22 +149,20 @@
@Test
public void testCompanionProperty_publicPropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePublicProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "publicProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getPublicProp$cp", JAVA_LANG_STRING,
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setPublicProp$cp", "void",
- Collections.singletonList(JAVA_LANG_STRING));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -165,28 +177,98 @@
}
@Test
+ public void testCompanionLateInitProperty_privatePropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
+ "companionLateInitProperties_usePrivateLateInitProp");
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(outerClass, getterAccessor);
+ checkMethodIsAbsent(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(outerClass, getterAccessor);
+ checkMethodIsPresent(outerClass, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ public void testCompanionLateInitProperty_internalPropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
+ "companionLateInitProperties_useInternalLateInitProp");
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(outerClass, getterAccessor);
+ checkMethodIsAbsent(outerClass, setterAccessor);
+ });
+ }
+
+ @Test
+ public void testCompanionLateInitProperty_publicPropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
+ "companionLateInitProperties_usePublicLateInitProp");
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(outerClass, getterAccessor);
+ checkMethodIsAbsent(outerClass, setterAccessor);
+ });
+ }
+
+ @Test
public void testAccessor() throws Exception {
+ TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("accessors.AccessorKt",
- "accessor_accessCompanionPrivate");
+ "accessor_accessPropertyFromCompanionClass");
runTest("accessors", mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
- ClassSubject outerClass = checkClassExists(dexInspector,
- testedClass.getOuterClassName());
- ClassSubject companionClass = checkClassExists(dexInspector,
- testedClass.getClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
String propertyName = "property";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
// The getter is always inlined since it just calls into the accessor.
- MemberNaming.MethodSignature getter = testedClass
- .getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
checkMethodIsAbsent(companionClass, getter);
MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getProperty$cp", JAVA_LANG_STRING,
- Collections.emptyList());
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
checkMethodIsAbsent(outerClass, getterAccessor);
@@ -198,6 +280,169 @@
}
@Test
+ public void testAccessorFromPrivate() throws Exception {
+ TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("accessors.AccessorKt",
+ "accessor_accessPropertyFromOuterClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+ String propertyName = "property";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ // We cannot inline the getter because we don't know if NPE is preserved.
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ checkMethodIsPresent(companionClass, getter);
+
+ // We should always inline the static accessor method.
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ checkMethodIsAbsent(outerClass, getterAccessor);
+
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
+ }
+
+ @Test
+ public void testAccessorForInnerClassIsRemovedWhenNotUsed() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "noUseOfPropertyAccessorFromInnerClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+
+ for (String propertyName : testedClass.properties.keySet()) {
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testPrivatePropertyAccessorForInnerClassCanBeInlined() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "usePrivatePropertyAccessorFromInnerClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING,
+ propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(classSubject, getterAccessor);
+ checkMethodIsPresent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testPrivateLateInitPropertyAccessorForInnerClassCanBeInlined() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "usePrivateLateInitPropertyAccessorFromInnerClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING,
+ propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(classSubject, getterAccessor);
+ checkMethodIsPresent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testAccessorForLambdaIsRemovedWhenNotUsed() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_LAMBDA_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "noUseOfPropertyAccessorFromLambda");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+ String propertyName = "property";
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testAccessorForLambdaCanBeInlined() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_LAMBDA_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "usePropertyAccessorFromLambda");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+ String propertyName = "property";
+ FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(classSubject, getterAccessor);
+ checkMethodIsPresent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
public void testStaticFieldAccessorWithJasmin() throws Exception {
JasminBuilder jasminBuilder = new JasminBuilder();
ClassBuilder classBuilder = jasminBuilder.addClass("Foo");
@@ -228,13 +473,11 @@
if (javaResult.exitCode != 0) {
System.err.println(javaResult.stderr);
Assert.fail();
- } else {
- System.out.println(javaResult.stdout);
}
AndroidApp app = compileWithR8(jasminBuilder.build(),
keepMainProguardConfiguration("Foo") + "\n-dontobfuscate");
String artOutput = runOnArt(app, "Foo");
- System.out.println(artOutput);
+ assertEquals(javaResult.stdout, artOutput);
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
index d364bb8..0ec0f64 100644
--- a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
+++ b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.kotlin;
import com.android.tools.r8.naming.MemberNaming;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
/**
@@ -32,6 +34,22 @@
PRIVATE;
}
+ enum AccessorKind {
+ FROM_COMPANION("cp"),
+ FROM_INNER("p"),
+ FROM_LAMBDA("lp");
+
+ private final String accessorSuffix;
+
+ AccessorKind(String accessorSuffix) {
+ this.accessorSuffix = accessorSuffix;
+ }
+
+ public String getAccessorSuffix() {
+ return accessorSuffix;
+ }
+ }
+
protected static class KotlinProperty {
private final String name;
private final String type;
@@ -87,34 +105,74 @@
public MemberNaming.MethodSignature getGetterForProperty(String propertyName) {
KotlinProperty property = getProperty(propertyName);
String type = property.type;
- String getterName;
- if (propertyName.length() > 2 && propertyName.startsWith("is")
- && (propertyName.charAt(2) == '_' || Character.isUpperCase(propertyName.charAt(2)))) {
- // Getter for property "isAbc" is "isAbc".
- getterName = propertyName;
- } else {
- // Getter for property "abc" is "getAbc".
- getterName =
- "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
- }
+ String getterName = computeGetterName(propertyName);
if (property.getVisibility() == Visibility.INTERNAL) {
- // Append module name
- getterName += "$" + KOTLIN_MODULE_NAME;
+ getterName = appendInternalSuffix(getterName);
}
return new MemberNaming.MethodSignature(getterName, type, Collections.emptyList());
}
public MemberNaming.MethodSignature getSetterForProperty(String propertyName) {
KotlinProperty property = getProperty(propertyName);
- String setterName = "set"
- + Character.toUpperCase(property.name.charAt(0))
- + property.name.substring(1);
+ String setterName = computeSetterName(propertyName);
if (property.getVisibility() == Visibility.INTERNAL) {
- // Append module name
- setterName += "$" + KOTLIN_MODULE_NAME;
+ setterName = appendInternalSuffix(setterName);
}
return new MemberNaming.MethodSignature(setterName, "void",
Collections.singleton(property.getType()));
}
+ public MemberNaming.MethodSignature getGetterAccessorForProperty(String propertyName,
+ AccessorKind accessorKind) {
+ KotlinProperty property = getProperty(propertyName);
+ String getterName = computeGetterName(propertyName);
+ // Unlike normal getter, module name is not appended for accessor method of internal property.
+ getterName = wrapWithAccessorPrefixAndSuffix(accessorKind, getterName);
+ List<String> argumentTypes;
+ if (accessorKind != AccessorKind.FROM_COMPANION) {
+ argumentTypes = ImmutableList.of(getClassName());
+ } else {
+ argumentTypes = ImmutableList.of();
+ }
+ return new MemberNaming.MethodSignature(getterName, property.type, argumentTypes);
+ }
+
+ public MemberNaming.MethodSignature getSetterAccessorForProperty(String propertyName,
+ AccessorKind accessorKind) {
+ KotlinProperty property = getProperty(propertyName);
+ String setterName = computeSetterName(propertyName);
+ // Unlike normal setter, module name is not appended for accessor method of internal property.
+ setterName = wrapWithAccessorPrefixAndSuffix(accessorKind, setterName);
+ List<String> argumentTypes;
+ if (accessorKind != AccessorKind.FROM_COMPANION) {
+ argumentTypes = ImmutableList.of(getClassName(), property.getType());
+ } else {
+ argumentTypes = ImmutableList.of(property.getType());
+ }
+ return new MemberNaming.MethodSignature(setterName, "void", argumentTypes);
+ }
+
+ private static String computeGetterName(String propertyName) {
+ if (propertyName.length() > 2 && propertyName.startsWith("is")
+ && (propertyName.charAt(2) == '_' || Character.isUpperCase(propertyName.charAt(2)))) {
+ // Getter for property "isAbc" is "isAbc".
+ return propertyName;
+ } else {
+ // Getter for property "abc" is "getAbc".
+ return "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+ }
+ }
+
+ private static String computeSetterName(String propertyName) {
+ return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+ }
+
+ private static String appendInternalSuffix(String name) {
+ return name + "$" + KOTLIN_MODULE_NAME;
+ }
+
+ private String wrapWithAccessorPrefixAndSuffix(AccessorKind accessorKind, String methodName) {
+ return "access$" + methodName + "$" + accessorKind.getAccessorSuffix();
+ }
+
}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index dce8880..2f53fd9 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -13,9 +13,8 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.origin.Origin;
@@ -25,38 +24,14 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-@RunWith(Parameterized.class)
+@RunWith(VmTestRunner.class)
public class OverloadAggressivelyTest extends TestBase {
- private final DexVm dexVm;
- private final boolean overloadaggressively;
- public OverloadAggressivelyTest(DexVm dexVm, boolean overloadaggressively) {
- this.dexVm = dexVm;
- this.overloadaggressively = overloadaggressively;
- }
-
- @Parameters(name = "vm: {0}, overloadaggressively: {1}")
- public static Collection<Object[]> data() {
- List<Object[]> testCases = new ArrayList<>();
- for (DexVm version : DexVm.values()) {
- if (version.getKind() == Kind.HOST) {
- testCases.add(new Object[]{version, true});
- testCases.add(new Object[]{version, false});
- }
- }
- return testCases;
- }
-
- private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
+ private AndroidApp runR8(AndroidApp app, Class main, Path out, boolean overloadaggressively)
+ throws Exception {
R8Command command =
ToolHelper.addProguardConfigurationConsumer(
ToolHelper.prepareR8CommandBuilder(app),
@@ -75,9 +50,7 @@
return ToolHelper.runR8(command, o -> o.enableInlining = false);
}
- @Test
- public void fieldUpdater() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
+ private void fieldUpdater(boolean overloadaggressively) throws Exception {
byte[][] classes = {
ToolHelper.getClassAsBytes(FieldUpdater.class),
ToolHelper.getClassAsBytes(A.class),
@@ -85,7 +58,7 @@
};
AndroidApp originalApp = buildAndroidApp(classes);
Path out = temp.getRoot().toPath();
- AndroidApp processedApp = runR8(originalApp, FieldUpdater.class, out);
+ AndroidApp processedApp = runR8(originalApp, FieldUpdater.class, out, overloadaggressively);
DexInspector dexInspector = new DexInspector(processedApp);
ClassSubject a = dexInspector.clazz(A.class.getCanonicalName());
@@ -106,7 +79,7 @@
String main = FieldUpdater.class.getCanonicalName();
ProcessResult javaOutput = runOnJava(main, classes);
assertEquals(0, javaOutput.exitCode);
- ProcessResult artOutput = runOnArtRaw(processedApp, main, dexVm);
+ ProcessResult artOutput = runOnArtRaw(processedApp, main);
// TODO(b/72858955): eventually, R8 should avoid this field resolution conflict.
if (overloadaggressively) {
assertNotEquals(0, artOutput.exitCode);
@@ -120,8 +93,16 @@
}
@Test
- public void fieldResolution() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
+ public void testFieldUpdater_aggressively() throws Exception {
+ fieldUpdater(true);
+ }
+
+ @Test
+ public void testFieldUpdater_not_aggressively() throws Exception {
+ fieldUpdater(false);
+ }
+
+ private void fieldResolution(boolean overloadaggressively) throws Exception {
byte[][] classes = {
ToolHelper.getClassAsBytes(FieldResolution.class),
ToolHelper.getClassAsBytes(A.class),
@@ -129,7 +110,7 @@
};
AndroidApp originalApp = buildAndroidApp(classes);
Path out = temp.getRoot().toPath();
- AndroidApp processedApp = runR8(originalApp, FieldResolution.class, out);
+ AndroidApp processedApp = runR8(originalApp, FieldResolution.class, out, overloadaggressively);
DexInspector dexInspector = new DexInspector(processedApp);
ClassSubject a = dexInspector.clazz(A.class.getCanonicalName());
@@ -144,7 +125,7 @@
String main = FieldResolution.class.getCanonicalName();
ProcessResult javaOutput = runOnJava(main, classes);
assertEquals(0, javaOutput.exitCode);
- ProcessResult artOutput = runOnArtRaw(processedApp, main, dexVm);
+ ProcessResult artOutput = runOnArtRaw(processedApp, main);
// TODO(b/72858955): R8 should avoid field resolution conflict even w/ -overloadaggressively.
if (overloadaggressively) {
assertNotEquals(0, artOutput.exitCode);
@@ -158,15 +139,23 @@
}
@Test
- public void methodResolution() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
+ public void testFieldResolution_aggressively() throws Exception {
+ fieldResolution(true);
+ }
+
+ @Test
+ public void testFieldResolution_not_aggressively() throws Exception {
+ fieldResolution(false);
+ }
+
+ private void methodResolution(boolean overloadaggressively) throws Exception {
byte[][] classes = {
ToolHelper.getClassAsBytes(MethodResolution.class),
ToolHelper.getClassAsBytes(B.class)
};
AndroidApp originalApp = buildAndroidApp(classes);
Path out = temp.getRoot().toPath();
- AndroidApp processedApp = runR8(originalApp, MethodResolution.class, out);
+ AndroidApp processedApp = runR8(originalApp, MethodResolution.class, out, overloadaggressively);
DexInspector dexInspector = new DexInspector(processedApp);
ClassSubject b = dexInspector.clazz(B.class.getCanonicalName());
@@ -188,7 +177,7 @@
String main = MethodResolution.class.getCanonicalName();
ProcessResult javaOutput = runOnJava(main, classes);
assertEquals(0, javaOutput.exitCode);
- ProcessResult artOutput = runOnArtRaw(processedApp, main, dexVm);
+ ProcessResult artOutput = runOnArtRaw(processedApp, main);
// TODO(b/72858955): R8 should avoid method resolution conflict even w/ -overloadaggressively.
if (overloadaggressively) {
assertEquals(0, artOutput.exitCode);
@@ -200,4 +189,14 @@
// assertEquals(javaOutput.stderr.trim(), artOutput.stderr.trim());
}
}
+
+ @Test
+ public void testMethodResolution_aggressively() throws Exception {
+ methodResolution(true);
+ }
+
+ @Test
+ public void testMethodResolution_not_aggressively() throws Exception {
+ methodResolution(false);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
index a510cee..3e5ba7c 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
@@ -8,10 +8,8 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.jasmin.JasminTestBase;
@@ -22,39 +20,17 @@
import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-@RunWith(Parameterized.class)
+@RunWith(VmTestRunner.class)
public class ValidNameConflictTest extends JasminTestBase {
private final String CLASS_NAME = "Example";
private final String SUPER_CLASS = "Super";
private final String ANOTHER_CLASS = "Test";
private final String MSG = "Expected to be seen at the end.";
- private final DexVm dexVm;
-
- public ValidNameConflictTest(DexVm dexVm) {
- this.dexVm = dexVm;
- }
-
- @Parameters(name = "vm: {0}")
- public static Collection<Object> data() {
- List<Object> testCases = new ArrayList<>();
- for (DexVm version : DexVm.values()) {
- if (version.getKind() == Kind.HOST) {
- testCases.add(version);
- }
- }
- return testCases;
- }
-
private Iterable<String> buildCodeForVisitingDeclaredMembers(
Iterable<String> prologue, Iterable<String> argumentLoadingAndCall) {
return Iterables.concat(
@@ -105,7 +81,6 @@
@Test
public void remainFieldNameConflict_keepRules() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -130,14 +105,13 @@
assertFalse(f2.isRenamed());
assertEquals(f1.getFinalName(), f2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void remainFieldNameConflict_useuniqueclassmembernames() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -159,7 +133,7 @@
assertTrue(f2.isRenamed());
assertEquals(f1.getFinalName(), f2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@@ -167,7 +141,6 @@
@Test
public void remainFieldNameConflict_useuniqueclassmembernames_overloadaggressively()
throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -190,14 +163,13 @@
assertTrue(f2.isRenamed());
assertEquals(f1.getFinalName(), f2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void resolveFieldNameConflict_no_options() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -218,14 +190,13 @@
assertTrue(f2.isRenamed());
assertNotEquals(f1.getFinalName(), f2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void remainFieldNameConflict_overloadaggressively() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -247,7 +218,7 @@
assertTrue(f2.isRenamed());
assertEquals(f1.getFinalName(), f2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@@ -280,7 +251,6 @@
@Test
public void remainMethodNameConflict_keepRules() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -304,14 +274,13 @@
assertFalse(m2.isRenamed());
assertEquals(m1.getFinalName(), m2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void remainMethodNameConflict_useuniqueclassmembernames() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -333,7 +302,7 @@
assertTrue(m2.isRenamed());
assertEquals(m1.getFinalName(), m2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@@ -341,7 +310,6 @@
@Test
public void remainMethodNameConflict_useuniqueclassmembernames_overloadaggressively()
throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -364,14 +332,13 @@
assertTrue(m2.isRenamed());
assertEquals(m1.getFinalName(), m2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void resolveMethodNameConflict_no_options() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -392,14 +359,13 @@
assertTrue(m2.isRenamed());
assertNotEquals(m1.getFinalName(), m2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void remainMethodNameConflict_overloadaggressively() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -421,7 +387,7 @@
assertTrue(m2.isRenamed());
assertEquals(m1.getFinalName(), m2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@@ -467,7 +433,6 @@
@Test
public void remainMethodNameConflictInHierarchy_keepRules() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -505,14 +470,13 @@
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void remainMethodNameConflictInHierarchy_useuniqueclassmembernames() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -548,7 +512,7 @@
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@@ -556,7 +520,6 @@
@Test
public void remainMethodNameConflictInHierarchy_useuniqueclassmembernames_overloadaggressively()
throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -593,14 +556,13 @@
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void resolveMethodNameConflictInHierarchy_no_options() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -635,14 +597,13 @@
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
@Test
public void remainMethodNameConflictInHierarchy_overloadaggressively() throws Exception {
- Assume.assumeTrue(ToolHelper.artSupported());
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
@@ -679,7 +640,7 @@
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
- ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+ ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME);
assertEquals(0, artOutput.exitCode);
assertEquals(javaOutput.stdout, artOutput.stdout);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 5886829..4681d7d 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.shaking.ProguardKeepRule;
+import com.android.tools.r8.shaking.ProguardKeepRuleType;
import com.android.tools.r8.shaking.ProguardMemberRule;
import com.android.tools.r8.shaking.ProguardMemberType;
import com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods.ClassImplementingInterface;
@@ -274,6 +275,7 @@
configuration.getRules().forEach(rule -> {
assertTrue(rule instanceof ProguardKeepRule);
ProguardKeepRule keepRule = (ProguardKeepRule) rule;
+ assertEquals(ProguardKeepRuleType.KEEP, keepRule.getType());
assertTrue(keepRule.getModifiers().allowsObfuscation);
assertTrue(keepRule.getModifiers().allowsOptimization);
Set<ProguardMemberRule> memberRules = rule.getMemberRules();
@@ -372,6 +374,7 @@
configuration.getRules().forEach(rule -> {
assertTrue(rule instanceof ProguardKeepRule);
ProguardKeepRule keepRule = (ProguardKeepRule) rule;
+ assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, keepRule.getType());
assertTrue(keepRule.getModifiers().allowsObfuscation);
assertTrue(keepRule.getModifiers().allowsOptimization);
Set<ProguardMemberRule> memberRules = rule.getMemberRules();
@@ -477,6 +480,7 @@
configuration.getRules().forEach(rule -> {
assertTrue(rule instanceof ProguardKeepRule);
ProguardKeepRule keepRule = (ProguardKeepRule) rule;
+ assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, keepRule.getType());
assertTrue(keepRule.getModifiers().allowsObfuscation);
assertTrue(keepRule.getModifiers().allowsOptimization);
Set<ProguardMemberRule> memberRules = rule.getMemberRules();
diff --git a/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java b/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java
index a8ead8a..eff632a 100644
--- a/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java
+++ b/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java
@@ -65,10 +65,30 @@
@Test
public void testCatchAllWildcards() throws Exception {
+ testBaseWildcard(true);
+ testBaseWildcard(false);
+ testNonBaseCatchAll();
+ }
+
+ private void testNonBaseCatchAll() throws FeatureMappingException {
List<String> lines =
ImmutableList.of(
"com.google.Feature1:feature1",
- "*:base",
+ "*:nonbase",
+ "com.strange.*:feature2");
+ FeatureClassMapping mapping = new FeatureClassMapping(lines);
+ assertEquals(mapping.featureForClass("com.google.Feature1"), "feature1");
+ assertEquals(mapping.featureForClass("com.google.different.Feature1"), "nonbase");
+ assertEquals(mapping.featureForClass("com.strange.different.Feature1"), "feature2");
+ assertEquals(mapping.featureForClass("Feature1"), "nonbase");
+ assertEquals(mapping.featureForClass("a.b.z.A"), "nonbase");
+ }
+
+ private void testBaseWildcard(boolean explicitBase) throws FeatureMappingException {
+ List<String> lines =
+ ImmutableList.of(
+ "com.google.Feature1:feature1",
+ explicitBase ? "*:base" : "",
"com.strange.*:feature2");
FeatureClassMapping mapping = new FeatureClassMapping(lines);
assertEquals(mapping.featureForClass("com.google.Feature1"), "feature1");
diff --git a/src/test/kotlinR8TestResources/accessors/Accessor.kt b/src/test/kotlinR8TestResources/accessors/Accessor.kt
index e88a055..ffd0395 100644
--- a/src/test/kotlinR8TestResources/accessors/Accessor.kt
+++ b/src/test/kotlinR8TestResources/accessors/Accessor.kt
@@ -8,12 +8,20 @@
companion object {
private val property = "foo"
- fun printProperty() {
+ fun accessPropertyFromCompanionClass() {
println(property)
}
}
+
+ fun accessPropertyFromOuterClass() {
+ println(property)
+ }
}
-fun accessor_accessCompanionPrivate() {
- Accessor.printProperty()
+fun accessor_accessPropertyFromCompanionClass() {
+ Accessor.accessPropertyFromCompanionClass()
+}
+
+fun accessor_accessPropertyFromOuterClass() {
+ Accessor().accessPropertyFromOuterClass()
}
\ No newline at end of file
diff --git a/src/test/kotlinR8TestResources/accessors/PropertyAccessorForInnerClass.kt b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForInnerClass.kt
new file mode 100644
index 0000000..a2dd823
--- /dev/null
+++ b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForInnerClass.kt
@@ -0,0 +1,44 @@
+// Copyright (c) 2018, 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 accessors
+
+class PropertyAccessorForInnerClass {
+ private var privateProp = "private"
+
+ private lateinit var privateLateInitProp: String
+
+ // Causes a class initializer to be added to the class.
+ companion object {
+ public var companionProperty = "static"
+ }
+
+ inner class Inner {
+ fun accessPrivateProperty() {
+ privateProp = "bar"
+ println(privateProp)
+ }
+
+ fun accessPrivateLateInitPropertyStatus() {
+ println(::privateLateInitProp.isInitialized)
+ }
+ }
+}
+
+fun noUseOfPropertyAccessorFromInnerClass() {
+ // Create instance of class to keep them after tree shaking.
+ PropertyAccessorForInnerClass().Inner()
+}
+
+fun usePrivatePropertyAccessorFromInnerClass() {
+ // Creates a non-trivial class initializer
+ println(PropertyAccessorForInnerClass.companionProperty)
+ PropertyAccessorForInnerClass().Inner().accessPrivateProperty()
+}
+
+fun usePrivateLateInitPropertyAccessorFromInnerClass() {
+ // Creates a non-trivial class initializer
+ println(PropertyAccessorForInnerClass.companionProperty)
+ PropertyAccessorForInnerClass().Inner().accessPrivateLateInitPropertyStatus()
+}
\ No newline at end of file
diff --git a/src/test/kotlinR8TestResources/accessors/PropertyAccessorForLambda.kt b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForLambda.kt
new file mode 100644
index 0000000..28bed0a
--- /dev/null
+++ b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForLambda.kt
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, 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 accessors
+
+class PropertyAccessorForLambda {
+ private var property: String = "foo"
+ get() = { field }()
+ set(v) = { field = v }()
+
+ // Causes a class initializer to be added to the class.
+ companion object {
+ public var companionProperty = "static"
+ }
+
+ fun accessPropertyOfOuterClass() {
+ // Access to the property requires to go through an accessor method, respectively
+ // named "access$getProperty$lp" for getter and "access$setProperty$lp" for setter).
+ property = "bar"
+ println(property)
+ }
+}
+
+fun noUseOfPropertyAccessorFromLambda() {
+ // Create instance of class to keep them after tree shaking.
+ PropertyAccessorForLambda()
+}
+
+fun usePropertyAccessorFromLambda() {
+ PropertyAccessorForLambda.companionProperty = "fake"
+ PropertyAccessorForLambda().accessPropertyOfOuterClass()
+}
\ No newline at end of file