Merge commit 'f300db13a3e7d158ad40a5d675cc73ee285f75ac' into dev-release
diff --git a/build.gradle b/build.gradle index f775822..bde2f60 100644 --- a/build.gradle +++ b/build.gradle
@@ -1091,6 +1091,7 @@ task rawBuildLibraryDesugarConversions(type: Zip, dependsOn: downloadDeps) { from sourceSets.libraryDesugarConversions.output include "java/**/*.class" + include "desugar/sun/nio/fs/DesugarAndroid*.class" baseName 'library_desugar_conversions_raw' destinationDir file('build/tmp/desugaredlibrary') }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java index 37c4087..029ec75 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -168,7 +168,7 @@ itemBuilder.setClassPattern(classNamePattern); } if (methodName != null) { - itemBuilder.setMembersPattern( + itemBuilder.setMemberPattern( KeepMethodPattern.builder().setNamePattern(methodName).build()); } KeepTarget target = KeepTarget.builder().setItem(itemBuilder.build()).build();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java index 0e96364..8307265 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.keepanno.ast.KeepConsequences; import com.android.tools.r8.keepanno.ast.KeepEdge; import com.android.tools.r8.keepanno.ast.KeepItemPattern; -import com.android.tools.r8.keepanno.ast.KeepMembersPattern; +import com.android.tools.r8.keepanno.ast.KeepMemberPattern; import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern.KeepMethodNameExactPattern; import com.android.tools.r8.keepanno.ast.KeepMethodPattern; import com.android.tools.r8.keepanno.ast.KeepPreconditions; @@ -70,21 +70,21 @@ if (!item.getExtendsPattern().isAny()) { throw new Unimplemented(); } - writeMembers(item.getMembersPattern(), targetVisitor); + writeMember(item.getMemberPattern(), targetVisitor); targetVisitor.visitEnd(); }); arrayVisitor.visitEnd(); } - private void writeMembers(KeepMembersPattern membersPattern, AnnotationVisitor targetVisitor) { - if (membersPattern.isNone()) { + private void writeMember(KeepMemberPattern memberPattern, AnnotationVisitor targetVisitor) { + if (memberPattern.isNone()) { // Default is "no methods". return; } - if (membersPattern.isAll()) { + if (memberPattern.isAll()) { throw new Unimplemented(); } - KeepMethodPattern method = membersPattern.asMethod(); + KeepMethodPattern method = memberPattern.asMethod(); KeepMethodNameExactPattern exactMethodName = method.getNamePattern().asExact(); if (exactMethodName != null) { targetVisitor.visit(Target.methodName, exactMethodName.getName()); @@ -95,7 +95,14 @@ throw new Unimplemented(); } if (!method.getReturnTypePattern().isAny()) { - throw new Unimplemented(); + if (exactMethodName != null + && (exactMethodName.getName().equals("<init>") + || exactMethodName.getName().equals("<clinit>")) + && method.getReturnTypePattern().isVoid()) { + // constructors have implicit void return. + } else { + throw new Unimplemented(); + } } if (!method.getParametersPattern().isAny()) { throw new Unimplemented();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java index 5271837..89be76b 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -25,12 +25,13 @@ * CONDITION ::= ITEM_PATTERN * * CONSEQUENCES ::= TARGET+ - * TARGET ::= any | OPTIONS ITEM_PATTERN // TODO(b/248408342): What options are on target 'any'? + * TARGET ::= OPTIONS ITEM_PATTERN * OPTIONS ::= keep-all | OPTION+ * OPTION ::= shrinking | optimizing | obfuscating | access-modifying * - * ITEM_PATTERN ::= - * class QUALIFIED_CLASS_NAME_PATTERN extends EXTENDS_PATTERN { MEMBERS_PATTERN } + * ITEM_PATTERN + * ::= any + * | class QUALIFIED_CLASS_NAME_PATTERN extends EXTENDS_PATTERN { MEMBER_PATTERN } * * TYPE_PATTERN ::= any * PACKAGE_PATTERN ::= any | exact package-name @@ -38,7 +39,7 @@ * UNQUALIFIED_CLASS_NAME_PATTERN ::= any | exact simple-class-name * EXTENDS_PATTERN ::= any | QUALIFIED_CLASS_NAME_PATTERN * - * MEMBERS_PATTERN ::= none | all | METHOD_PATTERN + * MEMBER_PATTERN ::= none | all | METHOD_PATTERN * * METHOD_PATTERN * ::= METHOD_ACCESS_PATTERN
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java index 68c5c17..50d3921 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
@@ -31,14 +31,14 @@ private KeepQualifiedClassNamePattern classNamePattern; private KeepExtendsPattern extendsPattern = KeepExtendsPattern.any(); - private KeepMembersPattern membersPattern = KeepMembersPattern.none(); + private KeepMemberPattern memberPattern = KeepMemberPattern.none(); private Builder() {} public Builder any() { classNamePattern = KeepQualifiedClassNamePattern.any(); extendsPattern = KeepExtendsPattern.any(); - membersPattern = KeepMembersPattern.all(); + memberPattern = KeepMemberPattern.all(); return this; } @@ -52,8 +52,8 @@ return this; } - public Builder setMembersPattern(KeepMembersPattern membersPattern) { - this.membersPattern = membersPattern; + public Builder setMemberPattern(KeepMemberPattern memberPattern) { + this.memberPattern = memberPattern; return this; } @@ -61,29 +61,29 @@ if (classNamePattern == null) { throw new KeepEdgeException("Class pattern must define a class name pattern."); } - return new KeepItemPattern(classNamePattern, extendsPattern, membersPattern); + return new KeepItemPattern(classNamePattern, extendsPattern, memberPattern); } } private final KeepQualifiedClassNamePattern qualifiedClassPattern; private final KeepExtendsPattern extendsPattern; - private final KeepMembersPattern membersPattern; + private final KeepMemberPattern memberPattern; // TODO: class annotations private KeepItemPattern( KeepQualifiedClassNamePattern qualifiedClassPattern, KeepExtendsPattern extendsPattern, - KeepMembersPattern membersPattern) { + KeepMemberPattern memberPattern) { assert qualifiedClassPattern != null; assert extendsPattern != null; - assert membersPattern != null; + assert memberPattern != null; this.qualifiedClassPattern = qualifiedClassPattern; this.extendsPattern = extendsPattern; - this.membersPattern = membersPattern; + this.memberPattern = memberPattern; } public boolean isAny() { - return qualifiedClassPattern.isAny() && extendsPattern.isAny() && membersPattern.isAll(); + return qualifiedClassPattern.isAny() && extendsPattern.isAny() && memberPattern.isAll(); } public KeepQualifiedClassNamePattern getClassNamePattern() { @@ -94,8 +94,8 @@ return extendsPattern; } - public KeepMembersPattern getMembersPattern() { - return membersPattern; + public KeepMemberPattern getMemberPattern() { + return memberPattern; } @Override @@ -109,12 +109,12 @@ KeepItemPattern that = (KeepItemPattern) obj; return qualifiedClassPattern.equals(that.qualifiedClassPattern) && extendsPattern.equals(that.extendsPattern) - && membersPattern.equals(that.membersPattern); + && memberPattern.equals(that.memberPattern); } @Override public int hashCode() { - return Objects.hash(qualifiedClassPattern, extendsPattern, membersPattern); + return Objects.hash(qualifiedClassPattern, extendsPattern, memberPattern); } @Override @@ -124,8 +124,8 @@ + qualifiedClassPattern + ", extendsPattern=" + extendsPattern - + ", membersPattern=" - + membersPattern + + ", memberPattern=" + + memberPattern + '}'; } }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java similarity index 84% rename from src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java index 64e1b25..25a57ab 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
@@ -3,18 +3,17 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.ast; +public abstract class KeepMemberPattern { -public abstract class KeepMembersPattern { - - public static KeepMembersPattern none() { + public static KeepMemberPattern none() { return None.getInstance(); } - public static KeepMembersPattern all() { + public static KeepMemberPattern all() { return All.getInstance(); } - private static class All extends KeepMembersPattern { + private static class All extends KeepMemberPattern { private static final All INSTANCE = new All(); @@ -43,7 +42,7 @@ } } - private static class None extends KeepMembersPattern { + private static class None extends KeepMemberPattern { private static final None INSTANCE = new None(); @@ -72,7 +71,7 @@ } } - KeepMembersPattern() {} + KeepMemberPattern() {} public boolean isAll() { return false;
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java index bec8102..e00fedd 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -3,9 +3,10 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.ast; +import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern.KeepMethodNameExactPattern; import java.util.Objects; -public final class KeepMethodPattern extends KeepMembersPattern { +public final class KeepMethodPattern extends KeepMemberPattern { public static Builder builder() { return new Builder(); @@ -48,6 +49,15 @@ if (namePattern == null) { throw new KeepEdgeException("Method pattern must declar a name pattern"); } + KeepMethodReturnTypePattern returnTypePattern = this.returnTypePattern; + KeepMethodNameExactPattern exactName = namePattern.asExact(); + if (exactName != null + && (exactName.getName().equals("<init>") || exactName.getName().equals("<clinit>"))) { + if (!this.returnTypePattern.isAny() && !this.returnTypePattern.isVoid()) { + throw new KeepEdgeException("Method constructor pattern must match 'void' type."); + } + returnTypePattern = KeepMethodReturnTypePattern.voidType(); + } return new KeepMethodPattern( accessPattern, namePattern, returnTypePattern, parametersPattern); }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java index 111022b..6b7a212 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
@@ -7,10 +7,6 @@ public class KeepTarget { - public static KeepTarget any() { - return KeepTarget.builder().setItem(KeepItemPattern.any()).build(); - } - public static class Builder { private KeepItemPattern item;
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java index 3ab882f..7c8c032 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.keepanno.ast.KeepConsequences; import com.android.tools.r8.keepanno.ast.KeepEdge; import com.android.tools.r8.keepanno.ast.KeepItemPattern; -import com.android.tools.r8.keepanno.ast.KeepMembersPattern; +import com.android.tools.r8.keepanno.ast.KeepMemberPattern; import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern; import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern; @@ -102,16 +102,16 @@ if (!clazzPattern.getExtendsPattern().isAny()) { throw new Unimplemented(); } - KeepMembersPattern members = clazzPattern.getMembersPattern(); - if (members.isNone()) { + KeepMemberPattern member = clazzPattern.getMemberPattern(); + if (member.isNone()) { return builder; } - if (members.isAll()) { + if (member.isAll()) { return builder.append(" { *; }"); } - if (members.isMethod()) { + if (member.isMethod()) { builder.append(" {"); - printMethod(builder.append(' '), members.asMethod()); + printMethod(builder.append(' '), member.asMethod()); return builder.append(" }"); } throw new Unimplemented(); @@ -132,7 +132,7 @@ private static StringBuilder printParameters( StringBuilder builder, KeepMethodParametersPattern parametersPattern) { if (parametersPattern.isAny()) { - return builder.append("(***)"); + return builder.append("(...)"); } return builder .append('(') @@ -160,7 +160,7 @@ private static StringBuilder printType(StringBuilder builder, KeepTypePattern typePattern) { if (typePattern.isAny()) { - return builder.append("*"); + return builder.append("***"); } throw new Unimplemented(); } @@ -232,7 +232,7 @@ public boolean isMemberOnlyConsequent() { KeepItemPattern item = target.getItem(); - return !item.isAny() && !item.getMembersPattern().isNone(); + return !item.isAny() && !item.getMemberPattern().isNone(); } public KeepQualifiedClassNamePattern getHolderPattern() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java index 58601a6..c86857d 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.keepanno.ast.KeepConsequences; import com.android.tools.r8.keepanno.ast.KeepEdge; import com.android.tools.r8.keepanno.ast.KeepEdge.Builder; +import com.android.tools.r8.keepanno.ast.KeepEdgeException; import com.android.tools.r8.keepanno.ast.KeepItemPattern; import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern; import com.android.tools.r8.keepanno.ast.KeepMethodPattern; @@ -125,17 +126,31 @@ edgeBuilder.setConsequences(consequencesBuilder.build()); } + private String getTypeNameForClassConstantElement(DeclaredType type) { + // The processor API does not expose the descriptor or typename, so we need to depend on the + // sun.tools internals to extract it. If not, this code will not work for inner classes as + // we cannot recover the $ separator. + try { + Object tsym = type.getClass().getField("tsym").get(type); + Object flatname = tsym.getClass().getField("flatname").get(tsym); + return flatname.toString(); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new KeepEdgeException("Unable to obtain the class type name for: " + type); + } + } + private void processTarget(KeepTarget.Builder builder, AnnotationMirror mirror) { KeepItemPattern.Builder itemBuilder = KeepItemPattern.builder(); AnnotationValue classConstantValue = getAnnotationValue(mirror, Target.classConstant); if (classConstantValue != null) { DeclaredType type = AnnotationClassValueVisitor.getType(classConstantValue); - itemBuilder.setClassPattern(KeepQualifiedClassNamePattern.exact(type.toString())); + String typeName = getTypeNameForClassConstantElement(type); + itemBuilder.setClassPattern(KeepQualifiedClassNamePattern.exact(typeName)); } AnnotationValue methodNameValue = getAnnotationValue(mirror, Target.methodName); if (methodNameValue != null) { String methodName = AnnotationStringValueVisitor.getString(methodNameValue); - itemBuilder.setMembersPattern( + itemBuilder.setMemberPattern( KeepMethodPattern.builder() .setNamePattern(KeepMethodNamePattern.exact(methodName)) .build());
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidDefaultFileSystemProvider.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidDefaultFileSystemProvider.java new file mode 100644 index 0000000..8de3f76 --- /dev/null +++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidDefaultFileSystemProvider.java
@@ -0,0 +1,25 @@ +// Copyright (c) 2022, 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 desugar.sun.nio.fs; + +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.spi.FileSystemProvider; + +public class DesugarAndroidDefaultFileSystemProvider { + private static final FileSystemProvider INSTANCE = DesugarAndroidFileSystemProvider.create(); + + private DesugarAndroidDefaultFileSystemProvider() {} + + /** Returns the platform's default file system provider. */ + public static FileSystemProvider instance() { + return INSTANCE; + } + + /** Returns the platform's default file system. */ + public static FileSystem theFileSystem() { + return INSTANCE.getFileSystem(URI.create("file:///")); + } +}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java new file mode 100644 index 0000000..027d74f --- /dev/null +++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarAndroidFileSystemProvider.java
@@ -0,0 +1,49 @@ +// Copyright (c) 2022, 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 desugar.sun.nio.fs; + +import java.adapter.AndroidVersionTest; +import java.io.IOException; +import java.nio.channels.DesugarChannels; +import java.nio.channels.FileChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.spi.FileSystemProvider; +import java.util.Set; + +/** Linux implementation of {@link FileSystemProvider} for desugar support. */ +public class DesugarAndroidFileSystemProvider + extends desugar.sun.nio.fs.DesugarLinuxFileSystemProvider { + + public static DesugarAndroidFileSystemProvider create() { + return new DesugarAndroidFileSystemProvider(System.getProperty("user.dir"), "/"); + } + + DesugarAndroidFileSystemProvider(String userDir, String rootDir) { + super(userDir, rootDir); + } + + @Override + public SeekableByteChannel newByteChannel( + Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException { + if (path.toFile().isDirectory()) { + throw new UnsupportedOperationException( + "The desugar library does not support creating a file channel on a directory: " + path); + } + // A FileChannel is a SeekableByteChannel. + return newFileChannel(path, options, attrs); + } + + @Override + public FileChannel newFileChannel( + Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException { + if (AndroidVersionTest.is26OrAbove) { + throw new RuntimeException("Above Api 26, the platform FileSystemProvider should be used."); + } + return DesugarChannels.openEmulatedFileChannel(path, options, attrs); + } +}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileSystemProvider.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileSystemProvider.java deleted file mode 100644 index cf36b80..0000000 --- a/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileSystemProvider.java +++ /dev/null
@@ -1,14 +0,0 @@ -// Copyright (c) 2022, 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 desugar.sun.nio.fs; - -import java.nio.file.spi.FileSystemProvider; - -public class DesugarDefaultFileSystemProvider { - - public static FileSystemProvider instance() { - return null; - } -}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarLinuxFileSystemProvider.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarLinuxFileSystemProvider.java new file mode 100644 index 0000000..566bf12 --- /dev/null +++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarLinuxFileSystemProvider.java
@@ -0,0 +1,116 @@ +// Copyright (c) 2022, 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 desugar.sun.nio.fs; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.AccessMode; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.DirectoryStream.Filter; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.LinkOption; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.spi.FileSystemProvider; +import java.util.Map; +import java.util.Set; + +public class DesugarLinuxFileSystemProvider extends FileSystemProvider { + + DesugarLinuxFileSystemProvider(String userDir, String rootDir) { + super(); + } + + @Override + public String getScheme() { + return null; + } + + @Override + public FileSystem newFileSystem(URI uri, Map<String, ?> map) throws IOException { + return null; + } + + @Override + public FileSystem getFileSystem(URI uri) { + return null; + } + + @Override + public Path getPath(URI uri) { + return null; + } + + @Override + public SeekableByteChannel newByteChannel( + Path path, Set<? extends OpenOption> set, FileAttribute<?>... fileAttributes) + throws IOException { + return null; + } + + @Override + public DirectoryStream<Path> newDirectoryStream(Path path, Filter<? super Path> filter) + throws IOException { + return null; + } + + @Override + public void createDirectory(Path path, FileAttribute<?>... fileAttributes) throws IOException {} + + @Override + public void delete(Path path) throws IOException {} + + @Override + public void copy(Path path, Path path1, CopyOption... copyOptions) throws IOException {} + + @Override + public void move(Path path, Path path1, CopyOption... copyOptions) throws IOException {} + + @Override + public boolean isSameFile(Path path, Path path1) throws IOException { + return false; + } + + @Override + public boolean isHidden(Path path) throws IOException { + return false; + } + + @Override + public FileStore getFileStore(Path path) throws IOException { + return null; + } + + @Override + public void checkAccess(Path path, AccessMode... accessModes) throws IOException {} + + @Override + public <V extends FileAttributeView> V getFileAttributeView( + Path path, Class<V> aClass, LinkOption... linkOptions) { + return null; + } + + @Override + public <A extends BasicFileAttributes> A readAttributes( + Path path, Class<A> aClass, LinkOption... linkOptions) throws IOException { + return null; + } + + @Override + public Map<String, Object> readAttributes(Path path, String s, LinkOption... linkOptions) + throws IOException { + return null; + } + + @Override + public void setAttribute(Path path, String s, Object o, LinkOption... linkOptions) + throws IOException {} +}
diff --git a/src/library_desugar/java/java/adapter/AndroidVersionTest.java b/src/library_desugar/java/java/adapter/AndroidVersionTest.java new file mode 100644 index 0000000..065761e --- /dev/null +++ b/src/library_desugar/java/java/adapter/AndroidVersionTest.java
@@ -0,0 +1,25 @@ +// Copyright (c) 2022, 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 java.adapter; + +public class AndroidVersionTest { + + public static final boolean is24OrAbove = setUp("java.util.StringJoiner"); + public static final boolean is26OrAbove = setUp("java.nio.file.FileSystems"); + public static final boolean isHeadfull = setUp("android.os.Build"); + + /** + * Answers true if the class is present, implying the SDK is at least at the level where the class + * was introduced. + */ + private static boolean setUp(String className) { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException ignored) { + } + return false; + } +}
diff --git a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java index e34e571..eea074f 100644 --- a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java +++ b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
@@ -6,7 +6,7 @@ import android.os.StrictMode; import android.os.StrictMode.ThreadPolicy; -import desugar.sun.nio.fs.DesugarDefaultFileSystemProvider; +import desugar.sun.nio.fs.DesugarAndroidDefaultFileSystemProvider; import j$.nio.file.FileSystems; import java.net.URI; import java.nio.file.FileSystem; @@ -23,27 +23,18 @@ INSTANCE.getFileSystem(URI.create("file:///")); private static FileSystemProvider getFileSystemProvider() { - // Note: this fails on non Android devices. - try { + if (AndroidVersionTest.is26OrAbove) { // On API 26 and above, FileSystems is present. - Class.forName("java.nio.file.FileSystems"); j$.nio.file.FileSystem fileSystem = FileSystems.getDefault(); j$.nio.file.spi.FileSystemProvider provider = fileSystem.provider(); return j$.nio.file.spi.FileSystemProvider.wrap_convert(provider); - } catch (ClassNotFoundException ignored) { - // We reach this path is API < 26. } - // The DesugarDefaultFileSystemProvider requires the ThreadPolicy to be set to work correctly. - // We cannot set the ThreadPolicy in headless and it should not matter. - // In headless, android.os is absent so the following line will throw. - // In headfull, android.os is present and we set the thread policy. - try { - Class.forName("android.os.Build"); + if (AndroidVersionTest.isHeadfull) { + // The DesugarDefaultFileSystemProvider requires the ThreadPolicy to be set to work correctly. + // We cannot set the ThreadPolicy in headless and it should not matter. setThreadPolicy(); - } catch (ClassNotFoundException ignored) { - // Headless mode. } - return DesugarDefaultFileSystemProvider.instance(); + return DesugarAndroidDefaultFileSystemProvider.instance(); } private static void setThreadPolicy() {
diff --git a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java index da93736..8980540 100644 --- a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java +++ b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
@@ -17,13 +17,9 @@ private HybridFileTypeDetector() {} public static FileTypeDetector create() { - try { - // On API 26 and above, java.nio.file.Files is present. - Class.forName("java.nio.file.Files"); - return new PlatformFileTypeDetector(); - } catch (ClassNotFoundException ignored) { - return DesugarDefaultFileTypeDetector.create(); - } + return AndroidVersionTest.is26OrAbove + ? new PlatformFileTypeDetector() + : DesugarDefaultFileTypeDetector.create(); } static class PlatformFileTypeDetector extends FileTypeDetector {
diff --git a/src/library_desugar/java/java/nio/channels/DesugarChannels.java b/src/library_desugar/java/java/nio/channels/DesugarChannels.java new file mode 100644 index 0000000..4ca851d --- /dev/null +++ b/src/library_desugar/java/java/nio/channels/DesugarChannels.java
@@ -0,0 +1,242 @@ +// Copyright (c) 2022, 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 java.nio.channels; + +import java.adapter.AndroidVersionTest; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.file.NoSuchFileException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileAttribute; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class DesugarChannels { + + /** Special conversion for Channel to answer a converted FileChannel if required. */ + public static Channel convertMaybeLegacyChannelFromLibrary(Channel raw) { + if (raw == null) { + return null; + } + if (raw instanceof FileChannel) { + return convertMaybeLegacyFileChannelFromLibrary((FileChannel) raw); + } + return raw; + } + + /** + * Below Api 24 FileChannel does not implement SeekableByteChannel. When we get one from the + * library, we wrap it to implement the interface. + */ + public static FileChannel convertMaybeLegacyFileChannelFromLibrary(FileChannel raw) { + if (raw == null) { + return null; + } + if (AndroidVersionTest.is24OrAbove) { + return raw; + } + return WrappedFileChannel.wrap(raw); + } + + /** + * We unwrap when going to the library since we cannot intercept the calls to final methods in the + * library. + */ + public static FileChannel convertMaybeLegacyFileChannelToLibrary(FileChannel raw) { + if (raw == null) { + return null; + } + if (raw instanceof WrappedFileChannel) { + return ((WrappedFileChannel) raw).delegate; + } + return raw; + } + + static class WrappedFileChannel extends FileChannel implements SeekableByteChannel { + + final FileChannel delegate; + + public static FileChannel wrap(FileChannel channel) { + if (channel instanceof WrappedFileChannel) { + return channel; + } + return new WrappedFileChannel(channel); + } + + private WrappedFileChannel(FileChannel delegate) { + this.delegate = delegate; + } + + @Override + public int read(ByteBuffer dst) throws IOException { + return delegate.read(dst); + } + + @Override + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { + return delegate.read(dsts, offset, length); + } + + @Override + public int write(ByteBuffer src) throws IOException { + return delegate.write(src); + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { + return delegate.write(srcs, offset, length); + } + + @Override + public long position() throws IOException { + return delegate.position(); + } + + @Override + public FileChannel position(long newPosition) throws IOException { + return WrappedFileChannel.wrap(delegate.position(newPosition)); + } + + @Override + public long size() throws IOException { + return delegate.size(); + } + + @Override + public FileChannel truncate(long size) throws IOException { + return WrappedFileChannel.wrap(delegate.truncate(size)); + } + + @Override + public void force(boolean metaData) throws IOException { + delegate.force(metaData); + } + + @Override + public long transferTo(long position, long count, WritableByteChannel target) + throws IOException { + return delegate.transferTo(position, count, target); + } + + @Override + public long transferFrom(ReadableByteChannel src, long position, long count) + throws IOException { + return delegate.transferFrom(src, position, count); + } + + @Override + public int read(ByteBuffer dst, long position) throws IOException { + return delegate.read(dst, position); + } + + @Override + public int write(ByteBuffer src, long position) throws IOException { + return delegate.write(src, position); + } + + @Override + public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException { + return delegate.map(mode, position, size); + } + + @Override + public FileLock lock(long position, long size, boolean shared) throws IOException { + return delegate.lock(position, size, shared); + } + + @Override + public FileLock tryLock(long position, long size, boolean shared) throws IOException { + return delegate.tryLock(position, size, shared); + } + + @Override + public void implCloseChannel() throws IOException { + // We cannot call the protected method, this should be effectively equivalent. + delegate.close(); + } + } + + /** The 2 open methods are present to be retargeted from FileChannel#open. */ + public static FileChannel open(Path path, OpenOption... openOptions) throws IOException { + Set<OpenOption> openOptionSet = new HashSet<>(); + Collections.addAll(openOptionSet, openOptions); + return open(path, openOptionSet); + } + + public static FileChannel open( + Path path, Set<? extends OpenOption> openOptions, FileAttribute<?>... attrs) + throws IOException { + if (AndroidVersionTest.is26OrAbove) { + return FileChannel.open(path, openOptions, attrs); + } + return openEmulatedFileChannel(path, openOptions, attrs); + } + + /** + * All FileChannel creation go through the FileSystemProvider which then comes here if the Api is + * strictly below 26, and to the plaform FileSystemProvider if the Api is above or equal to 26. + * + * <p>Below Api 26 there is no way to create a FileChannel, so we create instead an emulated + * version using RandomAccessFile which tries, with a best effort, to support all settings. + * + * <p>The FileAttributes are ignored. + */ + public static FileChannel openEmulatedFileChannel( + Path path, Set<? extends OpenOption> openOptions, FileAttribute<?>... attrs) + throws IOException { + + validateOpenOptions(path, openOptions); + + RandomAccessFile randomAccessFile = + new RandomAccessFile(path.toFile(), getFileAccessModeText(openOptions)); + if (openOptions.contains(StandardOpenOption.TRUNCATE_EXISTING)) { + randomAccessFile.setLength(0); + } + + if (!openOptions.contains(StandardOpenOption.APPEND)) { + // This one may be retargeted, below 24, to support SeekableByteChannel. + return randomAccessFile.getChannel(); + } + + // TODO(b/259056135): Consider subclassing UnsupportedOperationException for desugared library. + // RandomAccessFile does not support APPEND. + // We could hack a wrapper to support APPEND in simple cases such as Files.write(). + throw new UnsupportedOperationException(); + } + + private static void validateOpenOptions(Path path, Set<? extends OpenOption> openOptions) + throws NoSuchFileException { + // Validations that resemble sun.nio.fs.UnixChannelFactory#newFileChannel. + if (openOptions.contains(StandardOpenOption.READ) + && openOptions.contains(StandardOpenOption.APPEND)) { + throw new IllegalArgumentException("READ + APPEND not allowed"); + } + if (openOptions.contains(StandardOpenOption.APPEND) + && openOptions.contains(StandardOpenOption.TRUNCATE_EXISTING)) { + throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); + } + if (openOptions.contains(StandardOpenOption.APPEND) && !path.toFile().exists()) { + throw new NoSuchFileException(path.toString()); + } + } + + private static String getFileAccessModeText(Set<? extends OpenOption> options) { + if (!options.contains(StandardOpenOption.WRITE)) { + return "r"; + } + if (options.contains(StandardOpenOption.SYNC)) { + return "rws"; + } + if (options.contains(StandardOpenOption.DSYNC)) { + return "rwd"; + } + return "rw"; + } +}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json index 91f9a56..46a6562 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -2,13 +2,57 @@ "configuration_format_version": 5, "group_id": "com.tools.android", "artifact_id": "desugar_jdk_libs", - "version": "1.2.4", - "required_compilation_api_level": 30, + "version": "1.2.5", + "required_compilation_api_level": 33, "synthesized_library_classes_package_prefix": "j$.", "support_all_callbacks_from_library": true, "common_flags": [ { - "api_level_below_or_equal": 10000, + "api_level_below_or_equal": 32, + "rewrite_prefix": { + "java.util.stream.DesugarCollectors": "j$.util.stream.DesugarCollectors" + }, + "retarget_lib_member": { + "java.util.stream.Collectors#filtering": "java.util.stream.DesugarCollectors", + "java.util.stream.Collectors#flatMapping": "java.util.stream.DesugarCollectors", + "java.util.stream.Collectors#toUnmodifiableList": "java.util.stream.DesugarCollectors", + "java.util.stream.Collectors#toUnmodifiableMap": "java.util.stream.DesugarCollectors", + "java.util.stream.Collectors#toUnmodifiableSet": "java.util.stream.DesugarCollectors" + } + }, + { + "api_level_below_or_equal": 30, + "rewrite_prefix": { + "java.time.DesugarDuration": "j$.time.DesugarDuration", + "java.time.DesugarLocalTime": "j$.time.DesugarLocalTime" + }, + "retarget_lib_member": { + "java.time.Duration#dividedBy": "java.time.DesugarDuration", + "java.time.Duration#toDaysPart": "java.time.DesugarDuration", + "java.time.Duration#toHoursPart": "java.time.DesugarDuration", + "java.time.Duration#toMillisPart": "java.time.DesugarDuration", + "java.time.Duration#toMinutesPart": "java.time.DesugarDuration", + "java.time.Duration#toNanosPart": "java.time.DesugarDuration", + "java.time.Duration#toSeconds": "java.time.DesugarDuration", + "java.time.Duration#toSecondsPart": "java.time.DesugarDuration", + "java.time.Duration#truncatedTo": "java.time.DesugarDuration", + "java.time.LocalTime#ofInstant": "java.time.DesugarLocalTime", + "java.time.LocalTime#toEpochSecond": "java.time.DesugarLocalTime" + } + }, + { + "api_level_below_or_equal": 29, + "rewrite_prefix": { + "java.util.concurrent.Flow": "j$.util.concurrent.Flow" + }, + "wrapper_conversion": [ + "java.util.concurrent.Flow$Publisher", + "java.util.concurrent.Flow$Subscriber", + "java.util.concurrent.Flow$Subscription" + ] + }, + { + "api_level_below_or_equal": 25, "rewrite_prefix": { "java.time.": "j$.time.", "java.util.Desugar": "j$.util.Desugar" @@ -34,26 +78,10 @@ } }, { - "api_level_below_or_equal": 29, - "rewrite_prefix": { - "java.util.concurrent.Flow": "j$.util.concurrent.Flow" - }, - "wrapper_conversion": [ - "java.util.concurrent.Flow$Publisher", - "java.util.concurrent.Flow$Subscriber", - "java.util.concurrent.Flow$Subscription" - ] - }, - { "api_level_below_or_equal": 23, "rewrite_prefix": { "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic", - "java.util.function.": "j$.util.function." - } - }, - { - "api_level_below_or_equal": 10000, - "rewrite_prefix": { + "java.util.function.": "j$.util.function.", "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics", "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics", "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics", @@ -150,7 +178,7 @@ ], "program_flags": [ { - "api_level_below_or_equal": 10000, + "api_level_below_or_equal": 25, "retarget_lib_member": { "java.util.TimeZone#getTimeZone": "java.util.DesugarTimeZone", "java.util.Calendar#toInstant": "java.util.DesugarCalendar", @@ -180,7 +208,7 @@ ], "library_flags": [ { - "api_level_below_or_equal": 10000, + "api_level_below_or_equal": 25, "rewrite_prefix": { "j$.time.": "java.time.", "java.lang.Desugar": "j$.lang.Desugar", @@ -196,7 +224,7 @@ } }, { - "api_level_below_or_equal": 10000, + "api_level_below_or_equal": 23, "rewrite_prefix": { "java.util.AbstractList": "j$.util.AbstractList", "java.util.CollSer": "j$.util.CollSer",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json index c97bd79..3295333 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -166,6 +166,7 @@ "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel", "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel", "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler", + "java.nio.channels.Desugar": "j$.nio.channels.Desugar", "java.nio.file.": "j$.nio.file." }, "dont_rewrite_prefix": [ @@ -230,7 +231,8 @@ "java.lang.Iterable java.nio.file.FileSystem#getRootDirectories()": [-1, "java.lang.Iterable java.nio.file.PathApiFlips#flipIterablePath(java.lang.Iterable)"], "java.util.Iterator java.nio.file.Path#iterator()": [-1, "java.util.Iterator java.nio.file.PathApiFlips#flipIteratorPath(java.util.Iterator)"], "java.nio.file.DirectoryStream java.nio.file.spi.FileSystemProvider#newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream$Filter)": [-1, "java.nio.file.DirectoryStream java.nio.file.PathApiFlips#flipDirectoryStreamPath(java.nio.file.DirectoryStream)", 1, "java.nio.file.DirectoryStream$Filter java.nio.file.PathApiFlips#flipDirectoryStreamFilterPath(java.nio.file.DirectoryStream$Filter)"], - "void java.nio.file.spi.FileSystemProvider#setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption[])": [2, "java.lang.Object java.nio.file.FileApiFlips#flipMaybeFileTime(java.lang.Object)"] + "void java.nio.file.spi.FileSystemProvider#setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption[])": [2, "java.lang.Object java.nio.file.FileApiFlips#flipMaybeFileTime(java.lang.Object)"], + "java.nio.channels.FileChannel java.nio.channels.FileChannel#open(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "java.util.Set java.nio.file.FileApiFlips#flipOpenOptionSet(java.util.Set)"] }, "wrapper_conversion": [ "java.nio.channels.CompletionHandler", @@ -279,8 +281,6 @@ "api_level_below_or_equal": 23, "rewrite_prefix": { "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader", - "java.nio.channels.FileChannel": "j$.nio.channels.FileChannel", - "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel", "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics", "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics", "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics", @@ -294,12 +294,10 @@ "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic", "java.util.stream.": "j$.util.stream." }, - "dont_rewrite_prefix": [ - "java.nio.channels.FileChannel$MapMode" - ], "maintain_prefix": [ "java.util.function.", - "java.io.UncheckedIOException" + "java.io.UncheckedIOException", + "java.nio.channels.SeekableByteChannel" ], "emulate_interface": { "java.lang.Iterable": "j$.lang.Iterable", @@ -347,13 +345,18 @@ "java.util.stream.IntStream java.util.stream.IntStream#flatMap(java.util.function.IntFunction)": [0, "java.util.function.IntFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.IntFunction)"], "java.util.stream.LongStream java.util.stream.Stream#flatMapToLong(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"], "java.util.stream.LongStream java.util.stream.LongStream#flatMap(java.util.function.LongFunction)": [0, "java.util.function.LongFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.LongFunction)"], - "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.StackWalkerApiFlips#flipFunctionStream(java.util.function.Function)"] + "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.StackWalkerApiFlips#flipFunctionStream(java.util.function.Function)"], + "java.nio.channels.FileChannel java.nio.channels.FileLock#channel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"], + "java.nio.channels.Channel java.nio.channels.FileLock#acquiredBy()": [-1, "java.nio.channels.Channel java.nio.channels.DesugarChannels#convertMaybeLegacyChannelFromLibrary(java.nio.channels.Channel)"], + "java.nio.channels.FileChannel java.io.RandomAccessFile#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"], + "java.nio.channels.FileChannel java.io.FileInputStream#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"], + "java.nio.channels.FileChannel java.io.FileOutputStream#getChannel()": [-1, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelFromLibrary(java.nio.channels.FileChannel)"], + "void java.nio.channels.FileLock#<init>(java.nio.channels.FileChannel,long, long, boolean)": [0, "java.nio.channels.FileChannel java.nio.channels.DesugarChannels#convertMaybeLegacyFileChannelToLibrary(java.nio.channels.FileChannel)"] }, "never_outline_api": [ "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)" ], "wrapper_conversion": [ - "java.nio.channels.SeekableByteChannel", "java.util.PrimitiveIterator$OfDouble", "java.util.PrimitiveIterator$OfInt", "java.util.PrimitiveIterator$OfLong", @@ -370,18 +373,6 @@ "java.util.stream.LongStream", "java.util.stream.Stream" ], - "wrapper_conversion_excluding": { - "java.nio.channels.FileChannel": [ - "long java.nio.channels.FileChannel#read(java.nio.ByteBuffer[])", - "long java.nio.channels.FileChannel#write(java.nio.ByteBuffer[])", - "java.nio.channels.FileLock java.nio.channels.FileChannel#lock()", - "java.nio.channels.FileLock java.nio.channels.FileChannel#tryLock()", - "void java.nio.channels.spi.AbstractInterruptibleChannel#close()", - "boolean java.nio.channels.spi.AbstractInterruptibleChannel#isOpen()", - "void java.nio.channels.spi.AbstractInterruptibleChannel#begin()", - "void java.nio.channels.spi.AbstractInterruptibleChannel#end(boolean)" - ] - }, "custom_conversion": { "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions", "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions", @@ -442,7 +433,9 @@ "java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar", "java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate", "java.util.GregorianCalendar java.util.GregorianCalendar#from(java.time.ZonedDateTime)": "java.util.DesugarGregorianCalendar", - "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone" + "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone", + "java.nio.channels.FileChannel java.nio.channels.FileChannel#open(java.nio.file.Path, java.nio.file.OpenOption[])": "java.nio.channels.DesugarChannels", + "java.nio.channels.FileChannel java.nio.channels.FileChannel#open(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])": "java.nio.channels.DesugarChannels" } }, { @@ -483,7 +476,6 @@ { "api_level_below_or_equal": 25, "rewrite_prefix": { - "java.nio.channels.Desugar": "j$.nio.channels.Desugar", "jdk.internal.": "j$.jdk.internal.", "sun.misc.Desugar": "j$.sun.misc.Desugar", "sun.nio.cs.": "j$.sun.nio.cs.",
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java index 6754dde..4649132 100644 --- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java +++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -141,10 +141,12 @@ private final Set<DexLibraryClass> libraryClassesToMock = Sets.newConcurrentHashSet(); private final Set<DexType> seenTypes = Sets.newConcurrentHashSet(); private final AndroidApiLevelCompute apiLevelCompute; + private final DexItemFactory factory; public ApiReferenceStubber(AppView<?> appView) { this.appView = appView; apiLevelCompute = appView.apiLevelCompute(); + factory = appView.dexItemFactory(); } public void run(ExecutorService executorService) throws ExecutionException { @@ -228,6 +230,12 @@ || libraryClass.getType().toDescriptorString().startsWith("Ljava/")) { return; } + // We cannot reliably create a stub that will have the same throwing + // behavior for all VMs. We only create stubs for exceptions to allow them being present in + // catch handlers. See b/b/258270051 for more information. + if (!isThrowable(libraryClass)) { + return; + } if (appView .options() .machineDesugaredLibrarySpecification @@ -259,7 +267,6 @@ .setProto(factory.createProto(factory.voidType)) .setAccessFlags(MethodAccessFlags.createForClassInitializer()) .setCode(method -> throwExceptionCode)); - // Based on b/138781768#comment57 there is no significant reason to synthesize fields. if (libraryClass.isInterface()) { classBuilder.setInterface(); } @@ -269,4 +276,16 @@ }, ignored -> {}); } + + private boolean isThrowable(DexLibraryClass libraryClass) { + DexClass current = libraryClass; + while (current.getSuperType() != null) { + DexType superType = current.getSuperType(); + if (superType == factory.throwableType) { + return true; + } + current = appView.definitionFor(current.getSuperType()); + } + return false; + } }
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 5869f46..65572c8 100644 --- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java +++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -75,9 +75,8 @@ import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.code.ValueType; -import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.RetracerForCodePrinting; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; @@ -112,7 +111,7 @@ private final List<List<LocalVariableInfo>> localsAtLabel; private final StringBuilder builder = new StringBuilder(); - private final ClassNameMapper mapper; + private final RetracerForCodePrinting retracer; private int nextInstructionIndex = 0; private final int instructionIndexSpace; @@ -121,7 +120,7 @@ public CfPrinter() { indent = ""; labelToIndex = null; - mapper = null; + retracer = RetracerForCodePrinting.empty(); instructionIndexSpace = 0; sortedLabels = Collections.emptyList(); @@ -130,12 +129,12 @@ /** Entry for printing a complete code object. */ public CfPrinter(CfCode code) { - this(code, null, null); + this(code, null, RetracerForCodePrinting.empty()); } /** Entry for printing a complete method object. */ - public CfPrinter(CfCode code, DexEncodedMethod method, ClassNameMapper mapper) { - this.mapper = mapper; + public CfPrinter(CfCode code, DexEncodedMethod method, RetracerForCodePrinting retracer) { + this.retracer = retracer; indent = " "; instructionIndexSpace = ("" + code.getInstructions().size()).length(); labelToIndex = new Reference2IntOpenHashMap<>(); @@ -770,56 +769,36 @@ } private void appendDescriptor(DexType type) { - if (mapper != null) { - builder.append(DescriptorUtils.javaTypeToDescriptor(mapper.originalNameOf(type))); - return; - } - builder.append(type.toDescriptorString()); + builder.append(retracer.toDescriptor(type)); } private void appendType(DexType type) { if (type.isArrayType() || type.isClassType()) { appendClass(type); } else { - builder.append(type); + builder.append(retracer.toDescriptor(type)); } } private void appendTypeElement(TypeElement type) { - builder.append(type.toString()); + builder.append(type); } private void appendClass(DexType type) { assert type.isArrayType() || type.isClassType(); - if (mapper == null) { - builder.append(type.getInternalName()); - } else if (type == DexItemFactory.nullValueType) { + if (type == DexItemFactory.nullValueType) { builder.append("NULL"); } else { - builder.append( - DescriptorUtils.descriptorToInternalName( - DescriptorUtils.javaTypeToDescriptor(mapper.originalNameOf(type)))); + builder.append(retracer.toDescriptor(type)); } } private void appendField(DexField field) { - if (mapper != null) { - builder.append(mapper.originalSignatureOf(field).toString()); - return; - } - appendClass(field.holder); - builder.append('/').append(field.name); + builder.append(retracer.toDescriptor(field)); } private void appendMethod(DexMethod method) { - if (mapper != null) { - MethodSignature signature = mapper.originalSignatureOf(method); - builder.append(mapper.originalNameOf(method.holder)).append('.'); - builder.append(signature.name).append(signature.toDescriptor()); - return; - } - builder.append(method.qualifiedName()); - builder.append(method.proto.toDescriptorString()); + builder.append(retracer.toDescriptor(method)); } private String opcodeName(int opcode) {
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConst.java b/src/main/java/com/android/tools/r8/dex/code/DexConst.java index 33fdff4..c47c264 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConst.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConst.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.SingleConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConst extends DexFormat31i implements SingleConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) + " # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConst16.java b/src/main/java/com/android/tools/r8/dex/code/DexConst16.java index 1188df2..d676e03 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConst16.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConst16.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.SingleConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConst16 extends DexFormat21s implements SingleConstant { @@ -44,7 +44,7 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 4) + " (" + decodedValue() + ")"); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConst4.java b/src/main/java/com/android/tools/r8/dex/code/DexConst4.java index 09ecd15..f1dd41c 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConst4.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConst4.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.SingleConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConst4 extends DexFormat11n implements SingleConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + A + ", " + StringUtils.hexString(decodedValue(), 1) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + A + ", " + StringUtils.hexString(decodedValue(), 2) + " # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstHigh16.java b/src/main/java/com/android/tools/r8/dex/code/DexConstHigh16.java index 2e732ed..82a6aa3 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstHigh16.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstHigh16.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.SingleConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConstHigh16 extends DexFormat21h implements SingleConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) + " # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java index 87964c8..b909f64 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java
@@ -15,7 +15,7 @@ import com.android.tools.r8.graph.UseRegistry.MethodHandleUse; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; @@ -58,12 +58,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", \"" + BBBB.toString() + "\""); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\""); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java index 68f49fb..15bee8c 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java
@@ -14,7 +14,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; @@ -57,12 +57,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", \"" + BBBB.toString() + "\""); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\""); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstString.java b/src/main/java/com/android/tools/r8/dex/code/DexConstString.java index 0117785..e143d89 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstString.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstString.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; @@ -75,12 +75,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", \"" + BBBB.toString() + "\""); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\""); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstStringJumbo.java b/src/main/java/com/android/tools/r8/dex/code/DexConstStringJumbo.java index 725ef8a..e1811ba 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstStringJumbo.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstStringJumbo.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.OffsetToObjectMapping; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; public class DexConstStringJumbo extends DexFormat31c { @@ -52,12 +52,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", \"" + BBBBBBBB.toString() + "\""); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", \"" + BBBBBBBB.toString() + "\""); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstWide.java b/src/main/java/com/android/tools/r8/dex/code/DexConstWide.java index 6fbd6f2..899e221 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstWide.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstWide.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.WideConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConstWide extends DexFormat51l implements WideConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + "L # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstWide16.java b/src/main/java/com/android/tools/r8/dex/code/DexConstWide16.java index e30b886..2cd5ae3 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstWide16.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstWide16.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.WideConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConstWide16 extends DexFormat21s implements WideConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + "L # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstWide32.java b/src/main/java/com/android/tools/r8/dex/code/DexConstWide32.java index b315cb9..9797a19 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstWide32.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstWide32.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.WideConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConstWide32 extends DexFormat31i implements WideConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + " # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstWideHigh16.java b/src/main/java/com/android/tools/r8/dex/code/DexConstWideHigh16.java index 0eeca93..93b7f34 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexConstWideHigh16.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexConstWideHigh16.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.WideConstant; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; public class DexConstWideHigh16 extends DexFormat21h implements WideConstant { @@ -44,13 +44,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + " (" + decodedValue() + ")"); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + "L # " + decodedValue()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFillArrayData.java b/src/main/java/com/android/tools/r8/dex/code/DexFillArrayData.java index de478d7..0a3974d 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFillArrayData.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFillArrayData.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.dex.code; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; public class DexFillArrayData extends DexFormat31t { @@ -41,7 +41,7 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java b/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java index 67d796c..228258d 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; @@ -91,8 +91,8 @@ } @Override - public String toString(ClassNameMapper naming) { - return super.toString(naming) + public String toString(RetracerForCodePrinting retracer) { + return super.toString(retracer) + "[FillArrayPayload], " + "width: " + element_width @@ -101,7 +101,7 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); builder.append(" "); builder.append(".array-data ");
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java index ba9065a..ba29cbe 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java
@@ -9,7 +9,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import java.nio.ShortBuffer; @@ -56,12 +56,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString(formatRelativeOffset(AA)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString(":label_" + (getOffset() + AA)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java index 3cb41fb..174c89b 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java
@@ -9,7 +9,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import java.nio.ShortBuffer; abstract class DexFormat10x extends DexBase1Format { @@ -33,12 +33,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString(null); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString(null); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java index fe02384..09e3fb6 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -70,7 +70,7 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + A + ", #" + B); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java index b07478c..393ab88 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import java.nio.ShortBuffer; @@ -56,12 +56,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java index 633837d..979bcc7 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -64,12 +64,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + A + ", v" + B); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + A + ", v" + B); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java index 0c0e15e..a03b9c5 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java
@@ -9,7 +9,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import java.nio.ShortBuffer; @@ -56,12 +56,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("" + AAAA + " " + formatRelativeOffset(AAAA)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString(":label_" + (getOffset() + AAAA)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat21c.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat21c.java index b47c6d7..4d25775 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat21c.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat21c.java
@@ -5,7 +5,7 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.IndexedDexItem; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -52,13 +52,12 @@ abstract void internalSubSpecify(StructuralSpecification<DexFormat21c<T>, ?> spec); @Override - public String toString(ClassNameMapper naming) { - return formatString( - "v" + AA + ", " + (naming == null ? BBBB.toString() : naming.originalNameOf(BBBB))); + public String toString(RetracerForCodePrinting retracer) { + return formatString("v" + AA + ", " + retracer.toDescriptor(BBBB)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { // TODO(sgjesse): Add support for smali name mapping. return formatSmaliString("v" + AA + ", " + BBBB.toSmaliString()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java index 97e3529..3d66ad2 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; @@ -67,12 +67,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", #" + BBBB); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", " + StringUtils.hexString(BBBB, 4) + " # " + BBBB); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java index 1c80b33..93cd025 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.ir.code.ValueTypeConstraint; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -85,12 +85,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", " + formatRelativeOffset(BBBB)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBB)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java index 687d7e7..222fc09 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; @@ -71,12 +71,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", v" + BB + ", #" + CC); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + AA + ", v" + BB + ", " + StringUtils.hexString(CC, 2) + " # " + CC); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22c.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22c.java index 2e1246c..68c7839 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22c.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22c.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.IndexedDexItem; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -57,13 +57,12 @@ } @Override - public String toString(ClassNameMapper naming) { - return formatString( - "v" + A + ", v" + B + ", " + (naming == null ? CCCC : naming.originalNameOf(CCCC))); + public String toString(RetracerForCodePrinting retracer) { + return formatString("v" + A + ", v" + B + ", " + retracer.toDescriptor(CCCC)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { // TODO(sgjesse): Add support for smali name mapping. return formatSmaliString("v" + A + ", v" + B + ", " + CCCC.toSmaliString()); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java index e503467..53d247c 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; @@ -71,12 +71,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + A + ", v" + B + ", #" + CCCC); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString( "v" + A + ", v" + B + ", " + StringUtils.hexString(CCCC, 4) + " # " + CCCC); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java index 17695d7..c276ca1 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.ir.code.ValueTypeConstraint; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -89,12 +89,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + A + ", v" + B + ", " + formatRelativeOffset(CCCC)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + A + ", v" + B + ", :label_" + (getOffset() + CCCC)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java index 3c34540..3cd881d 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -66,12 +66,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", v" + (int) BBBB); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", v" + (int) BBBB); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java index df098d9..1e1b130 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -70,12 +70,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", v" + BB + ", v" + CC); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", v" + BB + ", v" + CC); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java index aa744ad..cfd2bf7 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java
@@ -9,7 +9,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import java.nio.ShortBuffer; @@ -55,12 +55,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString(formatOffset(AAAAAAAA)); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString(":label_" + (getOffset() + AAAAAAAA)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java index 8023664..ae0ca6c 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -69,9 +69,8 @@ } @Override - public String toString(ClassNameMapper naming) { - return formatString( - "v" + AA + ", " + (naming == null ? BBBBBBBB : naming.originalNameOf(BBBBBBBB))); + public String toString(RetracerForCodePrinting retracer) { + return formatString("v" + AA + ", " + retracer.toDescriptor(BBBBBBBB)); } @Override
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java index 0795a92..942c5e2 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -65,7 +65,7 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", #" + BBBBBBBB); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java index 320514d..7f26c21 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -80,7 +80,7 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", " + formatRelativeOffset(BBBBBBBB)); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java index 00cf1f4..5389c2f 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -68,12 +68,12 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AAAA + ", v" + BBBB); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AAAA + ", v" + BBBB); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat35c.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat35c.java index 6e40c73..0f195c1 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat35c.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat35c.java
@@ -5,7 +5,7 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.IndexedDexItem; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralItem; @@ -94,20 +94,16 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterArguments(builder, " "); builder.append(" "); - if (naming == null) { - builder.append(BBBB.toSmaliString()); - } else { - builder.append(naming.originalNameOf(BBBB)); - } + builder.append(retracer.toDescriptor(BBBB)); return formatString(builder.toString()); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterArguments(builder, ", "); builder.append(", ");
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat3rc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat3rc.java index b0c0204..3d8289c 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat3rc.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat3rc.java
@@ -5,7 +5,7 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.IndexedDexItem; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralItem; @@ -71,20 +71,16 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterRange(builder); builder.append(" "); - if (naming == null) { - builder.append(BBBB.toSmaliString()); - } else { - builder.append(naming.originalNameOf(BBBB)); - } + builder.append(retracer.toDescriptor(BBBB)); return formatString(builder.toString()); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterRange(builder); builder.append(", ");
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java index 61a0257..2f4adc5 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
@@ -11,12 +11,11 @@ import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.GraphLens.MethodLookupResult; -import com.android.tools.r8.graph.IndexedDexItem; import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -137,7 +136,7 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterArguments(builder, ", "); builder.append(", "); @@ -149,26 +148,16 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterArguments(builder, " "); builder.append(" "); - builder.append(itemToString(BBBB, naming)); + builder.append(retracer.toDescriptor(BBBB)); builder.append(", "); - builder.append(itemToString(HHHH, naming)); + builder.append(retracer.toDescriptor(HHHH)); return formatString(builder.toString()); } - private String itemToString(IndexedDexItem indexedDexItem, ClassNameMapper naming) { - String str; - if (naming == null) { - str = indexedDexItem.toSmaliString(); - } else { - str = naming.originalNameOf(indexedDexItem); - } - return str; - } - private void appendRegisterArguments(StringBuilder builder, String separator) { builder.append("{ "); int[] values = new int[] {C, D, E, F, G};
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java index b795316..8aa065a 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
@@ -15,7 +15,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -90,25 +90,17 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterRange(builder); builder.append(" "); - if (naming == null) { - builder.append(BBBB.toSmaliString()); - } else { - builder.append(naming.originalNameOf(BBBB)); - } - if (naming == null) { - builder.append(HHHH.toSmaliString()); - } else { - builder.append(naming.originalNameOf(HHHH)); - } + builder.append(retracer.toDescriptor(BBBB)); + builder.append(retracer.toDescriptor(HHHH)); return formatString(builder.toString()); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); appendRegisterRange(builder); builder.append(", ");
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java index 30ddee5..ddce5ad 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -65,7 +65,7 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { return formatString("v" + AA + ", #" + BBBBBBBBBBBBBBBB); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java b/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java index c600656..8591fd9 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java
@@ -16,7 +16,7 @@ import com.android.tools.r8.ir.code.FieldMemberType; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -144,18 +144,12 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + dest + ", " + clazz.toSmaliString()); } @Override - public String toString(ClassNameMapper naming) { - StringBuilder builder = new StringBuilder("v").append(dest).append(", "); - if (naming == null) { - builder.append(clazz.toSourceString()); - } else { - builder.append(naming.originalNameOf(clazz)); - } - return formatString(builder.toString()); + public String toString(RetracerForCodePrinting retracer) { + return formatString("v" + dest + ", " + retracer.toDescriptor(clazz)); } }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java b/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java index d5f8183..e6362d0 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
@@ -19,7 +19,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.Equatable; @@ -372,21 +372,21 @@ throw new InternalCompilerError("Instruction " + payloadUser + " is not a payload user"); } - public abstract String toSmaliString(ClassNameMapper naming); + public abstract String toSmaliString(RetracerForCodePrinting retracer); public String toSmaliString() { - return toSmaliString((ClassNameMapper) null); + return toSmaliString((RetracerForCodePrinting) null); } - public abstract String toString(ClassNameMapper naming); + public abstract String toString(RetracerForCodePrinting retracer); - public String toString(ClassNameMapper naming, DexInstruction payloadUser) { + public String toString(RetracerForCodePrinting retracer, DexInstruction payloadUser) { throw new InternalCompilerError("Instruction " + payloadUser + " is not a payload user"); } @Override public String toString() { - return toString(null); + return toString(RetracerForCodePrinting.empty()); } public abstract void write(
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java index d34a783..ddaf074 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java
@@ -13,8 +13,8 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; @@ -85,13 +85,13 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { // TODO(christofferqa): Apply mapping to item. return formatString("v" + AA + ", \"" + BBBB.toString() + "\""); } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { // TODO(christofferqa): Apply mapping to item. return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\""); }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitch.java b/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitch.java index 34029d8..c00a3b4 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitch.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitch.java
@@ -5,7 +5,7 @@ package com.android.tools.r8.dex.code; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; public class DexPackedSwitch extends DexFormat31t { @@ -50,7 +50,7 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB)); } }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java b/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java index d06f619..95f6eed 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java
@@ -7,7 +7,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; @@ -102,12 +102,12 @@ } @Override - public String toString(ClassNameMapper naming) { - return toString(naming, null); + public String toString(RetracerForCodePrinting retracer) { + return toString(retracer, null); } @Override - public String toString(ClassNameMapper naming, DexInstruction payloadUser) { + public String toString(RetracerForCodePrinting retracer, DexInstruction payloadUser) { StringBuilder builder = new StringBuilder("[PackedSwitchPayload"); if (payloadUser == null) { builder.append(" offsets relative to associated PackedSwitch"); @@ -123,7 +123,7 @@ } StringUtils.appendLeftPadded(builder, (first_key + i) + " -> " + offsetString + "\n", 20); } - return super.toString(naming) + builder.toString(); + return super.toString(retracer) + builder.toString(); } @Override
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java b/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java index 955f67e..3101663 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -100,7 +100,7 @@ } @Override - public String toString(ClassNameMapper naming) { + public String toString(RetracerForCodePrinting retracer) { StringBuilder sb = new StringBuilder(); sb.append("v").append(outRegister).append(" "); appendArguments(sb); @@ -108,8 +108,8 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { - return toString(naming); + public String toSmaliString(RetracerForCodePrinting retracer) { + return toString(retracer); } @Override
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitch.java b/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitch.java index 4f15ab7..8dfabb6 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitch.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitch.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.dex.code; import com.android.tools.r8.ir.conversion.IRBuilder; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; public class DexSparseSwitch extends DexFormat31t { @@ -49,7 +49,7 @@ } @Override - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB)); } }
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java b/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java index 97d369a..838e4ea 100644 --- a/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java +++ b/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java
@@ -7,7 +7,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; @@ -108,12 +108,12 @@ } @Override - public String toString(ClassNameMapper naming) { - return toString(naming, null); + public String toString(RetracerForCodePrinting retracer) { + return toString(retracer, null); } @Override - public String toString(ClassNameMapper naming, DexInstruction payloadUser) { + public String toString(RetracerForCodePrinting retracer, DexInstruction payloadUser) { StringBuilder builder = new StringBuilder("[SparseSwitchPayload"); if (payloadUser == null) { builder.append(" offsets relative to associated SparseSwitch"); @@ -129,7 +129,7 @@ } StringUtils.appendLeftPadded(builder, keys[i] + " -> " + offsetString + "\n", 20); } - return super.toString(naming) + builder.toString(); + return super.toString(retracer) + builder.toString(); } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java index 369e60c..4b70a1c 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -275,8 +275,8 @@ public boolean isSubtype(DexType subtype, DexType supertype) { assert subtype != null; assert supertype != null; - assert subtype.isClassType(); - assert supertype.isClassType(); + assert subtype.isClassType() : "subtype not a class: " + subtype; + assert supertype.isClassType() : "supertype not a class: " + supertype; return subtype == supertype || isStrictSubtypeOf(subtype, supertype); }
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java index 3061f1b..43ee151 100644 --- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java +++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -12,11 +12,12 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; import com.android.tools.r8.kotlin.Kotlin; import com.android.tools.r8.kotlin.KotlinMetadataWriter; -import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.naming.MemberNaming.FieldSignature; import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy; import com.android.tools.r8.utils.CfgPrinter; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.RetracerForCodePrinting; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.StringUtils.BraceType; import com.android.tools.r8.utils.Timing; import java.io.BufferedReader; import java.io.PrintStream; @@ -34,6 +35,7 @@ private final Kotlin kotlin; private final Timing timing = new Timing("AssemblyWriter"); private final CompilationContext compilationContext; + private final RetracerForCodePrinting retracer; public AssemblyWriter( DexApplication application, @@ -62,6 +64,8 @@ this.appInfo = null; } kotlin = new Kotlin(application.dexItemFactory); + retracer = + RetracerForCodePrinting.create(application.getProguardMap(), application.options.reporter); } public static String getFileEnding() { @@ -70,22 +74,17 @@ @Override void writeClassHeader(DexProgramClass clazz, PrintStream ps) { - String clazzName; - if (application.getProguardMap() != null) { - clazzName = application.getProguardMap().originalNameOf(clazz.type); - } else { - clazzName = clazz.type.toSourceString(); - } + String clazzName = retracer.toSourceString(clazz.getType()); ps.println("# Bytecode for"); ps.println("# Class: '" + clazzName + "'"); if (writeAllClassInfo) { writeAnnotations(clazz, clazz.annotations(), ps); ps.println("# Flags: '" + clazz.accessFlags + "'"); if (clazz.superType != application.dexItemFactory.objectType) { - ps.println("# Extends: '" + clazz.superType.toSourceString() + "'"); + ps.println("# Extends: '" + retracer.toSourceString(clazz.superType) + "'"); } for (DexType value : clazz.interfaces.values) { - ps.println("# Implements: '" + value.toSourceString() + "'"); + ps.println("# Implements: '" + retracer.toSourceString(value) + "'"); } if (!clazz.getInnerClasses().isEmpty()) { ps.println("# InnerClasses:"); @@ -93,10 +92,10 @@ ps.println( "# Outer: " + (innerClassAttribute.getOuter() != null - ? innerClassAttribute.getOuter().toSourceString() + ? retracer.toSourceString(innerClassAttribute.getOuter()) : "-") + ", inner: " - + innerClassAttribute.getInner().toSourceString() + + retracer.toSourceString(innerClassAttribute.getInner()) + ", inner name: " + innerClassAttribute.getInnerName() + ", access: " @@ -107,10 +106,12 @@ if (enclosingMethodAttribute != null) { ps.println("# EnclosingMethod:"); if (enclosingMethodAttribute.getEnclosingClass() != null) { - ps.println("# Class: " + enclosingMethodAttribute.getEnclosingClass().toSourceString()); + ps.println( + "# Class: " + retracer.toSourceString(enclosingMethodAttribute.getEnclosingClass())); } else { ps.println( - "# Method: " + enclosingMethodAttribute.getEnclosingMethod().toSourceString()); + "# Method: " + + retracer.toSourceString(enclosingMethodAttribute.getEnclosingMethod())); } } } @@ -129,14 +130,9 @@ @Override void writeField(DexEncodedField field, PrintStream ps) { if (writeFields) { - ClassNameMapper naming = application.getProguardMap(); - FieldSignature fieldSignature = - naming != null - ? naming.originalSignatureOf(field.getReference()) - : FieldSignature.fromDexField(field.getReference()); writeAnnotations(null, field.annotations(), ps); ps.print(field.accessFlags + " "); - ps.print(fieldSignature); + ps.print(retracer.toSourceString(field.getReference())); if (field.isStatic() && field.hasExplicitStaticValue()) { ps.print(" = " + field.getStaticValue()); } @@ -152,15 +148,13 @@ @Override void writeMethod(ProgramMethod method, PrintStream ps) { DexEncodedMethod definition = method.getDefinition(); - ClassNameMapper naming = application.getProguardMap(); - String methodName = - naming != null - ? naming.originalSignatureOf(method.getReference()).name - : method.getReference().name.toString(); ps.println("#"); - ps.println("# Method: '" + methodName + "':"); + ps.println("# Method: '" + retracer.toSourceString(definition.getReference()) + "':"); writeAnnotations(null, definition.annotations(), ps); ps.println("# " + definition.accessFlags); + if (!retracer.isEmpty()) { + ps.println("# Residual: '" + definition.getReference().toSourceString()); + } ps.println("#"); ps.println(); if (!writeCode) { @@ -171,7 +165,7 @@ if (writeIR) { writeIR(method, ps); } else { - ps.println(code.toString(definition, naming)); + ps.println(code.toString(definition, retracer)); } } } @@ -188,7 +182,7 @@ OptimizationFeedbackIgnore.getInstance(), methodProcessor, methodProcessingContext)); - ps.println(printer.toString()); + ps.println(printer); } private void writeAnnotations( @@ -202,9 +196,19 @@ assert clazz != null : "Kotlin metadata is a class annotation"; KotlinMetadataWriter.writeKotlinMetadataAnnotation(prefix, annotation, ps, kotlin); } else { - String annotationString = annotation.toString(); + StringBuilder sb = new StringBuilder(); + sb.append(annotation.getVisibility()); + sb.append(" "); + sb.append(retracer.toSourceString(annotation.getAnnotationType())); + sb.append( + StringUtils.join( + ",", + annotation.annotation.elements, + element -> + element.getName().toString() + " = " + getStringValue(element.getValue()), + BraceType.SQUARE)); ps.print( - new BufferedReader(new StringReader(annotationString)) + new BufferedReader(new StringReader(sb.toString())) .lines() .collect( Collectors.joining( @@ -215,6 +219,26 @@ } } + private String getStringValue(DexValue value) { + if (value.isDexValueType()) { + return retracer.toSourceString(value.asDexValueType().getValue()); + } else if (value.isDexValueMethodHandle()) { + return retracer.toSourceString(value.asDexValueMethodHandle().value.asMethod()); + } else if (value.isDexValueMethod()) { + return retracer.toSourceString(value.asDexValueMethod().value); + } else if (value.isDexItemBasedValueString()) { + return retracer.toSourceString(value.asDexItemBasedValueString().value); + } else if (value.isDexValueEnum()) { + return retracer.toSourceString(value.asDexValueEnum().value); + } else if (value.isDexValueField()) { + return retracer.toSourceString(value.asDexValueField().value); + } else if (value.isDexValueArray()) { + return "[" + value.asDexValueArray() + "]"; + } else { + return value.toString(); + } + } + @Override void writeClassFooter(DexProgramClass clazz, PrintStream ps) {
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java index f415e4b..63d7d65 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -38,11 +38,11 @@ import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralItem; @@ -783,8 +783,8 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { - return new CfPrinter(this, method, naming).toString(); + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { + return new CfPrinter(this, method, retracer).toString(); } public ConstraintWithTarget computeInliningConstraint(
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java index 0edeb6f..1077fb9 100644 --- a/src/main/java/com/android/tools/r8/graph/Code.java +++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -13,8 +13,8 @@ import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.RetracerForCodePrinting; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; public abstract class Code extends CachedHashValueDexItem { @@ -71,7 +71,7 @@ @Override public abstract String toString(); - public abstract String toString(DexEncodedMethod method, ClassNameMapper naming); + public abstract String toString(DexEncodedMethod method, RetracerForCodePrinting retracer); public boolean isCfCode() { return false;
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java index 425c5d5..165ff81 100644 --- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java +++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -28,10 +28,10 @@ import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions; import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.IteratorUtils; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.HashingVisitor; import com.google.common.collect.ImmutableList; import java.nio.ShortBuffer; @@ -391,7 +391,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return toString(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java index 924590f..aff4bff 100644 --- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java +++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -101,6 +101,10 @@ return visibility + " " + annotation; } + public int getVisibility() { + return visibility; + } + public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) { annotation.collectIndexedItems(appView, indexedItems); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java index a2be262..aa6aaec 100644 --- a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java +++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
@@ -71,4 +71,7 @@ assert false; } + public DexString getName() { + return name; + } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java index ad59604..7e55f8e 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -27,9 +27,9 @@ import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.ArrayUtils; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.Equatable; import com.android.tools.r8.utils.structural.HashCodeVisitor; @@ -449,14 +449,14 @@ @Override public String toString() { - return toString(null, null); + return toString(null, RetracerForCodePrinting.empty()); } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); if (method != null) { - builder.append(method.toSourceString()).append("\n"); + builder.append(retracer.toSourceString(method.getReference())).append("\n"); } builder.append("registers: ").append(registerSize); builder.append(", inputs: ").append(incomingRegisterSize); @@ -495,9 +495,9 @@ builder.append(": "); if (insn.isSwitchPayload()) { DexInstruction payloadUser = payloadUsers.get(insn.getOffset()); - builder.append(insn.toString(naming, payloadUser)); + builder.append(insn.toString(retracer, payloadUser)); } else { - builder.append(insn.toString(naming)); + builder.append(insn.toString(retracer)); } builder.append('\n'); } @@ -542,7 +542,7 @@ return current; } - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); // Find labeled targets. Map<Integer, DexInstruction> payloadUsers = new HashMap<>(); @@ -582,7 +582,7 @@ DexInstruction payloadUser = payloadUsers.get(dex.getOffset()); builder.append(dex.toSmaliString(payloadUser)).append('\n'); } else { - builder.append(dex.toSmaliString(naming)).append('\n'); + builder.append(dex.toSmaliString(retracer)).append('\n'); } } if (tries.length > 0) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index 985e215..318b353 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -56,7 +56,6 @@ import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder; import com.android.tools.r8.kotlin.KotlinMethodLevelInfo; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.naming.MemberNaming.Signature; import com.android.tools.r8.naming.NamingLens; @@ -65,6 +64,7 @@ import com.android.tools.r8.utils.ConsumerUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.OptionalBool; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.Ordered; @@ -902,7 +902,7 @@ this.parameterAnnotationsList = parameterAnnotations; } - public String toSmaliString(ClassNameMapper naming) { + public String toSmaliString(RetracerForCodePrinting naming) { checkIfObsolete(); StringBuilder builder = new StringBuilder(); builder.append(".method "); @@ -1226,7 +1226,7 @@ public String codeToString() { checkIfObsolete(); - return code == null ? "<no code>" : code.toString(this, null); + return code == null ? "<no code>" : code.toString(this, RetracerForCodePrinting.empty()); } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java index 36c45bc..e0e2bd6 100644 --- a/src/main/java/com/android/tools/r8/graph/DexType.java +++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.references.Reference; +import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; @@ -56,6 +57,10 @@ return Reference.classFromDescriptor(toDescriptorString()); } + public TypeReference asTypeReference() { + return Reference.typeFromDescriptor(toDescriptorString()); + } + public DynamicTypeWithUpperBound toDynamicType(AppView<AppInfoWithLiveness> appView) { return toDynamicType(appView, Nullability.maybeNull()); }
diff --git a/src/main/java/com/android/tools/r8/graph/InvalidCode.java b/src/main/java/com/android/tools/r8/graph/InvalidCode.java index 2b9de26..b3016c9 100644 --- a/src/main/java/com/android/tools/r8/graph/InvalidCode.java +++ b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
@@ -6,8 +6,8 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.RetracerForCodePrinting; public class InvalidCode extends Code { @@ -48,7 +48,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { StringBuilder builder = new StringBuilder(); if (method != null) { builder.append(method.toSourceString()).append("\n");
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java index d18e823..3ab128b 100644 --- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java +++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -63,7 +63,6 @@ import com.android.tools.r8.ir.code.Position.SourcePosition; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.MethodPosition; import com.android.tools.r8.position.TextPosition; @@ -74,6 +73,7 @@ import com.android.tools.r8.utils.ExceptionUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Reporter; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringDiagnostic; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; @@ -295,8 +295,8 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { - return asCfCode().toString(method, naming); + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { + return asCfCode().toString(method, retracer); } protected BiFunction<String, String, LazyCfCode> createCodeLocator(ReparseContext context) {
diff --git a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java index edf7d58..0eff9d1 100644 --- a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java +++ b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.Timing; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -31,7 +32,7 @@ } catch (IOException e) { throw new CompilationError("Failed to generate smali sting", e); } - return new String(os.toByteArray(), StandardCharsets.UTF_8); + return os.toString(StandardCharsets.UTF_8); } public static String getFileEnding() { @@ -67,7 +68,12 @@ @Override void writeMethod(ProgramMethod method, PrintStream ps) { ps.append("\n"); - ps.append(method.getDefinition().toSmaliString(application.getProguardMap())); + ps.append( + method + .getDefinition() + .toSmaliString( + RetracerForCodePrinting.create( + application.getProguardMap(), application.options.reporter))); ps.append("\n"); }
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java index 789bb19..dc18cfe 100644 --- a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java +++ b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
@@ -19,8 +19,8 @@ import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.HashingVisitor; import java.nio.ShortBuffer; import java.util.Objects; @@ -237,7 +237,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return "ThrowExceptionCode"; } }
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java index cac6f7a..661cefb 100644 --- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java +++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -22,9 +22,9 @@ import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions; import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.structural.HashingVisitor; import com.google.common.collect.ImmutableList; import java.nio.ShortBuffer; @@ -266,7 +266,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return "ThrowNullCode"; }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java index c9c8e34..abb1fbf 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
@@ -15,8 +15,8 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.RetracerForCodePrinting; public abstract class IncompleteHorizontalClassMergerCode extends Code { @@ -80,7 +80,7 @@ public abstract String toString(); @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return toString(); } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java index 2e6cc06..6a030d7 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -35,11 +35,11 @@ import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.CfVersionUtils; import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.ListUtils; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -311,7 +311,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { throw new Unreachable(); } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java index 30c3b67..2a543a8 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.DexValue.DexValueNull; import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound; @@ -29,6 +30,7 @@ import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeDirect; +import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.NewArrayEmpty; import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.Value; @@ -37,6 +39,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.Timing; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; public class StaticFieldValueAnalysis extends FieldValueAnalysis { @@ -210,7 +213,7 @@ if (value.isPhi()) { return null; } - if (value.definition.isNewArrayEmpty()) { + if (value.definition.isNewArrayEmptyOrInvokeNewArray()) { return computeSingleEnumFieldValueForValuesArray(value); } if (value.definition.isNewInstance()) { @@ -220,7 +223,7 @@ } private SingleFieldValue computeSingleEnumFieldValueForValuesArray(Value value) { - if (!value.definition.isNewArrayEmpty()) { + if (!value.definition.isNewArrayEmptyOrInvokeNewArray()) { return null; } AbstractValue valuesValue = computedValues.get(value); @@ -241,26 +244,38 @@ } private SingleFieldValue internalComputeSingleEnumFieldValueForValuesArray(Value value) { - assert value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty); - NewArrayEmpty newArrayEmpty = value.definition.asNewArrayEmpty(); - if (newArrayEmpty.type.toBaseType(appView.dexItemFactory()) != context.getHolder().type) { + InvokeNewArray invokeNewArray = value.definition.asInvokeNewArray(); + assert newArrayEmpty != null || invokeNewArray != null; + + DexType arrayType = newArrayEmpty != null ? newArrayEmpty.type : invokeNewArray.getArrayType(); + if (arrayType.toBaseType(appView.dexItemFactory()) != context.getHolder().type) { return null; } if (value.hasDebugUsers() || value.hasPhiUsers()) { return null; } - if (!newArrayEmpty.size().isConstNumber()) { - return null; - } - int valuesSize = newArrayEmpty.size().getConstInstruction().asConstNumber().getIntValue(); - if (valuesSize == 0) { - // No need to compute the state of an empty array. + int valuesSize = newArrayEmpty != null ? newArrayEmpty.sizeIfConst() : invokeNewArray.size(); + if (valuesSize < 1) { + // Array is empty or non-const size. return null; } ObjectState[] valuesState = new ObjectState[valuesSize]; + + if (invokeNewArray != null) { + // Populate array values from filled-new-array values. + List<Value> inValues = invokeNewArray.inValues(); + for (int i = 0; i < valuesSize; ++i) { + if (!updateEnumValueState(valuesState, i, inValues.get(i))) { + return null; + } + } + } + + // Populate / update array values from aput-object instructions, and find the static-put + // instruction. DexEncodedField valuesField = null; for (Instruction user : value.aliasedUsers()) { switch (user.opcode()) { @@ -276,18 +291,9 @@ if (index < 0 || index >= valuesSize) { return null; } - ObjectState objectState = computeEnumInstanceObjectState(arrayPut.value()); - if (objectState == null || objectState.isEmpty()) { - // We need the state of all fields for the analysis to be valuable. + if (!updateEnumValueState(valuesState, index, arrayPut.value())) { return null; } - if (!valuesArrayIndexMatchesOrdinal(index, objectState)) { - return null; - } - if (valuesState[index] != null) { - return null; - } - valuesState[index] = objectState; break; case ASSUME: @@ -328,24 +334,34 @@ .createSingleFieldValue(valuesField.getReference(), new EnumValuesObjectState(valuesState)); } - private ObjectState computeEnumInstanceObjectState(Value value) { + private boolean updateEnumValueState(ObjectState[] valuesState, int index, Value value) { Value root = value.getAliasedValue(); if (root.isPhi()) { - return ObjectState.empty(); + return false; } Instruction definition = root.getDefinition(); - if (definition.isNewInstance()) { - return computeObjectState(definition.outValue()); - } if (definition.isStaticGet()) { // Enums with many instance rely on staticGets to set the $VALUES data instead of directly // keeping the values in registers, due to the max capacity of the redundant field load // elimination. The capacity has already been increased, so that this case is extremely // uncommon (very large enums). // TODO(b/169050248): We could consider analysing these to answer the object state here. - return ObjectState.empty(); + return false; } - return ObjectState.empty(); + ObjectState objectState = + definition.isNewInstance() ? computeObjectState(definition.outValue()) : null; + if (objectState == null || objectState.isEmpty()) { + // We need the state of all fields for the analysis to be valuable. + return false; + } + if (!valuesArrayIndexMatchesOrdinal(index, objectState)) { + return false; + } + if (valuesState[index] != null) { + return false; + } + valuesState[index] = objectState; + return true; } private boolean valuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java index 74a3fb5..b4b2359 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -33,6 +33,7 @@ import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; +import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.code.NewArrayEmpty; import com.android.tools.r8.ir.code.NewInstance; @@ -46,6 +47,7 @@ import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -316,17 +318,37 @@ Value sizeValue = instructionIterator.insertConstIntInstruction(code, appView.options(), objects.size()); Value newObjectsValue = code.createValue(objectArrayType); - instructionIterator.add( - new NewArrayEmpty(newObjectsValue, sizeValue, appView.dexItemFactory().objectArrayType)); // Populate the `objects` array. - for (int i = 0; i < objects.size(); i++) { - Value indexValue = instructionIterator.insertConstIntInstruction(code, appView.options(), i); - Instruction materializingInstruction = objects.get(i).buildIR(appView, code); - instructionIterator.add(materializingInstruction); + var rewriteOptions = appView.options().rewriteArrayOptions(); + if (rewriteOptions.canUseFilledNewArrayOfObjects() + && objects.size() < rewriteOptions.maxRangeInputs) { + List<Value> arrayValues = new ArrayList<>(objects.size()); + for (int i = 0; i < objects.size(); i++) { + Instruction materializingInstruction = objects.get(i).buildIR(appView, code); + instructionIterator.add(materializingInstruction); + arrayValues.add(materializingInstruction.outValue()); + } instructionIterator.add( - new ArrayPut( - MemberType.OBJECT, newObjectsValue, indexValue, materializingInstruction.outValue())); + new InvokeNewArray( + appView.dexItemFactory().objectArrayType, newObjectsValue, arrayValues)); + } else { + instructionIterator.add( + new NewArrayEmpty(newObjectsValue, sizeValue, appView.dexItemFactory().objectArrayType)); + + // Populate the `objects` array. + for (int i = 0; i < objects.size(); i++) { + Value indexValue = + instructionIterator.insertConstIntInstruction(code, appView.options(), i); + Instruction materializingInstruction = objects.get(i).buildIR(appView, code); + instructionIterator.add(materializingInstruction); + instructionIterator.add( + new ArrayPut( + MemberType.OBJECT, + newObjectsValue, + indexValue, + materializingInstruction.outValue())); + } } // Pass the newly created `objects` array to RawMessageInfo.<init>(...) or
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java index fbe3ff5..c25c456 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -31,6 +31,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionIterator; import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.NewArrayEmpty; import com.android.tools.r8.ir.code.StaticGet; @@ -302,18 +303,30 @@ */ private static ThrowingIterator<Value, InvalidRawMessageInfoException> createObjectIterator( Value objectsValue) throws InvalidRawMessageInfoException { - if (objectsValue.isPhi() || !objectsValue.definition.isNewArrayEmpty()) { + if (objectsValue.isPhi()) { throw new InvalidRawMessageInfoException(); } NewArrayEmpty newArrayEmpty = objectsValue.definition.asNewArrayEmpty(); - int expectedArraySize = objectsValue.uniqueUsers().size() - 1; + InvokeNewArray invokeNewArray = objectsValue.definition.asInvokeNewArray(); - // Verify that the size is correct. + if (newArrayEmpty == null && invokeNewArray == null) { + throw new InvalidRawMessageInfoException(); + } + // Verify that the array is used in only one spot. + if (invokeNewArray != null) { + if (objectsValue.uniqueUsers().size() != 1) { + throw new InvalidRawMessageInfoException(); + } + return ThrowingIterator.fromIterator(invokeNewArray.inValues().iterator()); + } + Value sizeValue = newArrayEmpty.size().getAliasedValue(); - if (sizeValue.isPhi() - || !sizeValue.definition.isConstNumber() - || sizeValue.definition.asConstNumber().getIntValue() != expectedArraySize) { + if (sizeValue.isPhi() || !sizeValue.definition.isConstNumber()) { + throw new InvalidRawMessageInfoException(); + } + int arraySize = sizeValue.definition.asConstNumber().getIntValue(); + if (arraySize != objectsValue.uniqueUsers().size() - 1) { throw new InvalidRawMessageInfoException(); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java index e06cdbee..99f92eb 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeUtils.java
@@ -73,20 +73,25 @@ * <p>Use with caution! */ public static void removeArrayAndTransitiveInputsIfNotUsed(IRCode code, Instruction definition) { - Deque<InstructionOrPhi> worklist = new ArrayDeque<>(); if (definition.isConstNumber()) { // No need to explicitly remove `null`, it will be removed by ordinary dead code elimination // anyway. assert definition.asConstNumber().isZero(); return; } - - if (definition.isNewArrayEmpty()) { - Value arrayValue = definition.outValue(); - if (arrayValue.hasPhiUsers() || arrayValue.hasDebugUsers()) { - return; - } - + Value arrayValue = definition.outValue(); + if (arrayValue.hasPhiUsers() || arrayValue.hasDebugUsers()) { + return; + } + if (!definition.isNewArrayEmptyOrInvokeNewArray()) { + assert false; + return; + } + Deque<InstructionOrPhi> worklist = new ArrayDeque<>(); + InvokeNewArray invokeNewArray = definition.asInvokeNewArray(); + if (invokeNewArray != null) { + worklist.add(definition); + } else if (definition.isNewArrayEmpty()) { for (Instruction user : arrayValue.uniqueUsers()) { // If we encounter an Assume instruction here, we also need to consider indirect users. assert !user.isAssume(); @@ -95,11 +100,10 @@ } worklist.add(user); } - internalRemoveInstructionAndTransitiveInputsIfNotUsed(code, worklist); - return; + } else { + assert false; } - - assert false; + internalRemoveInstructionAndTransitiveInputsIfNotUsed(code, worklist); } /**
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 c99ee11..8892da0 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
@@ -1038,6 +1038,10 @@ return false; } + public boolean isNewArrayEmptyOrInvokeNewArray() { + return isNewArrayEmpty() || isInvokeNewArray(); + } + public NewArrayEmpty asNewArrayEmpty() { return null; }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java index a2e815f..f0715b5 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -221,4 +221,9 @@ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) { registry.registerTypeReference(type); } + + // Returns the number of elements in the array. + public int size() { + return inValues.size(); + } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java index c6d527f..a829247 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -49,10 +49,6 @@ return super.toString() + " " + type.toString(); } - public Value dest() { - return outValue; - } - public Value size() { return inValues.get(0); } @@ -60,7 +56,7 @@ @Override public void buildDex(DexBuilder builder) { int size = builder.allocatedRegister(size(), getNumber()); - int dest = builder.allocatedRegister(dest(), getNumber()); + int dest = builder.allocatedRegister(outValue, getNumber()); builder.add(this, new DexNewArray(dest, size, type)); } @@ -171,4 +167,10 @@ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) { registry.registerTypeReference(type); } + + // Returns the size of the array if it is known, -1 otherwise. + public int sizeIfConst() { + Value size = size(); + return size.isConstNumber() ? size.getConstInstruction().asConstNumber().getIntValue() : -1; + } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java index 178255b..40f9d67 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -170,7 +170,9 @@ private DexMethod ensureApiGenericConversion( DexMethod conversion, DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) { - assert !appView.options().isDesugaredLibraryCompilation(); + if (appView.appInfoForDesugaring().resolveMethod(conversion, false).isSingleResolution()) { + return conversion; + } ClasspathMethod classpathMethod = appView .getSyntheticItems()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java index 7a669c2..ae3214b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -14,6 +14,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; @@ -29,12 +31,14 @@ private final Set<DexString> descriptorDontRewritePrefix; private final Map<DexString, Map<DexString, DexString>> descriptorDifferentPrefix; private final Set<DexString> usedPrefix = Sets.newIdentityHashSet(); + private final boolean jdk11Legacy; public HumanToMachinePrefixConverter( AppInfoWithClassHierarchy appInfo, MachineRewritingFlags.Builder builder, String synthesizedPrefix, boolean libraryCompilation, + String identifier, HumanRewritingFlags rewritingFlags) { this.appInfo = appInfo; this.builder = builder; @@ -45,6 +49,7 @@ this.descriptorMaintainPrefix = convertPrefixSet(rewritingFlags.getMaintainPrefix()); this.descriptorDifferentPrefix = convertRewriteDifferentPrefix(rewritingFlags.getRewriteDerivedPrefix()); + this.jdk11Legacy = identifier.startsWith("com.tools.android:desugar_jdk_libs:1.2."); } public void convertPrefixFlags( @@ -63,12 +68,27 @@ } private void warnIfUnusedPrefix(BiConsumer<String, Set<DexString>> warnConsumer) { - Set<DexString> prefixes = Sets.newIdentityHashSet(); - prefixes.addAll(descriptorPrefix.keySet()); - prefixes.addAll(descriptorMaintainPrefix); - prefixes.addAll(descriptorDifferentPrefix.keySet()); - prefixes.removeAll(usedPrefix); - warnConsumer.accept("The following prefixes do not match any type: ", prefixes); + Set<DexString> unused = Sets.newIdentityHashSet(); + unused.addAll(descriptorPrefix.keySet()); + unused.addAll(descriptorMaintainPrefix); + unused.addAll(descriptorDifferentPrefix.keySet()); + unused.removeAll(usedPrefix); + if (jdk11Legacy) { + // We have to allow duplicate because of the jdk11 legacy configuration, since there is no + // api_greater_or_equal in legacy, we're bound to have duplicates. + // If we have x.y. -> w.z and x. -> w., then if one is used the other is used. + List<DexString> duplicates = new ArrayList<>(); + for (DexString prefix : unused) { + descriptorPrefix.forEach( + (k, v) -> { + if (!unused.contains(k) && (k.startsWith(prefix) || prefix.startsWith(k))) { + duplicates.add(prefix); + } + }); + } + duplicates.forEach(unused::remove); + } + warnConsumer.accept("The following prefixes do not match any type: ", unused); } public DexType convertJavaNameToDesugaredLibrary(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java index 2773de6..48f3aaa 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -57,12 +57,13 @@ MachineTopLevelFlags machineTopLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags()); String synthesizedPrefix = machineTopLevelFlags.getSynthesizedLibraryClassesPackagePrefix(); + String identifier = machineTopLevelFlags.getIdentifier(); Map<ApiLevelRange, MachineRewritingFlags> commonFlags = - convertRewritingFlagMap(humanSpec.getCommonFlags(), synthesizedPrefix, true); + convertRewritingFlagMap(humanSpec.getCommonFlags(), synthesizedPrefix, true, identifier); Map<ApiLevelRange, MachineRewritingFlags> programFlags = - convertRewritingFlagMap(humanSpec.getProgramFlags(), synthesizedPrefix, false); + convertRewritingFlagMap(humanSpec.getProgramFlags(), synthesizedPrefix, false, identifier); Map<ApiLevelRange, MachineRewritingFlags> libraryFlags = - convertRewritingFlagMap(humanSpec.getLibraryFlags(), synthesizedPrefix, true); + convertRewritingFlagMap(humanSpec.getLibraryFlags(), synthesizedPrefix, true, identifier); MultiAPILevelMachineDesugaredLibrarySpecification machineSpec = new MultiAPILevelMachineDesugaredLibrarySpecification( @@ -74,13 +75,15 @@ private Map<ApiLevelRange, MachineRewritingFlags> convertRewritingFlagMap( Map<ApiLevelRange, HumanRewritingFlags> libFlags, String synthesizedPrefix, - boolean interpretAsLibraryCompilation) { + boolean interpretAsLibraryCompilation, + String identifier) { Map<ApiLevelRange, MachineRewritingFlags> map = new HashMap<>(); libFlags.forEach( (range, flags) -> map.put( range, - convertRewritingFlags(flags, synthesizedPrefix, interpretAsLibraryCompilation))); + convertRewritingFlags( + flags, synthesizedPrefix, interpretAsLibraryCompilation, identifier))); return map; } @@ -99,7 +102,8 @@ convertRewritingFlags( humanSpec.getRewritingFlags(), humanSpec.getSynthesizedLibraryClassesPackagePrefix(), - humanSpec.isLibraryCompilation()); + humanSpec.isLibraryCompilation(), + humanSpec.getIdentifier()); MachineTopLevelFlags topLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags()); timing.end(); return new MachineDesugaredLibrarySpecification( @@ -117,7 +121,10 @@ } private MachineRewritingFlags convertRewritingFlags( - HumanRewritingFlags rewritingFlags, String synthesizedPrefix, boolean libraryCompilation) { + HumanRewritingFlags rewritingFlags, + String synthesizedPrefix, + boolean libraryCompilation, + String identifier) { timing.begin("convert rewriting flags"); MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder(); DesugaredLibraryAmender.run( @@ -135,7 +142,7 @@ new HumanToMachineEmulatedInterfaceConverter(appInfo) .convertEmulatedInterfaces(rewritingFlags, appInfo, builder, this::warnMissingReferences); new HumanToMachinePrefixConverter( - appInfo, builder, synthesizedPrefix, libraryCompilation, rewritingFlags) + appInfo, builder, synthesizedPrefix, libraryCompilation, identifier, rewritingFlags) .convertPrefixFlags(rewritingFlags, this::warnMissingDexString); new HumanToMachineWrapperConverter(appInfo) .convertWrappers(rewritingFlags, builder, this::warnMissingReferences);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java index c6ea5b0..2acd508 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -27,7 +27,7 @@ } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.R)) { levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;"); } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.N)) { - levelType = app.dexItemFactory.createType("Ljava/util/function/Supplier;"); + levelType = app.dexItemFactory.createType("Ljava/util/StringJoiner;"); } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.T)) { levelType = app.dexItemFactory.createType("Ljava/lang/invoke/VarHandle;"); } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java index 6314197..1d4e435 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.algorithms.scc.SCC; import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; -import com.android.tools.r8.dex.Constants; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AccessControl; @@ -55,7 +54,6 @@ import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler; import com.android.tools.r8.ir.code.CheckCast; import com.android.tools.r8.ir.code.ConstClass; -import com.android.tools.r8.ir.code.ConstInstruction; import com.android.tools.r8.ir.code.ConstNumber; import com.android.tools.r8.ir.code.ConstString; import com.android.tools.r8.ir.code.DebugLocalWrite; @@ -84,6 +82,7 @@ import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.InvokeVirtual; +import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator; import com.android.tools.r8.ir.code.Move; import com.android.tools.r8.ir.code.NewArrayEmpty; import com.android.tools.r8.ir.code.NewArrayFilledData; @@ -138,6 +137,7 @@ import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.Collections; @@ -162,7 +162,6 @@ FALSE } - private static final int MAX_FILL_ARRAY_SIZE = 8 * Constants.KILOBYTE; // This constant was determined by experimentation. private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50; @@ -2100,16 +2099,21 @@ } } - private short[] computeArrayFilledData(ConstInstruction[] values, int size, int elementSize) { - if (values == null) { - return null; + private short[] computeArrayFilledData(Value[] values, int size, int elementSize) { + for (Value v : values) { + if (!v.isConstant()) { + return null; + } } if (elementSize == 1) { short[] result = new short[(size + 1) / 2]; for (int i = 0; i < size; i += 2) { - short value = (short) (values[i].asConstNumber().getIntValue() & 0xFF); + short value = + (short) (values[i].getConstInstruction().asConstNumber().getIntValue() & 0xFF); if (i + 1 < size) { - value |= (short) ((values[i + 1].asConstNumber().getIntValue() & 0xFF) << 8); + value |= + (short) + ((values[i + 1].getConstInstruction().asConstNumber().getIntValue() & 0xFF) << 8); } result[i / 2] = value; } @@ -2119,7 +2123,7 @@ int shortsPerConstant = elementSize / 2; short[] result = new short[size * shortsPerConstant]; for (int i = 0; i < size; i++) { - long value = values[i].asConstNumber().getRawValue(); + long value = values[i].getConstInstruction().asConstNumber().getRawValue(); for (int part = 0; part < shortsPerConstant; part++) { result[i * shortsPerConstant + part] = (short) ((value >> (16 * part)) & 0xFFFFL); } @@ -2127,40 +2131,45 @@ return result; } - private ConstInstruction[] computeConstantArrayValues( - NewArrayEmpty newArray, BasicBlock block, int size) { - if (size > MAX_FILL_ARRAY_SIZE) { - return null; - } - ConstInstruction[] values = new ConstInstruction[size]; + private Value[] computeArrayValues(LinearFlowInstructionListIterator it, int size) { + NewArrayEmpty newArrayEmpty = it.next().asNewArrayEmpty(); + assert newArrayEmpty != null; + Value arrayValue = newArrayEmpty.outValue(); + + // aput-object allows any object for arrays of interfaces, but new-filled-array fails to verify + // if types require a cast. + // TODO(b/246971330): Check if adding a checked-cast would have the same observable result. E.g. + // if aput-object throws a ClassCastException if given an object that does not implement the + // desired interface, then we could add check-cast instructions for arguments we're not sure + // about. + DexType elementType = newArrayEmpty.type.toDimensionMinusOneType(dexItemFactory); + boolean needsTypeCheck = + !elementType.isPrimitiveType() && elementType != dexItemFactory.objectType; + + Value[] values = new Value[size]; int remaining = size; - Set<Instruction> users = newArray.outValue().uniqueUsers(); - Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet(); - // We allow the array instantiations to cross block boundaries as long as it hasn't encountered - // an instruction instance that can throw an exception. - InstructionIterator it = block.iterator(); - it.nextUntil(i -> i == newArray); - do { - visitedBlocks.add(block); + Set<Instruction> users = newArrayEmpty.outValue().uniqueUsers(); while (it.hasNext()) { Instruction instruction = it.next(); - // If we encounter an instruction that can throw an exception we need to bail out of the - // optimization so that we do not transform half-initialized arrays into fully initialized - // arrays on exceptional edges. If the block has no handlers it is not observable so - // we perform the rewriting. - if (block.hasCatchHandlers() && instruction.instructionInstanceCanThrow()) { + // If we encounter an instruction that can throw an exception we need to bail out of the + // optimization so that we do not transform half-initialized arrays into fully initialized + // arrays on exceptional edges. If the block has no handlers it is not observable so + // we perform the rewriting. + // TODO(b/246971330): Allow simplification when all users of the array are in the same + // try/catch. + if (instruction.getBlock().hasCatchHandlers() && instruction.instructionInstanceCanThrow()) { return null; } if (!users.contains(instruction)) { continue; } - // If the initialization sequence is broken by another use we cannot use a - // fill-array-data instruction. - if (!instruction.isArrayPut()) { + ArrayPut arrayPut = instruction.asArrayPut(); + // If the initialization sequence is broken by another use we cannot use a fill-array-data + // instruction. + if (arrayPut == null || arrayPut.array() != arrayValue) { return null; } - ArrayPut arrayPut = instruction.asArrayPut(); - if (!(arrayPut.value().isConstant() && arrayPut.index().isConstNumber())) { + if (!arrayPut.index().isConstNumber()) { return null; } int index = arrayPut.index().getConstInstruction().asConstNumber().getIntValue(); @@ -2170,40 +2179,38 @@ if (values[index] != null) { return null; } - ConstInstruction value = arrayPut.value().getConstInstruction(); + Value value = arrayPut.value(); + if (needsTypeCheck && !value.isAlwaysNull(appView)) { + DexType valueDexType = value.getType().asReferenceType().toDexType(dexItemFactory); + if (elementType.isArrayType()) { + if (elementType != valueDexType) { + return null; + } + } else if (valueDexType.isArrayType()) { + // isSubtype asserts for this case. + return null; + } else if (valueDexType.isNullValueType()) { + // Assume instructions can cause value.isAlwaysNull() == false while the DexType is + // null. + // TODO(b/246971330): Figure out how to write a test in SimplifyArrayConstructionTest + // that hits this case. + } else { + // TODO(b/246971330): When in d8 mode, we might still be able to see if this is true for + // library types (which this helper does not do). + if (appView.isSubtype(valueDexType, elementType).isPossiblyFalse()) { + return null; + } + } + } values[index] = value; --remaining; if (remaining == 0) { return values; } } - BasicBlock nextBlock = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null; - block = nextBlock != null && !visitedBlocks.contains(nextBlock) ? nextBlock : null; - it = block != null ? block.iterator() : null; - } while (it != null); return null; } - private boolean allowNewFilledArrayConstruction(Instruction instruction) { - if (!(instruction instanceof NewArrayEmpty)) { - return false; - } - NewArrayEmpty newArray = instruction.asNewArrayEmpty(); - if (!newArray.size().isConstant()) { - return false; - } - assert newArray.size().isConstNumber(); - int size = newArray.size().getConstInstruction().asConstNumber().getIntValue(); - if (size < 1) { - return false; - } - if (newArray.type.isPrimitiveArrayType()) { - return true; - } - return newArray.type == dexItemFactory.stringArrayType - && options.canUseFilledNewArrayOfObjects(); - } - /** * Replace new-array followed by stores of constants to all entries with new-array * and fill-array-data / filled-new-array. @@ -2212,6 +2219,11 @@ if (options.isGeneratingClassFiles()) { return; } + InternalOptions.RewriteArrayOptions rewriteOptions = options.rewriteArrayOptions(); + boolean canUseForStrings = rewriteOptions.canUseFilledNewArrayOfStrings(); + boolean canUseForObjects = rewriteOptions.canUseFilledNewArrayOfObjects(); + boolean canUseForArrays = rewriteOptions.canUseFilledNewArrayOfArrays(); + for (BasicBlock block : code.blocks) { // Map from the array value to the number of array put instruction to remove for that value. Map<Value, Instruction> instructionToInsertForArray = new HashMap<>(); @@ -2220,31 +2232,56 @@ InstructionListIterator it = block.listIterator(code); while (it.hasNext()) { Instruction instruction = it.next(); - if (instruction.getLocalInfo() != null || !allowNewFilledArrayConstruction(instruction)) { + NewArrayEmpty newArrayEmpty = instruction.asNewArrayEmpty(); + if (newArrayEmpty == null || !newArrayEmpty.size().isConstant()) { continue; } - NewArrayEmpty newArray = instruction.asNewArrayEmpty(); - int size = newArray.size().getConstInstruction().asConstNumber().getIntValue(); - ConstInstruction[] values = computeConstantArrayValues(newArray, block, size); + if (instruction.getLocalInfo() != null) { + continue; + } + int size = newArrayEmpty.size().getConstInstruction().asConstNumber().getIntValue(); + if (size < 1 || size > rewriteOptions.maxFillArrayDataInputs) { + continue; + } + DexType arrayType = newArrayEmpty.type; + if (!arrayType.isPrimitiveArrayType()) { + if (arrayType == dexItemFactory.stringArrayType) { + if (!canUseForStrings) { + continue; + } + } else if (!canUseForObjects) { + continue; + } else if (!canUseForArrays && arrayType.getNumberOfLeadingSquareBrackets() > 1) { + continue; + } + } + + Value[] values = + computeArrayValues( + new LinearFlowInstructionListIterator(code, block, it.previousIndex()), size); if (values == null) { continue; } - if (newArray.type == dexItemFactory.stringArrayType) { + // filled-new-array is implemented only for int[] and Object[]. + // For int[], using filled-new-array is usually smaller than filled-array-data. + // filled-new-array supports up to 5 registers before it's filled-new-array/range. + if (!arrayType.isPrimitiveArrayType() + || (arrayType == dexItemFactory.intArrayType && size <= 5)) { // Don't replace with filled-new-array if it requires more than 200 consecutive registers. - if (size > 200) { + if (size > rewriteOptions.maxRangeInputs) { continue; } - List<Value> stringValues = new ArrayList<>(size); - for (ConstInstruction value : values) { - stringValues.add(value.outValue()); + // block.hasCatchHandlers() is fine here since new-array-filled replaces new-array-empty + // and computeArrayValues already checks that no throwing instructions exist between the + // original new-array-empty and the final aput-object (where the new-array-filled will be + // positioned). + Value invokeValue = + code.createValue(newArrayEmpty.getOutType(), newArrayEmpty.getLocalInfo()); + InvokeNewArray invoke = new InvokeNewArray(arrayType, invokeValue, Arrays.asList(values)); + for (Value value : newArrayEmpty.inValues()) { + value.removeUser(newArrayEmpty); } - Value invokeValue = code.createValue(newArray.getOutType(), newArray.getLocalInfo()); - InvokeNewArray invoke = - new InvokeNewArray(dexItemFactory.stringArrayType, invokeValue, stringValues); - for (Value value : newArray.inValues()) { - value.removeUser(newArray); - } - newArray.outValue().replaceUsers(invokeValue); + newArrayEmpty.outValue().replaceUsers(invokeValue); it.removeOrReplaceByDebugLocalRead(); instructionToInsertForArray.put(invokeValue, invoke); storesToRemoveForArray.put(invokeValue, size); @@ -2254,26 +2291,33 @@ if (size == 1) { continue; } - int elementSize = newArray.type.elementSizeForPrimitiveArrayType(); + // TODO(b/246971330): Allow simplification when all users of the array are in the same + // try/catch. + if (block.hasCatchHandlers()) { + // NewArrayFilledData can throw, so creating one as done below would add a second + // throwing instruction to the same block (the first one being NewArrayEmpty). + continue; + } + int elementSize = arrayType.elementSizeForPrimitiveArrayType(); short[] contents = computeArrayFilledData(values, size, elementSize); if (contents == null) { continue; } - if (block.hasCatchHandlers()) { - continue; - } - int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue(); + int arraySize = newArrayEmpty.size().getConstInstruction().asConstNumber().getIntValue(); + // fill-array-data requires the new-array-empty instruction to remain, as it does not + // itself create an array. NewArrayFilledData fillArray = - new NewArrayFilledData(newArray.outValue(), elementSize, arraySize, contents); - fillArray.setPosition(newArray.getPosition()); + new NewArrayFilledData(newArrayEmpty.outValue(), elementSize, arraySize, contents); + fillArray.setPosition(newArrayEmpty.getPosition()); it.add(fillArray); - storesToRemoveForArray.put(newArray.outValue(), size); + storesToRemoveForArray.put(newArrayEmpty.outValue(), size); } } // Second pass: remove all the array put instructions for the array for which we have // inserted a fill array data instruction instead. if (!storesToRemoveForArray.isEmpty()) { Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet(); + int numInstructionsInserted = 0; do { visitedBlocks.add(block); it = block.listIterator(code); @@ -2295,6 +2339,7 @@ // last removed put at which point we are now adding the construction. construction.setPosition(instruction.getPosition()); it.add(construction); + numInstructionsInserted += 1; } } } @@ -2303,6 +2348,7 @@ BasicBlock nextBlock = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null; block = nextBlock != null && !visitedBlocks.contains(nextBlock) ? nextBlock : null; } while (block != null); + assert numInstructionsInserted == instructionToInsertForArray.size(); } } assert code.isConsistentSSA(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java index 2cab3fb..4a99fb4 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -66,12 +66,12 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; import com.android.tools.r8.ir.optimize.outliner.OutlineCollection; import com.android.tools.r8.ir.optimize.outliner.Outliner; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOptions.OutlineOptions; import com.android.tools.r8.utils.ListUtils; +import com.android.tools.r8.utils.RetracerForCodePrinting; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.StringUtils.BraceType; import com.android.tools.r8.utils.ThreadUtils; @@ -1830,7 +1830,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return null; } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java index a649af6..6fb553c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -18,6 +18,7 @@ import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL; @@ -68,6 +69,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeCustom; import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.InvokeVirtual; import com.android.tools.r8.ir.code.MemberType; @@ -1060,6 +1062,10 @@ instruction.asInstancePut(), code, context, enumClass, enumValue); case INVOKE_DIRECT: case INVOKE_INTERFACE: + return analyzeInvokeUser(instruction.asInvokeMethod(), code, context, enumClass, enumValue); + case INVOKE_NEW_ARRAY: + return analyzeInvokeNewArrayUser( + instruction.asInvokeNewArray(), code, context, enumClass, enumValue); case INVOKE_STATIC: case INVOKE_SUPER: case INVOKE_VIRTUAL: @@ -1130,6 +1136,40 @@ return Reason.INVALID_ARRAY_PUT; } + private Reason analyzeInvokeNewArrayUser( + InvokeNewArray invokeNewArray, + IRCode code, + ProgramMethod context, + DexProgramClass enumClass, + Value enumValue) { + // MyEnum[] array = new MyEnum[] { MyEnum.A }; is valid. + // We need to prove that the value to put in and the array have correct types. + TypeElement arrayType = invokeNewArray.getOutType(); + assert arrayType.isArrayType(); + + ClassTypeElement arrayBaseType = arrayType.asArrayType().getBaseType().asClassType(); + if (arrayBaseType == null) { + assert false; + return Reason.INVALID_INVOKE_NEW_ARRAY; + } + if (arrayBaseType.getClassType() != enumClass.type) { + return Reason.INVALID_INVOKE_NEW_ARRAY; + } + + for (Value value : invokeNewArray.inValues()) { + TypeElement valueBaseType = value.getType(); + if (valueBaseType.isArrayType()) { + assert valueBaseType.asArrayType().getBaseType().isClassType(); + assert valueBaseType.asArrayType().getNesting() == arrayType.asArrayType().getNesting() - 1; + valueBaseType = valueBaseType.asArrayType().getBaseType(); + } + if (!arrayBaseType.equalUpToNullability(valueBaseType)) { + return Reason.INVALID_INVOKE_NEW_ARRAY; + } + } + return Reason.ELIGIBLE; + } + private Reason analyzeCheckCastUser( CheckCast checkCast, IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java index 6e187ae..4e7801c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
@@ -19,9 +19,9 @@ import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.IteratorUtils; +import com.android.tools.r8.utils.RetracerForCodePrinting; /** * A special code object used by enum unboxing that supplies IR from an existing method @@ -128,7 +128,7 @@ } @Override - public String toString(DexEncodedMethod method, ClassNameMapper naming) { + public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return toString(); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java index 3e17a0a..7cb0290 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -32,6 +32,8 @@ new StringReason("IMPLICIT_UP_CAST_IN_RETURN"); public static final Reason INVALID_FIELD_PUT = new StringReason("INVALID_FIELD_PUT"); public static final Reason INVALID_ARRAY_PUT = new StringReason("INVALID_ARRAY_PUT"); + public static final Reason INVALID_INVOKE_NEW_ARRAY = + new StringReason("INVALID_INVOKE_NEW_ARRAY"); public static final Reason TYPE_MISMATCH_FIELD_PUT = new StringReason("TYPE_MISMATCH_FIELD_PUT"); public static final Reason INVALID_IF_TYPES = new StringReason("INVALID_IF_TYPES"); public static final Reason ASSIGNMENT_OUTSIDE_INIT = new StringReason("ASSIGNMENT_OUTSIDE_INIT");
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java index 8c28b9e..ad0b54c 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -21,8 +21,8 @@ import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions; import com.android.tools.r8.ir.conversion.SourceCode; -import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.RetracerForCodePrinting; import java.util.function.Consumer; public abstract class AbstractSynthesizedCode extends Code { @@ -73,7 +73,7 @@ @Override public final String toString() { - return toString(null, null); + return toString(null, RetracerForCodePrinting.empty()); } @Override @@ -101,7 +101,7 @@ } @Override - public final String toString(DexEncodedMethod method, ClassNameMapper naming) { + public final String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) { return this.getClass().getSimpleName(); }
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java index 3e4078b..6e14907 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -27,6 +27,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionIterator; import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.InvokeVirtual; import com.android.tools.r8.ir.code.NewArrayEmpty; @@ -424,12 +425,16 @@ // Perform a conservative evaluation of an array content of dex type values from its construction // until its use at a given instruction. - private static DexType[] evaluateTypeArrayContentFromConstructionToUse( - NewArrayEmpty newArray, - List<CheckCast> aliases, - int size, - Instruction user, - DexItemFactory factory) { + private static DexTypeList evaluateTypeArrayContentFromConstructionToUse( + NewArrayEmpty newArray, List<CheckCast> aliases, Instruction user, DexItemFactory factory) { + int size = newArray.sizeIfConst(); + if (size < 0) { + return null; + } else if (size == 0) { + // TODO: We should likely still scan to ensure no ArrayPut instructions exist. + return DexTypeList.empty(); + } + DexType[] values = new DexType[size]; int remaining = size; Set<Instruction> users = Sets.newIdentityHashSet(); @@ -453,7 +458,7 @@ if (instruction == user) { // Return the array content if all elements are known when hitting the user for which // the content was requested. - return remaining == 0 ? values : null; + return remaining == 0 ? new DexTypeList(values) : null; } // Any other kinds of use besides array-put mean that the array escapes and its content // could be altered. @@ -498,6 +503,21 @@ return null; } + private static DexTypeList evaluateTypeArrayContent( + InvokeNewArray newArray, DexItemFactory factory) { + List<Value> arrayValues = newArray.inValues(); + int size = arrayValues.size(); + DexType[] values = new DexType[size]; + for (int i = 0; i < size; ++i) { + DexType type = getTypeFromConstClassOrBoxedPrimitive(arrayValues.get(i), factory); + if (type == null) { + return null; + } + values[i] = type; + } + return new DexTypeList(values); + } + /** * Visits all {@link ArrayPut}'s with the given {@param classListValue} as array and {@link Class} * as value. Then collects all corresponding {@link DexType}s so as to determine reflective cases. @@ -551,30 +571,13 @@ } // Make sure this Value refers to a new array. - if (!classListValue.definition.isNewArrayEmpty() - || !classListValue.definition.asNewArrayEmpty().size().isConstant()) { + if (classListValue.definition.isNewArrayEmpty()) { + return evaluateTypeArrayContentFromConstructionToUse( + classListValue.definition.asNewArrayEmpty(), aliases, invoke, factory); + } else if (classListValue.definition.isInvokeNewArray()) { + return evaluateTypeArrayContent(classListValue.definition.asInvokeNewArray(), factory); + } else { return null; } - - int size = - classListValue - .definition - .asNewArrayEmpty() - .size() - .getConstInstruction() - .asConstNumber() - .getIntValue(); - if (size == 0) { - return DexTypeList.empty(); - } - - DexType[] arrayContent = - evaluateTypeArrayContentFromConstructionToUse( - classListValue.definition.asNewArrayEmpty(), aliases, size, invoke, factory); - - if (arrayContent == null) { - return null; - } - return new DexTypeList(arrayContent); } }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java index 0dbea46..2e6fb53 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -87,11 +87,11 @@ private DexMethod getReboundMethodReference(DexMethod method) { DexMethod rebound = nonReboundMethodReferenceToDefinitionMap.get(method); + assert method != rebound; while (rebound != null) { method = rebound; rebound = nonReboundMethodReferenceToDefinitionMap.get(method); } - assert method != rebound; return method; }
diff --git a/src/main/java/com/android/tools/r8/references/FieldReference.java b/src/main/java/com/android/tools/r8/references/FieldReference.java index dab6fd8..90f4b91 100644 --- a/src/main/java/com/android/tools/r8/references/FieldReference.java +++ b/src/main/java/com/android/tools/r8/references/FieldReference.java
@@ -65,6 +65,14 @@ @Override public String toString() { - return getHolderClass().toString() + getFieldName() + ":" + getFieldType().getDescriptor(); + return getHolderClass() + getFieldName() + ":" + getFieldType().getDescriptor(); + } + + public String toSourceString() { + return getFieldType().getTypeName() + + " " + + getHolderClass().getTypeName() + + "." + + getFieldName(); } }
diff --git a/src/main/java/com/android/tools/r8/references/MethodReference.java b/src/main/java/com/android/tools/r8/references/MethodReference.java index 5ef8596..3339916 100644 --- a/src/main/java/com/android/tools/r8/references/MethodReference.java +++ b/src/main/java/com/android/tools/r8/references/MethodReference.java
@@ -5,7 +5,6 @@ import com.android.tools.r8.Keep; import com.android.tools.r8.KeepForRetraceApi; -import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.StringUtils.BraceType; import java.util.List; @@ -78,8 +77,7 @@ } public String getMethodDescriptor() { - return StringUtils.join( - "", ListUtils.map(getFormalTypes(), TypeReference::getDescriptor), BraceType.PARENS) + return StringUtils.join("", getFormalTypes(), TypeReference::getDescriptor, BraceType.PARENS) + (getReturnType() == null ? "V" : getReturnType().getDescriptor()); } @@ -87,4 +85,16 @@ public String toString() { return getHolderClass() + getMethodName() + getMethodDescriptor(); } + + public String toSourceString() { + StringBuilder sb = new StringBuilder(); + sb.append(returnType == null ? "void" : returnType.getTypeName()); + sb.append(" "); + sb.append(holderClass.getTypeName()); + sb.append("."); + sb.append(methodName); + sb.append( + StringUtils.join(", ", getFormalTypes(), TypeReference::getTypeName, BraceType.PARENS)); + return sb.toString(); + } }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeElement.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeElement.java new file mode 100644 index 0000000..2df0a4c --- /dev/null +++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeElement.java
@@ -0,0 +1,13 @@ +// Copyright (c) 2022, 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.retrace; + +import com.android.tools.r8.Keep; + +@Keep +public interface RetraceTypeElement extends RetraceElement<RetraceTypeResult> { + + RetracedTypeReference getType(); +}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java index 30a4c5c..7925ff6 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java +++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -5,21 +5,6 @@ package com.android.tools.r8.retrace; import com.android.tools.r8.Keep; -import java.util.function.Consumer; -import java.util.stream.Stream; @Keep -public interface RetraceTypeResult { - - Stream<Element> stream(); - - RetraceTypeResult forEach(Consumer<Element> resultConsumer); - - boolean isAmbiguous(); - - @Keep - interface Element { - - RetracedTypeReference getType(); - } -} +public interface RetraceTypeResult extends RetraceResult<RetraceTypeElement> {}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java b/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java index 99e85be..5e6b29b 100644 --- a/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java +++ b/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java
@@ -12,6 +12,8 @@ String getTypeName(); + String getDescriptor(); + String getBinaryName(); RetracedTypeReference getRetracedType();
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java index 6242c3b..d1ee952 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java
@@ -4,61 +4,94 @@ package com.android.tools.r8.retrace.internal; +import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; +import com.android.tools.r8.retrace.RetraceTypeElement; import com.android.tools.r8.retrace.RetraceTypeResult; import com.android.tools.r8.retrace.RetracedTypeReference; import com.android.tools.r8.retrace.Retracer; +import com.android.tools.r8.utils.ListUtils; +import java.util.Collections; +import java.util.List; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; public class RetraceTypeResultImpl implements RetraceTypeResult { private final TypeReference obfuscatedType; + private final List<RetracedTypeReference> retracedTypeReferences; private final Retracer retracer; - private RetraceTypeResultImpl(TypeReference obfuscatedType, Retracer retracer) { + private RetraceTypeResultImpl( + TypeReference obfuscatedType, + List<RetracedTypeReference> retracedTypeReferences, + Retracer retracer) { this.obfuscatedType = obfuscatedType; + this.retracedTypeReferences = retracedTypeReferences; this.retracer = retracer; } static RetraceTypeResultImpl create(TypeReference obfuscatedType, Retracer retracer) { - return new RetraceTypeResultImpl(obfuscatedType, retracer); + // Handle void and primitive types as single element results. + return new RetraceTypeResultImpl( + obfuscatedType, retraceTypeReference(obfuscatedType, retracer), retracer); + } + + private static List<RetracedTypeReference> retraceTypeReference( + TypeReference obfuscatedType, Retracer retracer) { + if (obfuscatedType == null) { + return Collections.emptyList(); + } else if (obfuscatedType.isPrimitive()) { + return Collections.singletonList(RetracedTypeReferenceImpl.create(obfuscatedType)); + } else if (obfuscatedType.isArray()) { + int dimensions = obfuscatedType.asArray().getDimensions(); + List<RetracedTypeReference> baseTypeRetraceResult = + retraceTypeReference(obfuscatedType.asArray().getBaseType(), retracer); + return ListUtils.map( + baseTypeRetraceResult, + retraceTypeReference -> + RetracedTypeReferenceImpl.create( + Reference.array(retraceTypeReference.getTypeReference(), dimensions))); + } else { + assert obfuscatedType.isClass(); + return retracer.retraceClass(obfuscatedType.asClass()).stream() + .map(clazz -> clazz.getRetracedClass().getRetracedType()) + .collect(Collectors.toList()); + } } @Override - public Stream<Element> stream() { - // Handle void and primitive types as single element results. - if (obfuscatedType == null || obfuscatedType.isPrimitive()) { - return Stream.of(new ElementImpl(RetracedTypeReferenceImpl.create(obfuscatedType))); - } - if (obfuscatedType.isArray()) { - int dimensions = obfuscatedType.asArray().getDimensions(); - return retracer.retraceType(obfuscatedType.asArray().getBaseType()).stream() - .map( - baseElement -> - new ElementImpl( - RetracedTypeReferenceImpl.create(baseElement.getType().toArray(dimensions)))); - } - return retracer.retraceClass(obfuscatedType.asClass()).stream() - .map(classElement -> new ElementImpl(classElement.getRetracedClass().getRetracedType())); + public Stream<RetraceTypeElement> stream() { + List<RetraceTypeElement> map = + ListUtils.map( + retracedTypeReferences, + retracedTypeReference -> new ElementImpl(this, retracedTypeReference)); + return map.stream(); } @Override public boolean isAmbiguous() { - return false; + return retracedTypeReferences.size() > 1; } @Override - public RetraceTypeResultImpl forEach(Consumer<Element> resultConsumer) { + public void forEach(Consumer<RetraceTypeElement> resultConsumer) { stream().forEach(resultConsumer); - return this; } - public static class ElementImpl implements RetraceTypeResult.Element { + @Override + public boolean isEmpty() { + return retracedTypeReferences.size() == 0; + } + public static class ElementImpl implements RetraceTypeElement { + + private final RetraceTypeResult typeResult; private final RetracedTypeReference retracedType; - public ElementImpl(RetracedTypeReference retracedType) { + private ElementImpl(RetraceTypeResult typeResult, RetracedTypeReference retracedType) { + this.typeResult = typeResult; this.retracedType = retracedType; } @@ -66,5 +99,15 @@ public RetracedTypeReference getType() { return retracedType; } + + @Override + public RetraceTypeResult getParentResult() { + return typeResult; + } + + @Override + public boolean isCompilerSynthesized() { + return false; + } } }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java index ef23ef1..d036009 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java
@@ -26,6 +26,11 @@ } @Override + public String getDescriptor() { + return classReference.getDescriptor(); + } + + @Override public String getBinaryName() { return classReference.getBinaryName(); }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java index a8b5e71..c907105 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -15,8 +15,8 @@ import com.android.tools.r8.retrace.RetraceStackTraceElementProxy; import com.android.tools.r8.retrace.RetraceStackTraceElementProxyResult; import com.android.tools.r8.retrace.RetraceThrownExceptionElement; +import com.android.tools.r8.retrace.RetraceTypeElement; import com.android.tools.r8.retrace.RetraceTypeResult; -import com.android.tools.r8.retrace.RetraceTypeResult.Element; import com.android.tools.r8.retrace.RetracedClassReference; import com.android.tools.r8.retrace.RetracedFieldReference; import com.android.tools.r8.retrace.RetracedMethodReference; @@ -272,7 +272,8 @@ } else { TypeReference typeReference = Reference.typeFromTypeName(elementOrReturnType); RetraceTypeResult retraceTypeResult = retracer.retraceType(typeReference); - List<Element> retracedElements = retraceTypeResult.stream().collect(Collectors.toList()); + List<RetraceTypeElement> retracedElements = + retraceTypeResult.stream().collect(Collectors.toList()); return resultBuilder .setResultStream( currentResult.stream()
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index 213819d..93d387e 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -104,7 +104,9 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionIterator; import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.code.InvokeNewArray; import com.android.tools.r8.ir.code.InvokeVirtual; +import com.android.tools.r8.ir.code.NewArrayEmpty; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection; import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; @@ -5079,25 +5081,43 @@ return; } Value parametersValue = constructorDefinition.inValues().get(1); - if (parametersValue.isPhi() || !parametersValue.definition.isNewArrayEmpty()) { + if (parametersValue.isPhi()) { // Give up, we can't tell which constructor is being invoked. return; } - - Value parametersSizeValue = parametersValue.definition.asNewArrayEmpty().size(); - if (parametersSizeValue.isPhi() || !parametersSizeValue.definition.isConstNumber()) { - // Give up, we can't tell which constructor is being invoked. + NewArrayEmpty newArrayEmpty = parametersValue.definition.asNewArrayEmpty(); + InvokeNewArray invokeNewArray = parametersValue.definition.asInvokeNewArray(); + int parametersSize = + newArrayEmpty != null + ? newArrayEmpty.sizeIfConst() + : invokeNewArray != null ? invokeNewArray.size() : -1; + if (parametersSize < 0) { return; } ProgramMethod initializer = null; - int parametersSize = parametersSizeValue.definition.asConstNumber().getIntValue(); if (parametersSize == 0) { initializer = clazz.getProgramDefaultInitializer(); } else { DexType[] parameterTypes = new DexType[parametersSize]; int missingIndices = parametersSize; + + if (newArrayEmpty != null) { + missingIndices = parametersSize; + } else { + missingIndices = 0; + List<Value> values = invokeNewArray.inValues(); + for (int i = 0; i < parametersSize; ++i) { + DexType type = + ConstantValueUtils.getDexTypeRepresentedByValueForTracing(values.get(i), appView); + if (type == null) { + return; + } + parameterTypes[i] = type; + } + } + for (Instruction user : parametersValue.uniqueUsers()) { if (user.isArrayPut()) { ArrayPut arrayPutInstruction = user.asArrayPut(); @@ -5159,20 +5179,31 @@ } Value interfacesValue = invoke.arguments().get(1); - if (interfacesValue.isPhi() || !interfacesValue.definition.isNewArrayEmpty()) { + if (interfacesValue.isPhi()) { // Give up, we can't tell which interfaces the proxy implements. return; } - WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList(); - for (Instruction user : interfacesValue.uniqueUsers()) { - if (!user.isArrayPut()) { - continue; + InvokeNewArray invokeNewArray = interfacesValue.definition.asInvokeNewArray(); + NewArrayEmpty newArrayEmpty = interfacesValue.definition.asNewArrayEmpty(); + List<Value> values; + if (invokeNewArray != null) { + values = invokeNewArray.inValues(); + } else if (newArrayEmpty != null) { + values = new ArrayList<>(interfacesValue.uniqueUsers().size()); + for (Instruction user : interfacesValue.uniqueUsers()) { + ArrayPut arrayPut = user.asArrayPut(); + if (arrayPut != null) { + values.add(arrayPut.value()); + } } + } else { + return; + } - ArrayPut arrayPut = user.asArrayPut(); - DexType type = - ConstantValueUtils.getDexTypeRepresentedByValueForTracing(arrayPut.value(), appView); + WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList(); + for (Value value : values) { + DexType type = ConstantValueUtils.getDexTypeRepresentedByValueForTracing(value, appView); if (type == null || !type.isClassType()) { continue; }
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java index 7170817..57726f6 100644 --- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java +++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -156,6 +156,11 @@ if (clazz == null) { return; } + if (clazz.isLibraryClass()) { + // TODO(b/259204069): Mitigation of invalid interface removal. + // It would be nice to integrate this with the api database. + return; + } for (DexType itf : clazz.interfaces) { if (interfaces.remove(itf) && interfaces.isEmpty()) { return;
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java index cdac93d..443b9e7 100644 --- a/src/main/java/com/android/tools/r8/utils/DexVersion.java +++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -4,12 +4,11 @@ package com.android.tools.r8.utils; import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.utils.structural.Ordered; import java.util.Optional; -/** - * Android dex version - */ -public enum DexVersion { +/** Android dex version */ +public enum DexVersion implements Ordered<DexVersion> { V35(35, new byte[] {'0', '3', '5'}), V37(37, new byte[] {'0', '3', '7'}), V38(38, new byte[] {'0', '3', '8'}),
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index e653a7a..98d5a27 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -816,6 +816,7 @@ public boolean debug = false; + private final RewriteArrayOptions rewriteArrayOptions = new RewriteArrayOptions(); private final CallSiteOptimizationOptions callSiteOptimizationOptions = new CallSiteOptimizationOptions(); private final CfCodeAnalysisOptions cfCodeAnalysisOptions = new CfCodeAnalysisOptions(); @@ -850,6 +851,10 @@ public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON; + public RewriteArrayOptions rewriteArrayOptions() { + return rewriteArrayOptions; + } + public CallSiteOptimizationOptions callSiteOptimizationOptions() { return callSiteOptimizationOptions; } @@ -1383,6 +1388,42 @@ System.getProperty("com.android.tools.r8.lambdaClassFieldsNotFinal") == null; } + public class RewriteArrayOptions { + // Arbitrary limit of number of inputs to new-filled-array/range. + // The technical limit is 255 (Constants.U8BIT_MAX). + public int maxRangeInputs = 200; + // Arbitrary limit of number of inputs to fill-array-data. + public int maxFillArrayDataInputs = 8 * 1024; + + // Dalvik x86-atom backend had a bug that made it crash on filled-new-array instructions for + // arrays of objects. This is unfortunate, since this never hits arm devices, but we have + // to disallow filled-new-array of objects for dalvik until kitkat. The buggy code was + // removed during the jelly-bean release cycle and is not there from kitkat. + // + // Buggy code that accidentally call code that only works on primitives arrays. + // + // https://android.googlesource.com/platform/dalvik/+/ics-mr0/vm/mterp/out/InterpAsm-x86-atom.S#25106 + public boolean canUseFilledNewArrayOfStrings() { + assert isGeneratingDex(); + return hasFeaturePresentFrom(AndroidApiLevel.K); + } + + // When adding support for emitting new-filled-array for non-String types, ART 6.0.1 had issues. + // https://ci.chromium.org/ui/p/r8/builders/ci/linux-android-6.0.1/6507/overview + // It somehow had a new-array-filled return null. + public boolean canUseFilledNewArrayOfObjects() { + assert isGeneratingDex(); + return hasFeaturePresentFrom(AndroidApiLevel.N); + } + + // Dalvik doesn't handle new-filled-array with arrays as values. It fails with: + // W(629880) VFY: [Ljava/lang/Integer; is not instance of Ljava/lang/Integer; (dalvikvm) + public boolean canUseFilledNewArrayOfArrays() { + assert isGeneratingDex(); + return hasFeaturePresentFrom(AndroidApiLevel.L); + } + } + public class CallSiteOptimizationOptions { private boolean enabled = true; @@ -2387,19 +2428,6 @@ return hasFeaturePresentFrom(AndroidApiLevel.J); } - // Dalvik x86-atom backend had a bug that made it crash on filled-new-array instructions for - // arrays of objects. This is unfortunate, since this never hits arm devices, but we have - // to disallow filled-new-array of objects for dalvik until kitkat. The buggy code was - // removed during the jelly-bean release cycle and is not there from kitkat. - // - // Buggy code that accidentally call code that only works on primitives arrays. - // - // https://android.googlesource.com/platform/dalvik/+/ics-mr0/vm/mterp/out/InterpAsm-x86-atom.S#25106 - public boolean canUseFilledNewArrayOfObjects() { - assert isGeneratingDex(); - return hasFeaturePresentFrom(AndroidApiLevel.K); - } - // Art had a bug (b/68761724) for Android N and O in the arm32 interpreter // where an aget-wide instruction using the same register for the array // and the first register of the result could lead to the wrong exception
diff --git a/src/main/java/com/android/tools/r8/utils/RetracerForCodePrinting.java b/src/main/java/com/android/tools/r8/utils/RetracerForCodePrinting.java new file mode 100644 index 0000000..1c5f9b3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/RetracerForCodePrinting.java
@@ -0,0 +1,176 @@ +// Copyright (c) 2022, 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.utils; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.IndexedDexItem; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.references.FieldReference; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.retrace.RetraceClassElement; +import com.android.tools.r8.retrace.RetraceElement; +import com.android.tools.r8.retrace.RetraceMethodResult; +import com.android.tools.r8.retrace.RetraceResult; +import com.android.tools.r8.retrace.RetracedFieldReference; +import com.android.tools.r8.retrace.RetracedFieldReference.KnownRetracedFieldReference; +import com.android.tools.r8.retrace.RetracedMethodReference; +import com.android.tools.r8.retrace.RetracedMethodReference.KnownRetracedMethodReference; +import com.android.tools.r8.retrace.Retracer; +import com.android.tools.r8.retrace.internal.MappingSupplierInternalImpl; +import com.android.tools.r8.retrace.internal.RetracerImpl; +import java.util.function.Function; + +public class RetracerForCodePrinting { + + private static final RetracerForCodePrinting EMPTY = new RetracerForCodePrinting(null); + + public static RetracerForCodePrinting empty() { + return EMPTY; + } + + private final Retracer retracer; + + private RetracerForCodePrinting(Retracer retracer) { + this.retracer = retracer; + } + + public static RetracerForCodePrinting create( + ClassNameMapper classNameMapper, DiagnosticsHandler handler) { + return classNameMapper == null + ? empty() + : new RetracerForCodePrinting( + RetracerImpl.createInternal( + MappingSupplierInternalImpl.createInternal(classNameMapper), handler)); + } + + public <T extends RetraceElement<?>> String joinAmbiguousResults( + RetraceResult<T> retraceResult, Function<T, String> nameToString) { + return StringUtils.join(" <OR> ", retraceResult.stream(), nameToString); + } + + private String typeToString( + DexType type, + Function<DexType, String> noRetraceString, + Function<RetraceClassElement, String> retraceResult) { + return retracer == null + ? noRetraceString.apply(type) + : joinAmbiguousResults(retracer.retraceClass(type.asClassReference()), retraceResult); + } + + public String toSourceString(DexType type) { + return typeToString( + type, DexType::toSourceString, element -> element.getRetracedClass().getTypeName()); + } + + public String toDescriptor(DexType type) { + return typeToString( + type, DexType::toDescriptorString, element -> element.getRetracedClass().getDescriptor()); + } + + private String retraceMethodToString( + DexMethod method, + Function<DexMethod, String> noRetraceString, + Function<KnownRetracedMethodReference, String> knownToString, + Function<RetracedMethodReference, String> unknownToString) { + if (retracer == null) { + return noRetraceString.apply(method); + } + // TODO(b/169953605): Use retracer.retraceMethod() when we have enough information. + MethodReference methodReference = method.asMethodReference(); + RetraceMethodResult retraceMethodResult = + retracer + .retraceClass(methodReference.getHolderClass()) + .lookupMethod(methodReference.getMethodName()); + return joinAmbiguousResults( + retraceMethodResult, + element -> { + if (element.isUnknown()) { + return unknownToString.apply(element.getRetracedMethod()); + } else { + return knownToString.apply(element.getRetracedMethod().asKnown()); + } + }); + } + + public String toSourceString(DexMethod method) { + return retraceMethodToString( + method, + DexMethod::toSourceString, + knownRetracedMethodReference -> + knownRetracedMethodReference.getMethodReference().toSourceString(), + unknown -> unknown.getHolderClass().getTypeName() + " " + unknown.getMethodName()); + } + + public String toDescriptor(DexMethod method) { + return retraceMethodToString( + method, + m -> m.asMethodReference().toString(), + knownRetracedMethodReference -> + knownRetracedMethodReference.getMethodReference().toString(), + unknown -> unknown.getHolderClass().getDescriptor() + unknown.getMethodName()); + } + + private String retraceFieldToString( + DexField field, + Function<DexField, String> noRetraceString, + Function<KnownRetracedFieldReference, String> knownToString, + Function<RetracedFieldReference, String> unknownToString) { + if (retracer == null) { + return noRetraceString.apply(field); + } + // TODO(b/169953605): Use retracer.retraceField() when we have enough information. + FieldReference fieldReference = field.asFieldReference(); + return joinAmbiguousResults( + retracer + .retraceClass(fieldReference.getHolderClass()) + .lookupField(fieldReference.getFieldName()), + element -> { + if (element.isUnknown()) { + return unknownToString.apply(element.getField()); + } else { + return knownToString.apply(element.getField().asKnown()); + } + }); + } + + public String toSourceString(DexField field) { + return retraceFieldToString( + field, + f -> f.asFieldReference().toSourceString(), + known -> known.getFieldReference().toSourceString(), + unknown -> unknown.getHolderClass().getDescriptor() + " " + unknown.getFieldName()); + } + + public String toDescriptor(DexField field) { + return retraceFieldToString( + field, + f -> f.asFieldReference().toString(), + known -> known.getFieldReference().toString(), + unknown -> unknown.getHolderClass().getDescriptor() + unknown.getFieldName()); + } + + public String toDescriptor(IndexedDexItem item) { + if (!(item instanceof DexReference)) { + return item.toString(); + } + return ((DexReference) item).apply(this::toDescriptor, this::toDescriptor, this::toDescriptor); + } + + public String toSourceString(IndexedDexItem item) { + if (!(item instanceof DexReference)) { + return item.toSourceString(); + } + return ((DexReference) item) + .apply(this::toSourceString, this::toSourceString, this::toSourceString); + } + + public boolean isEmpty() { + return this == EMPTY; + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java index f1e4e18..69cef74 100644 --- a/src/main/java/com/android/tools/r8/utils/StringUtils.java +++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -15,6 +15,8 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class StringUtils { public static char[] EMPTY_CHAR_ARRAY = {}; @@ -143,6 +145,15 @@ return join(separator, iterable, fn, BraceType.NONE); } + public static <T> String join(String separator, Stream<T> stream, Function<T, String> fn) { + return join(separator, stream.collect(Collectors.toList()), fn, BraceType.NONE); + } + + public static <T> String join( + String separator, T[] elements, Function<T, String> fn, BraceType brace) { + return join(separator, Arrays.asList(elements), fn, brace); + } + public static <T> String join(String separator, Iterable<T> iterable, BraceType brace) { return join(separator, iterable, Object::toString, brace); }
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java b/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java index 50d23c9..7d41ae1 100644 --- a/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java +++ b/src/main/java/com/android/tools/r8/utils/ThrowingIterator.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.utils; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -32,4 +33,18 @@ } return result; } + + public static <T, E extends Exception> ThrowingIterator<T, E> fromIterator(Iterator<T> it) { + return new ThrowingIterator<>() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public T next() throws E { + return it.next(); + } + }; + } }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 0230b46..5c73e63 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DirectMappedDexApplication; +import com.android.tools.r8.keepanno.utils.Unimplemented; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; import com.android.tools.r8.shaking.FilteredClassPath; @@ -32,6 +33,7 @@ import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.AndroidAppConsumers; +import com.android.tools.r8.utils.DexVersion; import com.android.tools.r8.utils.ExceptionUtils; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.InternalOptions; @@ -66,6 +68,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -712,12 +715,28 @@ private static final Path DX = getDxExecutablePath(); private static Path getDexVmPath(DexVm vm) { - return Paths.get( - TOOLS, - "linux", - vm.getVersion() == DexVm.Version.DEFAULT - ? "art" - : "art-" + vm.getVersion()); + DexVm.Version version = vm.getVersion(); + Path base = Paths.get(TOOLS, "linux"); + switch (version) { + case DEFAULT: + return base.resolve("art"); + case V4_0_4: + case V4_4_4: + case V5_1_1: + case V6_0_1: + case V7_0_0: + case V8_1_0: + case V9_0_0: + case V10_0_0: + return base.resolve("art-" + version); + case V12_0_0: + return base.resolve("host").resolve("art-12.0.0-beta4"); + case V13_0_0: + case MASTER: + return base.resolve("host").resolve("art-" + version); + default: + throw new Unreachable(); + } } private static Path getDexVmLibPath(DexVm vm) { @@ -733,7 +752,25 @@ } private static String getArchString(DexVm vm) { - return vm.isOlderThanOrEqual(DexVm.ART_5_1_1_HOST) ? "arm" : "arm64"; + switch (vm.getVersion()) { + case V4_0_4: + case V4_4_4: + case V5_1_1: + return "arm"; + case V6_0_1: + case V7_0_0: + case V8_1_0: + case DEFAULT: + case V9_0_0: + case V10_0_0: + return "arm64"; + case V12_0_0: + case V13_0_0: + case MASTER: + return "x86_64"; + default: + throw new Unimplemented(); + } } private static Path getProductBootImagePath(DexVm vm) { @@ -1057,6 +1094,10 @@ } } + public static DexVersion getDexFileVersionForVm(DexVm vm) { + return DexVersion.getDexVersion(getMinApiLevelForDexVm(vm)); + } + private static String getPlatform() { return System.getProperty("os.name"); } @@ -1992,34 +2033,56 @@ } } - public static ProcessResult runDex2OatRaw(Path file, Path outFile, DexVm vm) throws IOException { - // TODO(jmhenaff): find a way to run this on windows (push dex and run on device/emulator?) + // Checked in VMs for which dex2oat should work specified in decreasing order. + private static List<DexVm> SUPPORTED_DEX2OAT_VMS = + ImmutableList.of(DexVm.ART_12_0_0_HOST, DexVm.ART_6_0_1_HOST); + + public static ProcessResult runDex2OatRaw(Path file, Path outFile, DexVm targetVm) + throws IOException { Assume.assumeTrue(ToolHelper.isDex2OatSupported()); - Assume.assumeFalse( - "b/144975341", - vm.version == DexVm.Version.V10_0_0 - || vm.version == DexVm.Version.V12_0_0 - || vm.version == DexVm.Version.V13_0_0); - if (vm.isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) { - // Run default dex2oat for tests on dalvik runtimes. - vm = DexVm.ART_DEFAULT; - } assert Files.exists(file); assert ByteStreams.toByteArray(Files.newInputStream(file)).length > 0; + assert SUPPORTED_DEX2OAT_VMS.stream() + .sorted(Comparator.comparing(DexVm::getVersion).reversed()) + .collect(Collectors.toList()) + .equals(SUPPORTED_DEX2OAT_VMS); + DexVersion requiredDexFileVersion = getDexFileVersionForVm(targetVm); + DexVm vm = null; + for (DexVm supported : SUPPORTED_DEX2OAT_VMS) { + DexVersion supportedDexFileVersion = getDexFileVersionForVm(supported); + // This and remaining VMs can't compile code at the required DEX version. + if (supportedDexFileVersion.isLessThan(requiredDexFileVersion)) { + break; + } + // Find the "oldest" supported VM. Only consider VMs older than targetVm if no VM matched. + if (supported.isNewerThanOrEqual(targetVm) || vm == null) { + vm = supported; + } + } + if (vm == null) { + throw new Unimplemented("Unable to find a supported dex2oat for VM " + vm); + } List<String> command = new ArrayList<>(); - command.add(getDex2OatPath(vm).toString()); - command.add("--android-root=" + getProductPath(vm) + "/system"); + command.add(getDex2OatPath(vm).toAbsolutePath().toString()); + command.add("--android-root=" + getProductPath(vm).toAbsolutePath() + "/system"); command.add("--runtime-arg"); command.add("-verbose:verifier"); command.add("--runtime-arg"); command.add("-Xnorelocate"); command.add("--dex-file=" + file.toAbsolutePath()); command.add("--oat-file=" + outFile.toAbsolutePath()); - // TODO(zerny): Create a proper interface for invoking dex2oat. Hardcoding arch here is a hack! command.add("--instruction-set=" + getArchString(vm)); + if (vm.version.equals(DexVm.Version.V12_0_0)) { + command.add( + "--boot-image=" + + getDexVmPath(vm).toAbsolutePath() + + "/apex/art_boot_images/javalib/boot.art"); + } ProcessBuilder builder = new ProcessBuilder(command); + builder.directory(getDexVmPath(vm).toFile()); builder.environment().put("LD_LIBRARY_PATH", getDexVmLibPath(vm).toString()); - return runProcess(builder); + ProcessResult processResult = runProcess(builder); + return processResult; } public static ProcessResult runProguardRaw( @@ -2192,8 +2255,15 @@ public static ProcessResult runProcess(ProcessBuilder builder, PrintStream out) throws IOException { + boolean printCwd = builder.directory() != null; + if (printCwd) { + out.println("(cd " + builder.directory().toString() + "; "); + } String command = String.join(" ", builder.command()); out.println(command); + if (printCwd) { + out.println(")"); + } return drainProcessOutputStreams(builder.start(), command); }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java new file mode 100644 index 0000000..380702e --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java
@@ -0,0 +1,186 @@ +// Copyright (c) 2022, 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.apimodel; + +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.SingleTestRunResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.testing.AndroidBuildVersion; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ApiModelMockAbstractMethodOnBaseToOutlineTest extends TestBase { + + private final AndroidApiLevel subMockLevel = AndroidApiLevel.M; + + @Parameter public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + } + + private boolean isGreaterOrEqualToMockLevel() { + return parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(subMockLevel); + } + + private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception { + testBuilder + .addProgramClasses(Main.class) + .addLibraryClasses( + LibraryClass.class, OtherLibraryClass.class, SubLibraryClassAtLaterApiLevel.class) + .addDefaultRuntimeLibrary(parameters) + .setMinApi(parameters.getApiLevel()) + .addAndroidBuildVersion() + .apply(ApiModelingTestHelper::enableStubbingOfClasses) + .apply(ApiModelingTestHelper::enableOutliningOfMethods) + .apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.B)) + .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, AndroidApiLevel.B)) + .apply( + setMockApiLevelForMethod( + LibraryClass.class.getDeclaredMethod("foo"), AndroidApiLevel.B)) + .apply( + setMockApiLevelForMethod( + OtherLibraryClass.class.getDeclaredMethod("baz"), subMockLevel)) + .apply(setMockApiLevelForClass(SubLibraryClassAtLaterApiLevel.class, subMockLevel)) + .apply( + setMockApiLevelForDefaultInstanceInitializer( + SubLibraryClassAtLaterApiLevel.class, subMockLevel)) + .apply( + setMockApiLevelForMethod( + SubLibraryClassAtLaterApiLevel.class.getDeclaredMethod("foo"), subMockLevel)); + } + + @Test + public void testD8Debug() throws Exception { + testForD8(parameters.getBackend()) + .setMode(CompilationMode.DEBUG) + .apply(this::setupTestBuilder) + .compile() + .inspect(this::inspect) + .addBootClasspathClasses(LibraryClass.class) + .applyIf( + isGreaterOrEqualToMockLevel(), + b -> + b.addBootClasspathClasses( + OtherLibraryClass.class, SubLibraryClassAtLaterApiLevel.class)) + .run(parameters.getRuntime(), Main.class) + .apply(runResult -> checkOutput(runResult, false)); + } + + @Test + public void testD8Release() throws Exception { + testForD8(parameters.getBackend()) + .setMode(CompilationMode.RELEASE) + .apply(this::setupTestBuilder) + .compile() + .inspect(this::inspect) + .addBootClasspathClasses(LibraryClass.class) + .applyIf( + isGreaterOrEqualToMockLevel(), + b -> + b.addBootClasspathClasses( + OtherLibraryClass.class, SubLibraryClassAtLaterApiLevel.class)) + .run(parameters.getRuntime(), Main.class) + .apply(runResult -> checkOutput(runResult, true)); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .apply(this::setupTestBuilder) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .compile() + .inspect(this::inspect) + .addBootClasspathClasses(LibraryClass.class) + .applyIf( + isGreaterOrEqualToMockLevel(), + b -> + b.addBootClasspathClasses( + OtherLibraryClass.class, SubLibraryClassAtLaterApiLevel.class)) + .run(parameters.getRuntime(), Main.class) + .apply(runResult -> checkOutput(runResult, true)); + } + + private void checkOutput(SingleTestRunResult<?> runResult, boolean isRelease) { + if (isGreaterOrEqualToMockLevel()) { + runResult.assertSuccessWithOutputLines( + "OtherLibraryClass::foo", "SubLibraryClassAtLaterApiLevel::foo"); + } else { + runResult.assertSuccessWithOutputLines("NoClassDefFoundError"); + } + runResult.applyIf( + !isGreaterOrEqualToMockLevel() + && parameters.isDexRuntime() + && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0), + result -> result.assertStderrMatches(not(containsString("This dex file is invalid")))); + } + + private void inspect(CodeInspector inspector) { + assertThat(inspector.clazz(SubLibraryClassAtLaterApiLevel.class), isAbsent()); + } + + public abstract static class LibraryClass { + + public abstract void foo(); + } + + public static class SubLibraryClassAtLaterApiLevel extends LibraryClass { + + @Override + public void foo() { + System.out.println("SubLibraryClassAtLaterApiLevel::foo"); + } + } + + public static class OtherLibraryClass { + + public static void baz() { + System.out.println("OtherLibraryClass::foo"); + } + } + + public static class Main { + + public static void main(String[] args) { + try { + OtherLibraryClass.baz(); + if (AndroidBuildVersion.VERSION >= 23) { + callSubFoo(new SubLibraryClassAtLaterApiLevel()); + } else if (System.currentTimeMillis() == 0) { + callSubFoo(new Object()); + } + } catch (NoClassDefFoundError e) { + System.out.println("NoClassDefFoundError"); + } + } + + @NeverInline + private static void callSubFoo(Object o) { + ((SubLibraryClassAtLaterApiLevel) o).foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java similarity index 66% copy from src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java copy to src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java index b80d79c..05c405f 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java
@@ -1,13 +1,14 @@ -// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// Copyright (c) 2022, 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.apimodel; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -17,7 +18,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) -public class ApiModelMockClassLoadingTest extends TestBase { +public class ApiModelMockClassCheckCastTest extends TestBase { private final AndroidApiLevel mockLevel = AndroidApiLevel.M; @@ -36,7 +36,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); } private boolean isGreaterOrEqualToMockLevel() { @@ -54,9 +54,17 @@ } @Test + public void testReference() throws Exception { + assumeTrue(parameters.isCfRuntime() && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + testForJvm() + .addProgramClasses(Main.class, TestClass.class) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkOutput); + } + + @Test public void testD8Debug() throws Exception { - assumeTrue(parameters.isDexRuntime()); - testForD8() + testForD8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) .compile() @@ -68,8 +76,7 @@ @Test public void testD8Release() throws Exception { - assumeTrue(parameters.isDexRuntime()); - testForD8() + testForD8(parameters.getBackend()) .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) .compile() @@ -94,12 +101,10 @@ private void checkOutput(SingleTestRunResult<?> runResult) { if (isGreaterOrEqualToMockLevel()) { - runResult.assertSuccessWithOutputLines("Hello World!"); - } else if (parameters.isDexRuntime() - && parameters.asDexRuntime().getVm().isEqualTo(DexVm.ART_4_4_4_HOST)) { - runResult.assertSuccessWithOutputLines("ClassNotFoundException"); + runResult.assertSuccessWithOutputLines("false", "checkcast caused ClassCastException"); } else { - runResult.assertSuccessWithOutputLines("NoClassDefFoundError"); + runResult.assertSuccessWithOutputLines( + "instanceof caused NoClassDefFoundError", "checkcast caused NoClassDefFoundError"); } runResult.applyIf( !isGreaterOrEqualToMockLevel() @@ -109,7 +114,7 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } // Only present from api level 23. @@ -118,14 +123,22 @@ public static class TestClass { @NeverInline - public static void test() { + public static void testInstanceOf(Object o) { try { - Class.forName(LibraryClass.class.getName()); - System.out.println("Hello World!"); - } catch (ExceptionInInitializerError | NoClassDefFoundError er) { - System.out.println("NoClassDefFoundError"); - } catch (ClassNotFoundException e) { - System.out.println("ClassNotFoundException"); + System.out.println(o instanceof LibraryClass); + } catch (NoClassDefFoundError ex) { + System.out.println("instanceof caused NoClassDefFoundError"); + } + } + + @NeverInline + public static void testCheckCast(Object o) { + try { + System.out.println(((LibraryClass) o).getClass().getName()); + } catch (NoClassDefFoundError e) { + System.out.println("checkcast caused NoClassDefFoundError"); + } catch (ClassCastException e) { + System.out.println("checkcast caused ClassCastException"); } } } @@ -133,7 +146,15 @@ public static class Main { public static void main(String[] args) { - TestClass.test(); + if (System.currentTimeMillis() > 0) { + Object o = new Object(); + TestClass.testInstanceOf(o); + TestClass.testCheckCast(o); + } else { + LibraryClass libraryClass = new LibraryClass(); + TestClass.testInstanceOf(libraryClass); + TestClass.testCheckCast(libraryClass); + } } } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java similarity index 77% copy from src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java copy to src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java index b80d79c..0b76660 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java
@@ -5,9 +5,10 @@ package com.android.tools.r8.apimodel; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -17,7 +18,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) -public class ApiModelMockClassLoadingTest extends TestBase { +public class ApiModelMockClassLoadingByClassForNameTest extends TestBase { private final AndroidApiLevel mockLevel = AndroidApiLevel.M; @@ -36,7 +36,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); } private boolean isGreaterOrEqualToMockLevel() { @@ -54,9 +54,17 @@ } @Test + public void testReference() throws Exception { + assumeTrue(parameters.isCfRuntime() && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + testForJvm() + .addProgramClasses(Main.class, TestClass.class) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkOutput); + } + + @Test public void testD8Debug() throws Exception { - assumeTrue(parameters.isDexRuntime()); - testForD8() + testForD8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) .compile() @@ -68,8 +76,7 @@ @Test public void testD8Release() throws Exception { - assumeTrue(parameters.isDexRuntime()); - testForD8() + testForD8(parameters.getBackend()) .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) .compile() @@ -95,11 +102,8 @@ private void checkOutput(SingleTestRunResult<?> runResult) { if (isGreaterOrEqualToMockLevel()) { runResult.assertSuccessWithOutputLines("Hello World!"); - } else if (parameters.isDexRuntime() - && parameters.asDexRuntime().getVm().isEqualTo(DexVm.ART_4_4_4_HOST)) { - runResult.assertSuccessWithOutputLines("ClassNotFoundException"); } else { - runResult.assertSuccessWithOutputLines("NoClassDefFoundError"); + runResult.assertSuccessWithOutputLines("ClassNotFoundException"); } runResult.applyIf( !isGreaterOrEqualToMockLevel() @@ -109,7 +113,7 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } // Only present from api level 23. @@ -120,9 +124,14 @@ @NeverInline public static void test() { try { - Class.forName(LibraryClass.class.getName()); + String className = + "com.android.tools.r8.apimodel.ApiModelMockClassLoadingByClassForNameTest$LibraryClass" + + (System.currentTimeMillis() == 0 ? "asdf" : ""); + Class.forName(className); System.out.println("Hello World!"); - } catch (ExceptionInInitializerError | NoClassDefFoundError er) { + } catch (ExceptionInInitializerError er) { + System.out.println("ExceptionInInitializerError"); + } catch (NoClassDefFoundError er) { System.out.println("NoClassDefFoundError"); } catch (ClassNotFoundException e) { System.out.println("ClassNotFoundException"); @@ -134,6 +143,9 @@ public static void main(String[] args) { TestClass.test(); + if (System.currentTimeMillis() == 0) { + System.out.println(LibraryClass.class.getName()); + } } } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java similarity index 80% rename from src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java rename to src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java index b80d79c..9074fcc 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java
@@ -1,13 +1,14 @@ -// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// Copyright (c) 2022, 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.apimodel; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -17,7 +18,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) -public class ApiModelMockClassLoadingTest extends TestBase { +public class ApiModelMockClassLoadingByClassReferenceTest extends TestBase { private final AndroidApiLevel mockLevel = AndroidApiLevel.M; @@ -36,7 +36,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); } private boolean isGreaterOrEqualToMockLevel() { @@ -54,9 +54,17 @@ } @Test + public void testReference() throws Exception { + assumeTrue(parameters.isCfRuntime() && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + testForJvm() + .addProgramClasses(Main.class, TestClass.class) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkOutput); + } + + @Test public void testD8Debug() throws Exception { - assumeTrue(parameters.isDexRuntime()); - testForD8() + testForD8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) .compile() @@ -68,8 +76,7 @@ @Test public void testD8Release() throws Exception { - assumeTrue(parameters.isDexRuntime()); - testForD8() + testForD8(parameters.getBackend()) .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) .compile() @@ -94,10 +101,7 @@ private void checkOutput(SingleTestRunResult<?> runResult) { if (isGreaterOrEqualToMockLevel()) { - runResult.assertSuccessWithOutputLines("Hello World!"); - } else if (parameters.isDexRuntime() - && parameters.asDexRuntime().getVm().isEqualTo(DexVm.ART_4_4_4_HOST)) { - runResult.assertSuccessWithOutputLines("ClassNotFoundException"); + runResult.assertSuccessWithOutputLines(typeName(LibraryClass.class)); } else { runResult.assertSuccessWithOutputLines("NoClassDefFoundError"); } @@ -109,7 +113,7 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } // Only present from api level 23. @@ -120,12 +124,9 @@ @NeverInline public static void test() { try { - Class.forName(LibraryClass.class.getName()); - System.out.println("Hello World!"); + System.out.println(new LibraryClass().getClass().getName()); } catch (ExceptionInInitializerError | NoClassDefFoundError er) { System.out.println("NoClassDefFoundError"); - } catch (ClassNotFoundException e) { - System.out.println("ClassNotFoundException"); } } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java index cff8d68..ac98967 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -6,11 +6,9 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -21,7 +19,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.references.Reference; import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; @@ -133,18 +130,7 @@ .compile() .writeToZip(); - if (isGreaterOrEqualToMockLevel()) { - assertFalse(globals.hasGlobals()); - } else if (outputMode == OutputMode.DexIndexed) { - assertTrue(globals.hasGlobals()); - assertTrue(globals.isSingleGlobal()); - } else { - assertTrue(globals.hasGlobals()); - // The TestClass does reference the mock and should have globals. - assertNotNull(globals.getProvider(Reference.classFromClass(TestClass.class))); - // The Main class does not have references to the mock and should have no globals. - assertNull(globals.getProvider(Reference.classFromClass(Main.class))); - } + assertFalse(globals.hasGlobals()); testForD8() .debug() @@ -172,7 +158,7 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java index 50edd76..1a5cd80 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java
@@ -7,7 +7,8 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -109,7 +110,7 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } public abstract static class LibraryClass {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java index 322b30f..5d32ca1 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -6,7 +6,8 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -97,7 +98,7 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java index 409cd9b..0ca565f 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
@@ -10,7 +10,6 @@ import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import com.android.tools.r8.CompilationMode; import com.android.tools.r8.OutputMode; @@ -19,7 +18,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.references.Reference; import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -102,17 +100,8 @@ paths.add(runD8ForClass(TestCallingFoo.class, testCallingFooGlobals, mode)); paths.add(runD8ForClass(TestCallingBar.class, testCallingBarGlobals, mode)); assertFalse(mainGlobals.hasGlobals()); - if (isGreaterOrEqualToMockLevel()) { - assertFalse(testCallingFooGlobals.hasGlobals()); - assertFalse(testCallingBarGlobals.hasGlobals()); - } else { - // The TestCallingX does reference the mock and should have globals. - assertNotNull( - testCallingFooGlobals.getProvider(Reference.classFromClass(TestCallingFoo.class))); - assertNotNull( - testCallingBarGlobals.getProvider(Reference.classFromClass(TestCallingBar.class))); - } - + assertFalse(testCallingFooGlobals.hasGlobals()); + assertFalse(testCallingBarGlobals.hasGlobals()); testForD8() .setMode(mode) .addProgramFiles(paths)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java index b2611de..01681c4 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
@@ -8,10 +8,8 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; -import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import com.android.tools.r8.CompilationMode; import com.android.tools.r8.OutputMode; @@ -20,7 +18,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.references.Reference; import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -103,17 +100,8 @@ paths.add(runD8ForClass(TestCallingFoo.class, testCallingFooGlobals, mode)); paths.add(runD8ForClass(TestCallingBar.class, testCallingBarGlobals, mode)); assertFalse(mainGlobals.hasGlobals()); - if (isGreaterOrEqualToMockLevel()) { - assertFalse(testCallingFooGlobals.hasGlobals()); - assertFalse(testCallingBarGlobals.hasGlobals()); - } else { - // The TestCallingX does reference the mock and should have globals. - assertNotNull( - testCallingFooGlobals.getProvider(Reference.classFromClass(TestCallingFoo.class))); - assertNotNull( - testCallingBarGlobals.getProvider(Reference.classFromClass(TestCallingBar.class))); - } - + assertFalse(testCallingFooGlobals.hasGlobals()); + assertFalse(testCallingBarGlobals.hasGlobals()); testForD8() .setMode(mode) .addProgramFiles(paths) @@ -132,13 +120,7 @@ private void inspect(CodeInspector inspector) { ClassSubject libraryClassSubject = inspector.clazz(LibraryClass.class); - if (isGreaterOrEqualToMockLevel()) { - assertThat(libraryClassSubject, isAbsent()); - } else { - assertThat(libraryClassSubject, isPresent()); - assertThat(libraryClassSubject.uniqueMethodWithOriginalName("foo"), isAbsent()); - assertThat(libraryClassSubject.uniqueMethodWithOriginalName("bar"), isAbsent()); - } + assertThat(libraryClassSubject, isAbsent()); } private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java index 5d4fc8f..a45f683 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -6,7 +6,8 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; -import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -121,9 +122,9 @@ } private void inspect(CodeInspector inspector) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel); - verifyThat(inspector, parameters, LibraryInterface.class).stubbedUntil(lowerMockApiLevel); - verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); + assertThat(inspector.clazz(LibraryInterface.class), isAbsent()); + assertThat(inspector.clazz(OtherLibraryClass.class), isAbsent()); } private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java index 4bb470c..37e361c 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -116,11 +116,7 @@ } else { verifyThat(inspector, parameters, methodOn23).isNotOutlinedFrom(mainMethod); } - if (canStub) { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryApiLevel); - } else { - assertThat(inspector.clazz(LibraryClass.class), isAbsent()); - } + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); } // Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java index 3504527..01894d1 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -7,6 +7,8 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -104,7 +106,7 @@ } private void inspect(CodeInspector inspector) throws Exception { - verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryClassLevel); + assertThat(inspector.clazz(LibraryClass.class), isAbsent()); verifyThat(inspector, parameters, apiMethod()) .isOutlinedFromUntil( Main.class.getDeclaredMethod("main", String[].class), libraryMethodLevel);
diff --git a/src/test/java/com/android/tools/r8/apimodel/NewInstanceToAbstractClassReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/NewInstanceToAbstractClassReferenceTest.java new file mode 100644 index 0000000..b733845 --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/NewInstanceToAbstractClassReferenceTest.java
@@ -0,0 +1,71 @@ +// Copyright (c) 2022, 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.apimodel; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.graph.ClassAccessFlags; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class NewInstanceToAbstractClassReferenceTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(Main.class) + .addProgramClassFileData( + transformer(A.class).setAccessFlags(ClassAccessFlags::setAbstract).transform()) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), InstantiationError.class) + .applyIf( + parameters.isDexRuntime(), + result -> { + if (parameters.getDexRuntimeVersion().isDalvik()) { + result.assertStderrMatches( + containsString( + "VFY: new-instance on interface or abstract class " + descriptor(A.class))); + + } else if (parameters.getDexRuntimeVersion().isOlderThan(Version.V7_0_0)) { + result.assertStderrMatches( + containsString( + "Verification failed on class " + + typeName(NewInstanceToAbstractClassReferenceTest.class))); + } else { + result.assertStderrMatches(not(containsString("Verification failed"))); + } + }); + } + + public static class A { + + public void foo() { + System.out.println("A::foo"); + } + } + + public static class Main { + + public static void main(String[] args) { + new A().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java index 1f9e6c7..7799ba7 100644 --- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -1021,7 +1021,7 @@ assertThat(method, isPresent()); assertThat( method.getMethod().getCode().asCfCode().toString(), - containsString("invokeinterface classmerging.MergeDefaultMethodIntoClassTest$A.f()V")); + containsString("invokeinterface Lclassmerging/MergeDefaultMethodIntoClassTest$A;f()V")); runTestOnInput( testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java b/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java index 812eb63..b00d78b 100644 --- a/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java +++ b/src/test/java/com/android/tools/r8/compatproguard/GetMembersTest.java
@@ -10,7 +10,9 @@ import com.android.tools.r8.dex.code.DexConst4; import com.android.tools.r8.dex.code.DexConstClass; import com.android.tools.r8.dex.code.DexConstString; +import com.android.tools.r8.dex.code.DexFilledNewArray; import com.android.tools.r8.dex.code.DexInvokeVirtual; +import com.android.tools.r8.dex.code.DexMoveResultObject; import com.android.tools.r8.dex.code.DexNewArray; import com.android.tools.r8.dex.code.DexReturnVoid; import com.android.tools.r8.graph.DexCode; @@ -93,16 +95,29 @@ assertTrue(method.isPresent()); DexCode code = method.getMethod().getCode().asDexCode(); - assertTrue(code.instructions[0] instanceof DexConst4); - assertTrue(code.instructions[1] instanceof DexNewArray); - assertTrue(code.instructions[2] instanceof DexConst4); - assertTrue(code.instructions[3] instanceof DexConstClass); - assertTrue(code.instructions[4] instanceof DexAputObject); - assertTrue(code.instructions[5] instanceof DexConstClass); - assertTrue(code.instructions[6] instanceof DexConstString); - assertNotEquals("foo", code.instructions[6].asConstString().getString().toString()); - assertTrue(code.instructions[7] instanceof DexInvokeVirtual); - assertTrue(code.instructions[8] instanceof DexReturnVoid); + + // Accept either array construction style (differs based on minSdkVersion). + if (code.instructions[1] instanceof DexFilledNewArray) { + assertTrue(code.instructions[0] instanceof DexConstClass); + assertTrue(code.instructions[1] instanceof DexFilledNewArray); + assertTrue(code.instructions[2] instanceof DexMoveResultObject); + assertTrue(code.instructions[3] instanceof DexConstClass); + assertTrue(code.instructions[4] instanceof DexConstString); + assertNotEquals("foo", code.instructions[4].asConstString().getString().toString()); + assertTrue(code.instructions[5] instanceof DexInvokeVirtual); + assertTrue(code.instructions[6] instanceof DexReturnVoid); + } else { + assertTrue(code.instructions[0] instanceof DexConst4); + assertTrue(code.instructions[1] instanceof DexNewArray); + assertTrue(code.instructions[2] instanceof DexConst4); + assertTrue(code.instructions[3] instanceof DexConstClass); + assertTrue(code.instructions[4] instanceof DexAputObject); + assertTrue(code.instructions[5] instanceof DexConstClass); + assertTrue(code.instructions[6] instanceof DexConstString); + assertNotEquals("foo", code.instructions[6].asConstString().getString().toString()); + assertTrue(code.instructions[7] instanceof DexInvokeVirtual); + assertTrue(code.instructions[8] instanceof DexReturnVoid); + } } }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java index 76bd656..2c99723 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -80,7 +80,11 @@ "java.util.stream.DoubleStream" + " java.util.stream.DoubleStream.dropWhile(java.util.function.DoublePredicate)", // FileStore.getBlockSize() was added in 33 which confuses the required library (30). - "long java.nio.file.FileStore.getBlockSize()"); + "long java.nio.file.FileStore.getBlockSize()", + // The call is present but unreachable above 26. + "java.nio.channels.FileChannel" + + " java.nio.channels.DesugarChannels.openEmulatedFileChannel(java.nio.file.Path," + + " java.util.Set, java.nio.file.attribute.FileAttribute[])"); private final TestParameters parameters; private final CompilationSpecification compilationSpecification;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java index a68f3ce..4602235 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -514,8 +514,7 @@ if (clazzType.startsWith("java.") && !doesNotNeedWrapper(clazzType, customConversions, maintainType) // FileChannel is there since B but it needs wrapping due to recently added interfaces. - && (!preDesugarTypes.contains(clazz) - || clazzType.equals("java.nio.channels.FileChannel"))) { + && !preDesugarTypes.contains(clazz)) { additions.accept(clazz); return true; }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java index 535da2a..5e6c723 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -148,23 +148,23 @@ .streamInstructions() .filter(instr -> instr.isInvokeInterface() || instr.isInvokeStatic()) .collect(Collectors.toList()); - assertInvokeStaticMatching(invokes, 0, "Set$-EL;->spliterator"); - assertInvokeStaticMatching(invokes, 1, "Collection$-EL;->stream"); - assertInvokeInterfaceMatching(invokes, 2, "Set;->iterator"); - assertInvokeStaticMatching(invokes, 3, "Collection$-EL;->stream"); - assertInvokeStaticMatching(invokes, 4, "Set$-EL;->spliterator"); - assertInvokeInterfaceMatching(invokes, 8, "Iterator;->remove"); - assertInvokeStaticMatching(invokes, 9, "DesugarArrays;->spliterator"); - assertInvokeStaticMatching(invokes, 10, "DesugarArrays;->spliterator"); - assertInvokeStaticMatching(invokes, 11, "DesugarArrays;->stream"); - assertInvokeStaticMatching(invokes, 12, "DesugarArrays;->stream"); - assertInvokeStaticMatching(invokes, 13, "Collection$-EL;->stream"); - assertInvokeStaticMatching(invokes, 14, "IntStream$-CC;->range"); - assertInvokeStaticMatching(invokes, 16, "Comparator$-CC;->comparingInt"); - assertInvokeStaticMatching(invokes, 17, "List$-EL;->sort"); - assertInvokeStaticMatching(invokes, 19, "Comparator$-CC;->comparingInt"); - assertInvokeStaticMatching(invokes, 20, "List$-EL;->sort"); - assertInvokeStaticMatching(invokes, 21, "Collection$-EL;->stream"); + assertInvokeStaticMatching(invokes, 0, "Set$-EL;spliterator"); + assertInvokeStaticMatching(invokes, 1, "Collection$-EL;stream"); + assertInvokeInterfaceMatching(invokes, 2, "Set;iterator"); + assertInvokeStaticMatching(invokes, 3, "Collection$-EL;stream"); + assertInvokeStaticMatching(invokes, 4, "Set$-EL;spliterator"); + assertInvokeInterfaceMatching(invokes, 8, "Iterator;remove"); + assertInvokeStaticMatching(invokes, 9, "DesugarArrays;spliterator"); + assertInvokeStaticMatching(invokes, 10, "DesugarArrays;spliterator"); + assertInvokeStaticMatching(invokes, 11, "DesugarArrays;stream"); + assertInvokeStaticMatching(invokes, 12, "DesugarArrays;stream"); + assertInvokeStaticMatching(invokes, 13, "Collection$-EL;stream"); + assertInvokeStaticMatching(invokes, 14, "IntStream$-CC;range"); + assertInvokeStaticMatching(invokes, 16, "Comparator$-CC;comparingInt"); + assertInvokeStaticMatching(invokes, 17, "List$-EL;sort"); + assertInvokeStaticMatching(invokes, 19, "Comparator$-CC;comparingInt"); + assertInvokeStaticMatching(invokes, 20, "List$-EL;sort"); + assertInvokeStaticMatching(invokes, 21, "Collection$-EL;stream"); assertEquals(22, invokes.size()); }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileChannelTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileChannelTest.java new file mode 100644 index 0000000..6c5c149 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileChannelTest.java
@@ -0,0 +1,234 @@ +// Copyright (c) 2022, 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.desugar.desugaredlibrary.jdk11; + +import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS; +import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; +import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification; +import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FileChannelTest extends DesugaredLibraryTestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "true", + "true", + "true", + "true", + "Hello World! ", + "Hello World! ", + "Bye bye. ", + "Hello World! ", + "Bye bye. ", + "Hello World! ", + "The monkey eats...", + "Bananas!", + "Bananas!", + "Bananas!"); + + private final TestParameters parameters; + private final LibraryDesugaringSpecification libraryDesugaringSpecification; + private final CompilationSpecification compilationSpecification; + + @Parameters(name = "{0}, spec: {1}, {2}") + public static List<Object[]> data() { + return buildParameters( + // Skip Android 4.4.4 due to missing libjavacrypto. + getTestParameters() + .withDexRuntime(Version.V4_0_4) + .withDexRuntimesStartingFromIncluding(Version.V5_1_1) + .withAllApiLevels() + .build(), + ImmutableList.of(JDK11_PATH), + DEFAULT_SPECIFICATIONS); + } + + public FileChannelTest( + TestParameters parameters, + LibraryDesugaringSpecification libraryDesugaringSpecification, + CompilationSpecification compilationSpecification) { + this.parameters = parameters; + this.libraryDesugaringSpecification = libraryDesugaringSpecification; + this.compilationSpecification = compilationSpecification; + } + + @Test + public void test() throws Throwable { + testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification) + .addInnerClasses(getClass()) + .addKeepMainRule(TestClass.class) + .compile() + .withArt6Plus64BitsLib() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + public static class TestClass { + + public static void main(String[] args) throws IOException { + instanceTest(); + fisTest(); + fosTest(); + fileChannelOpen(); + } + + /** + * These check look obvious but they are not on low Api level due to the interface injection. + */ + @SuppressWarnings("all") + private static void instanceTest() throws IOException { + Path tmp = Files.createTempFile("tmp", ".txt"); + System.out.println( + new FileInputStream(tmp.toFile()).getChannel() instanceof SeekableByteChannel); + System.out.println( + new FileOutputStream(tmp.toFile()).getChannel() instanceof SeekableByteChannel); + System.out.println( + new RandomAccessFile(tmp.toFile(), "rw").getChannel() instanceof SeekableByteChannel); + System.out.println( + Files.newByteChannel(tmp, StandardOpenOption.READ) instanceof SeekableByteChannel); + } + + private static void fosTest() throws IOException { + String toWrite = "The monkey eats..."; + Path tmp = Files.createTempFile("fos", ".txt"); + + ByteBuffer byteBuffer = ByteBuffer.wrap(toWrite.getBytes(StandardCharsets.UTF_8)); + FileOutputStream fos = new FileOutputStream(tmp.toFile()); + FileChannel channel = fos.getChannel(); + channel.write(byteBuffer); + + List<String> lines = Files.readAllLines(tmp); + System.out.println(lines.get(0)); + } + + private static void fileChannelOpen() throws IOException { + fileChannelOpenTest(); + fileChannelOpenSetTest(); + fileChannelOpenLockTest(); + } + + private static void fileChannelOpenLockTest() throws IOException { + Path tmp = Files.createTempFile("lock", ".txt"); + String contents = "Bananas!"; + Files.write(tmp, contents.getBytes(StandardCharsets.UTF_8)); + FileChannel fc = FileChannel.open(tmp, StandardOpenOption.READ); + ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length()); + fc.read(byteBuffer); + System.out.println(new String(byteBuffer.array())); + fc.close(); + } + + private static void fileChannelOpenTest() throws IOException { + Path tmp = Files.createTempFile("a", ".txt"); + String contents = "Bananas!"; + Files.write(tmp, contents.getBytes(StandardCharsets.UTF_8)); + FileChannel fc = FileChannel.open(tmp, StandardOpenOption.READ, StandardOpenOption.WRITE); + ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length()); + // Extra indirection through the lock. + fc.lock().channel().read(byteBuffer); + System.out.println(new String(byteBuffer.array())); + fc.close(); + } + + private static void fileChannelOpenSetTest() throws IOException { + Path tmp = Files.createTempFile("b", ".txt"); + String contents = "Bananas!"; + Files.write(tmp, contents.getBytes(StandardCharsets.UTF_8)); + Set<OpenOption> options = new HashSet<>(); + options.add(StandardOpenOption.READ); + FileChannel fc = FileChannel.open(tmp, options); + ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length()); + fc.read(byteBuffer); + System.out.println(new String(byteBuffer.array())); + fc.close(); + } + + private static void fisTest() throws IOException { + fisOwner(); + fisNotOwner(true); + fisNotOwner(false); + fisOwnerTryResources(); + } + + private static void fisNotOwner(boolean closeFirst) throws IOException { + String toWrite = "Hello World! "; + String toWriteFIS = "Bye bye. "; + Path tmp = Files.createTempFile("tmp", ".txt"); + Files.write(tmp, (toWrite + toWriteFIS).getBytes(StandardCharsets.UTF_8)); + + ByteBuffer byteBuffer = ByteBuffer.allocate(toWrite.length()); + ByteBuffer byteBufferFIS = ByteBuffer.allocate(toWriteFIS.length()); + FileInputStream fileInputStream = new FileInputStream(tmp.toFile()); + FileDescriptor fd = fileInputStream.getFD(); + FileInputStream fis2 = new FileInputStream(fd); + fileInputStream.getChannel().read(byteBuffer); + fis2.getChannel().read(byteBufferFIS); + + if (closeFirst) { + fileInputStream.close(); + fis2.close(); + } else { + fis2.close(); + fileInputStream.close(); + } + + System.out.println(new String(byteBuffer.array())); + System.out.println(new String(byteBufferFIS.array())); + } + + private static void fisOwner() throws IOException { + String toWrite = "Hello World! "; + Path tmp = Files.createTempFile("tmp", ".txt"); + Files.write(tmp, toWrite.getBytes(StandardCharsets.UTF_8)); + + ByteBuffer byteBuffer = ByteBuffer.allocate(toWrite.length()); + FileInputStream fileInputStream = new FileInputStream(tmp.toFile()); + fileInputStream.getChannel().read(byteBuffer); + fileInputStream.close(); + + System.out.println(new String(byteBuffer.array())); + } + + private static void fisOwnerTryResources() throws IOException { + String toWrite = "Hello World! "; + Path tmp = Files.createTempFile("tmp", ".txt"); + Files.write(tmp, toWrite.getBytes(StandardCharsets.UTF_8)); + + ByteBuffer byteBuffer = ByteBuffer.allocate(toWrite.length()); + try (FileInputStream fileInputStream = new FileInputStream(tmp.toFile())) { + fileInputStream.getChannel().read(byteBuffer); + } + + System.out.println(new String(byteBuffer.array())); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java index e6015ad..fb9ca6a 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
@@ -58,21 +58,6 @@ "tmp", "/", "true"); - private static final String EXPECTED_RESULT_DESUGARING_FILE_SYSTEM_PLATFORM_CHANNEL = - StringUtils.lines( - "bytes written: 11", - "String written: Hello World", - "bytes read: 11", - "String read: Hello World", - "unsupported", - "unsupported", - "null", - "true", - "unsupported", - "j$.nio.file.attribute", - "tmp", - "/", - "true"); private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM_DESUGARING = StringUtils.lines( "bytes written: 11", @@ -136,9 +121,7 @@ ? EXPECTED_RESULT_PLATFORM_FILE_SYSTEM_DESUGARING : EXPECTED_RESULT_PLATFORM_FILE_SYSTEM; } - return libraryDesugaringSpecification.hasNioChannelDesugaring(parameters) - ? EXPECTED_RESULT_DESUGARING_FILE_SYSTEM - : EXPECTED_RESULT_DESUGARING_FILE_SYSTEM_PLATFORM_CHANNEL; + return EXPECTED_RESULT_DESUGARING_FILE_SYSTEM; } @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java index 09bb322..d6724b5 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/JavaTimeJDK11Test.java
@@ -6,6 +6,7 @@ import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11; +import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_LEGACY; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -69,7 +70,7 @@ public static List<Object[]> data() { return buildParameters( getTestParameters().withDexRuntimes().withAllApiLevels().build(), - ImmutableList.of(JDK11, JDK11_PATH), + ImmutableList.of(JDK11, JDK11_PATH, JDK11_LEGACY), DEFAULT_SPECIFICATIONS); } @@ -101,7 +102,8 @@ .forEach( i -> { if (i.isInvoke()) { - if (libraryDesugaringSpecification.hasTimeDesugaring(parameters)) { + if (libraryDesugaringSpecification.hasTimeDesugaring(parameters) + && libraryDesugaringSpecification != JDK11_LEGACY) { checkInvokeTime(i, "j$.time.Duration", "j$.time.LocalTime"); return; }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java index 4c77e47..84e5f4b 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/NewCollectorsTest.java
@@ -6,7 +6,9 @@ import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11; +import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_LEGACY; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -46,7 +48,7 @@ public static List<Object[]> data() { return buildParameters( getTestParameters().withDexRuntimes().withAllApiLevels().build(), - ImmutableList.of(JDK11, JDK11_PATH), + ImmutableList.of(JDK11, JDK11_PATH, JDK11_LEGACY), DEFAULT_SPECIFICATIONS); } @@ -76,7 +78,11 @@ if (libraryDesugaringSpecification.hasEmulatedInterfaceDesugaring(parameters)) { // Collectors is not present, all calls to the j$ version. assertTrue(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.Collectors")); - assertFalse(anyStaticInvokeToHolder(methodSubject, "j$.util.stream.DesugarCollectors")); + // In JDK11_LEGACY DesugarCollectors is used whenever possible, in other specifications, + // it is used only when needed. + assertEquals( + libraryDesugaringSpecification == JDK11_LEGACY, + anyStaticInvokeToHolder(methodSubject, "j$.util.stream.DesugarCollectors")); assertFalse(anyStaticInvokeToHolder(methodSubject, "java.util.stream.Collectors")); return; }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java index 1e56f81..7de441c 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -206,7 +206,7 @@ if (artProcessResult.exitCode != 0) { System.out.println(artProcessResult); } - assertEquals(0, artProcessResult.exitCode); + assertEquals(artProcessResult.stderr, 0, artProcessResult.exitCode); assertProgramsEqual( "The output of R8/JVM in-process and R8/ART external differ.", outputThroughCf,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java index 6245fec..3d76b78 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
@@ -37,7 +37,7 @@ public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 26, -1, 10000, -1); public static Descriptor EMPTY_DESCRIPTOR_24 = new Descriptor(-1, -1, -1, 24, -1); public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 26, 26, 10000, -1); - public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(10000, 10000, -1, 10000, 24); + public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(24, 26, -1, 32, 24); private static class Descriptor { @@ -166,7 +166,7 @@ // The legacy specification is not using the undesugared JAR. DESUGARED_JDK_11_LIB_JAR, "jdk11/desugar_jdk_libs_legacy.json", - AndroidApiLevel.R, + AndroidApiLevel.T, JDK11_LEGACY_DESCRIPTOR, LEGACY); public static final LibraryDesugaringSpecification RELEASED_1_0_9 =
diff --git a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java index c8ea9ec..6242204 100644 --- a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java +++ b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
@@ -6,11 +6,10 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.D8Command; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.VmTestRunner; -import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan; import com.android.tools.r8.utils.AndroidApiLevel; import java.nio.file.Paths; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,7 +19,7 @@ private static final String JAR = "third_party/framework/framework_14082017.jar"; @Test - @IgnoreIfVmOlderThan(Version.V7_0_0) + @Ignore("b/259195080") public void verifyDebugBuild() throws Exception { runAndCheckVerification( D8Command.builder() @@ -32,7 +31,7 @@ } @Test - @IgnoreIfVmOlderThan(Version.V7_0_0) + @Ignore("b/259195080") public void verifyReleaseBuild() throws Exception { runAndCheckVerification( D8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java index 1e43777..7141ad08 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
@@ -53,8 +53,7 @@ .addProgramClasses(I.class, A.class) .addProgramClassFileData(getTransformedMain()) .addKeepMainRule(Main.class) - // Keep get() to prevent that we optimize it into having static return type A. - .addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }") + .addKeepMethodRules(Reference.methodFromMethod(Main.class.getDeclaredMethod("get"))) .addOptionsModification( options -> options
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java index 0f07d76..d9a6eaf 100644 --- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -109,7 +109,7 @@ .addTarget( target( buildClassItem(CLASS) - .setMembersPattern(defaultInitializerPattern()) + .setMemberPattern(defaultInitializerPattern()) .build())) .build()) .build(); @@ -146,7 +146,7 @@ .addTarget( target( buildClassItem(CLASS) - .setMembersPattern(defaultInitializerPattern()) + .setMemberPattern(defaultInitializerPattern()) .build())) .build()) .build(); @@ -169,7 +169,7 @@ return KeepItemPattern.builder().setClassPattern(KeepQualifiedClassNamePattern.exact(typeName)); } - private KeepMembersPattern defaultInitializerPattern() { + private KeepMemberPattern defaultInitializerPattern() { return KeepMethodPattern.builder() .setNamePattern(KeepMethodNamePattern.initializer()) .setParametersPattern(KeepMethodParametersPattern.none())
diff --git a/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java b/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java new file mode 100644 index 0000000..94001ec --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractorTest.java
@@ -0,0 +1,63 @@ +// Copyright (c) 2022, 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.keepanno.keeprules; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.keepanno.asm.KeepEdgeReader; +import com.android.tools.r8.keepanno.ast.KeepEdge; +import com.android.tools.r8.keepanno.testsource.KeepClassAndDefaultConstructorSource; +import com.android.tools.r8.keepanno.testsource.KeepSourceEdges; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepRuleExtractorTest extends TestBase { + + private static final Class<?> SOURCE = KeepClassAndDefaultConstructorSource.class; + private static final String EXPECTED = KeepSourceEdges.getExpected(SOURCE); + private static final Path KEEP_ANNO_PATH = + Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "keepanno"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevels().build(); + } + + public KeepRuleExtractorTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + List<String> rules = getKeepRulesForClass(SOURCE); + testForR8(parameters.getBackend()) + .addClasspathFiles(KEEP_ANNO_PATH) + .addProgramClassesAndInnerClasses(SOURCE) + .addKeepRules(rules) + .addKeepMainRule(SOURCE) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), SOURCE) + .assertSuccessWithOutput(EXPECTED); + } + + private List<String> getKeepRulesForClass(Class<?> clazz) throws IOException { + Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(ToolHelper.getClassAsBytes(clazz)); + List<String> rules = new ArrayList<>(); + KeepRuleExtractor extractor = new KeepRuleExtractor(rules::add); + keepEdges.forEach(extractor::extract); + return rules; + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java b/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java index eb7b616..83f60a3 100644 --- a/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java
@@ -36,6 +36,7 @@ private static final Path KEEP_ANNO_PATH = Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "keepanno"); private static final Class<?> SOURCE = KeepClassAndDefaultConstructorSource.class; + private static final String EXPECTED = KeepSourceEdges.getExpected(SOURCE); private final TestParameters parameters; @@ -63,6 +64,12 @@ checkSynthesizedKeepEdgeClass(inspector, out); // The source is added as a classpath name but not part of the compilation unit output. assertThat(inspector.clazz(SOURCE), isAbsent()); + + testForJvm() + .addProgramClasses(SOURCE, KeepClassAndDefaultConstructorSource.A.class) + .addProgramFiles(out) + .run(parameters.getRuntime(), SOURCE) + .assertSuccessWithOutput(EXPECTED); } @Test @@ -78,7 +85,7 @@ testForJvm() .addProgramFiles(out) .run(parameters.getRuntime(), SOURCE) - .assertSuccessWithOutputLines("A is alive!") + .assertSuccessWithOutput(EXPECTED) .inspect( inspector -> { assertThat(inspector.clazz(SOURCE), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java index ca83a46..b9fb2b6 100644 --- a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java +++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java
@@ -9,9 +9,11 @@ @KeepEdge( consequences = { // Keep the class to allow lookup of it. - @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class), + @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.A.class), // Keep the default constructor. - @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class, methodName = "<init>") + @KeepTarget( + classConstant = KeepClassAndDefaultConstructorSource.A.class, + methodName = "<init>") }) public class KeepClassAndDefaultConstructorSource {
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java index b6369de..5637edf 100644 --- a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java +++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.keepanno.ast.KeepMethodPattern; import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; import com.android.tools.r8.keepanno.ast.KeepTarget; +import com.android.tools.r8.utils.StringUtils; import java.util.Collections; import java.util.Set; @@ -27,8 +28,19 @@ throw new RuntimeException(); } + public static String getExpected(Class<?> clazz) { + if (clazz.equals(KeepClassAndDefaultConstructorSource.class)) { + return getKeepClassAndDefaultConstructorSourceExpected(); + } + throw new RuntimeException(); + } + + public static String getKeepClassAndDefaultConstructorSourceExpected() { + return StringUtils.lines("A is alive!"); + } + public static Set<KeepEdge> getKeepClassAndDefaultConstructorSourceEdges() { - Class<?> clazz = KeepClassAndDefaultConstructorSource.class; + Class<?> clazz = KeepClassAndDefaultConstructorSource.A.class; // Build the class target. KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName()); KeepItemPattern classItem = KeepItemPattern.builder().setClassPattern(name).build(); @@ -37,10 +49,7 @@ KeepMethodPattern constructorMethod = KeepMethodPattern.builder().setNamePattern(KeepMethodNamePattern.exact("<init>")).build(); KeepItemPattern constructorItem = - KeepItemPattern.builder() - .setClassPattern(name) - .setMembersPattern(constructorMethod) - .build(); + KeepItemPattern.builder().setClassPattern(name).setMemberPattern(constructorMethod).build(); KeepTarget constructorTarget = KeepTarget.builder().setItem(constructorItem).build(); // The consequet set is the class an its constructor. KeepConsequences consequences =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java index 9fd2a2e..87dc848 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -133,9 +133,7 @@ if (kotlinParameters.isNewerThan(KOTLINC_1_7_0)) { assertThat( compileResult.stderr, - containsString( - "the feature \"references to synthetic java properties\" is only available since" - + " language version 1.9")); + containsString("the feature \"references to synthetic java properties\"")); } else { assertThat( compileResult.stderr,
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java index 83ea780..c544c9e 100644 --- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java +++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -16,10 +16,12 @@ import com.android.tools.r8.dex.code.DexConst4; import com.android.tools.r8.dex.code.DexConstClass; import com.android.tools.r8.dex.code.DexConstString; +import com.android.tools.r8.dex.code.DexFilledNewArray; import com.android.tools.r8.dex.code.DexInvokeDirect; import com.android.tools.r8.dex.code.DexInvokeStatic; import com.android.tools.r8.dex.code.DexInvokeVirtual; import com.android.tools.r8.dex.code.DexIputObject; +import com.android.tools.r8.dex.code.DexMoveResultObject; import com.android.tools.r8.dex.code.DexNewArray; import com.android.tools.r8.dex.code.DexReturnVoid; import com.android.tools.r8.dex.code.DexSgetObject; @@ -639,7 +641,8 @@ + "}", "-keep class " + CLASS_NAME, "-keep class R { *; }"); - CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector(); + CodeInspector inspector = + compileWithR8(builder, testBuilder -> testBuilder.addKeepRules(pgConfigs)).inspector(); ClassSubject clazz = inspector.clazz(CLASS_NAME); assertTrue(clazz.isPresent()); @@ -647,19 +650,33 @@ assertNotNull(method); DexCode code = method.getCode().asDexCode(); - checkInstructions( - code, - ImmutableList.of( - DexInvokeDirect.class, - DexConst4.class, - DexNewArray.class, - DexConst4.class, - DexConstClass.class, - DexAputObject.class, - DexConstString.class, - DexInvokeStatic.class, - DexReturnVoid.class)); - DexConstString constString = (DexConstString) code.instructions[6]; + // Accept either array construction style (differs based on minSdkVersion). + if (code.instructions[2].getClass() == DexFilledNewArray.class) { + checkInstructions( + code, + ImmutableList.of( + DexInvokeDirect.class, + DexConstClass.class, + DexFilledNewArray.class, + DexMoveResultObject.class, + DexConstString.class, + DexInvokeStatic.class, + DexReturnVoid.class)); + } else { + checkInstructions( + code, + ImmutableList.of( + DexInvokeDirect.class, + DexConst4.class, + DexNewArray.class, + DexConst4.class, + DexConstClass.class, + DexAputObject.class, + DexConstString.class, + DexInvokeStatic.class, + DexReturnVoid.class)); + } + DexConstString constString = (DexConstString) code.instructions[code.instructions.length - 3]; assertEquals("foo", constString.getString().toString()); } @@ -700,7 +717,8 @@ + "}", "-keep class " + CLASS_NAME, "-keep,allowobfuscation class R { *; }"); - CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector(); + CodeInspector inspector = + compileWithR8(builder, testBuilder -> testBuilder.addKeepRules(pgConfigs)).inspector(); ClassSubject clazz = inspector.clazz(CLASS_NAME); assertTrue(clazz.isPresent()); @@ -708,19 +726,33 @@ assertNotNull(method); DexCode code = method.getCode().asDexCode(); - checkInstructions( - code, - ImmutableList.of( - DexInvokeDirect.class, - DexConst4.class, - DexNewArray.class, - DexConst4.class, - DexConstClass.class, - DexAputObject.class, - DexConstString.class, - DexInvokeStatic.class, - DexReturnVoid.class)); - DexConstString constString = (DexConstString) code.instructions[6]; + // Accept either array construction style (differs based on minSdkVersion). + if (code.instructions[2].getClass() == DexFilledNewArray.class) { + checkInstructions( + code, + ImmutableList.of( + DexInvokeDirect.class, + DexConstClass.class, + DexFilledNewArray.class, + DexMoveResultObject.class, + DexConstString.class, + DexInvokeStatic.class, + DexReturnVoid.class)); + } else { + checkInstructions( + code, + ImmutableList.of( + DexInvokeDirect.class, + DexConst4.class, + DexNewArray.class, + DexConst4.class, + DexConstClass.class, + DexAputObject.class, + DexConstString.class, + DexInvokeStatic.class, + DexReturnVoid.class)); + } + DexConstString constString = (DexConstString) code.instructions[code.instructions.length - 3]; assertNotEquals("foo", constString.getString().toString()); }
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationDestinationOverrideLibraryTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationDestinationOverrideLibraryTest.java new file mode 100644 index 0000000..3b7a115 --- /dev/null +++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationDestinationOverrideLibraryTest.java
@@ -0,0 +1,111 @@ +// Copyright (c) 2022, 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.optimize.proto; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThrows; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoMethodStaticizing; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ProtoNormalizationDestinationOverrideLibraryTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private static final String[] EXPECTED = new String[] {"LibraryMethod421337"}; + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(Main.class, ProgramClass.class, X.class) + .addDefaultRuntimeLibrary(parameters) + .addLibraryClasses(LibraryClass.class) + .addRunClasspathFiles(buildOnDexRuntime(parameters, LibraryClass.class)) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + @Test + public void testR8() throws Exception { + // TODO(b/258720808): We should not fail compilation. + assertThrows( + CompilationFailedException.class, + () -> + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class, ProgramClass.class, X.class) + .addDefaultRuntimeLibrary(parameters) + .addLibraryClasses(LibraryClass.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(Main.class) + .addDontObfuscate() + .enableInliningAnnotations() + .enableNoMethodStaticizingAnnotations() + .compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertErrorMessageThatMatches( + containsString( + "went from not overriding a library method to overriding a library" + + " method")); + })); + } + + public static class LibraryClass { + + public void foo(int i1, int i2, String bar) { + System.out.println(bar + i1 + i2); + } + + public static void callFoo(LibraryClass clazz) { + clazz.foo(42, 1337, "LibraryMethod"); + } + } + + public static class ProgramClass extends LibraryClass { + @NeverInline + @NoMethodStaticizing + public void foo(String bar, int i1, int i2) { + System.out.println(i1 + i2 + bar); + } + } + + // Needs to have a class name that is after lexicographically than ProgramClass. + public static class X { + @NeverInline + public void foo(int i1, String bar, int i2) { + System.out.println(i1 + bar + i2); + } + } + + public static class Main { + + public static void main(String[] args) { + ProgramClass programClass = new ProgramClass(); + X otherProgramClass = new X(); + if (args.length == 1) { + programClass.foo("Hello World", 1, 1); + otherProgramClass.foo(1, "Hello World", 1); + } else if (args.length > 1) { + programClass.foo("Goodbye World", 2, 2); + otherProgramClass.foo(2, "Goodbye World", 2); + } + LibraryClass.callFoo(programClass); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java index ba776e4..c179493 100644 --- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
@@ -33,6 +33,20 @@ return getTestParameters().withAllRuntimesAndApiLevels().build(); } + private final String[] EXPECTED = + new String[] {"A::foo", "B", "A", "B::foo", "B", "A", "B::foo", "B", "A"}; + + private final String[] R8_EXPECTED = + new String[] {"A::foo", "B", "A", "A::foo", "B", "A", "B::foo", "B", "A"}; + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addInnerClasses(getClass()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + @Test public void test() throws Exception { testForR8(parameters.getBackend()) @@ -40,20 +54,23 @@ .addKeepMainRule(Main.class) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() - // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes. .addDontObfuscate() + .addKeepClassAndMembersRules(B.class) .setMinApi(parameters.getApiLevel()) .compile() + .run(parameters.getRuntime(), Main.class) + // TODO(b/258720808): We should not produce incorrect results. + .assertSuccessWithOutputLines(R8_EXPECTED) .inspect( inspector -> { - ClassSubject aClassSubject = inspector.clazz(A.class); - assertThat(aClassSubject, isPresent()); - ClassSubject bClassSubject = inspector.clazz(B.class); assertThat(bClassSubject, isPresent()); - TypeSubject aTypeSubject = aClassSubject.asTypeSubject(); + ClassSubject aClassSubject = inspector.clazz(A.class); + assertThat(aClassSubject, isPresent()); + TypeSubject bTypeSubject = bClassSubject.asTypeSubject(); + TypeSubject aTypeSubject = aClassSubject.asTypeSubject(); MethodSubject fooMethodSubject = aClassSubject.uniqueMethodWithOriginalName("foo"); assertThat(fooMethodSubject, isPresent()); @@ -62,12 +79,10 @@ // TODO(b/173398086): Consider rewriting B.foo(B, A) to B.foo(A, B, C) instead of // B.foo$1(A, B). MethodSubject otherFooMethodSubject = - bClassSubject.uniqueMethodWithOriginalName("foo$1"); + bClassSubject.uniqueMethodWithOriginalName("foo"); assertThat(otherFooMethodSubject, isPresent()); assertThat(otherFooMethodSubject, hasParameters(aTypeSubject, bTypeSubject)); - }) - .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines("A", "B", "A", "B"); + }); } static class Main { @@ -75,16 +90,18 @@ public static void main(String[] args) { A a = new A(); B b = new B(); + a.foo(b, a); a.foo(a, b); - b.foo(b, a); + b.foo(a, b); } } @NoVerticalClassMerging - static class A { + static class B { @NeverInline public void foo(A a, B b) { + System.out.println("B::foo"); System.out.println(a); System.out.println(b); } @@ -95,10 +112,11 @@ } } - static class B extends A { + static class A extends B { @NeverInline public void foo(B b, A a) { + System.out.println("A::foo"); System.out.println(a); System.out.println(b); }
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java index c233d49..e665c12 100644 --- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -41,7 +41,8 @@ RetracePartitionRoundTripTest.ApiTest.class, RetracePartitionJoinNoMetadataTest.ApiTest.class, RetracePartitionSerializedObfuscatedKeyTest.ApiTest.class, - RetracePartitionRoundTripInlineTest.ApiTest.class); + RetracePartitionRoundTripInlineTest.ApiTest.class, + RetraceApiTypeResultTest.ApiTest.class); public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY = ImmutableList.of(RetraceApiResidualSignatureTest.ApiTest.class);
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java new file mode 100644 index 0000000..4d76e2c --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTypeResultTest.java
@@ -0,0 +1,67 @@ +// Copyright (c) 2022, 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.retrace.api; + +import static junit.framework.TestCase.assertEquals; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.references.TypeReference; +import com.android.tools.r8.retrace.ProguardMapProducer; +import com.android.tools.r8.retrace.RetraceTypeElement; +import com.android.tools.r8.retrace.Retracer; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class RetraceApiTypeResultTest extends RetraceApiTestBase { + + public RetraceApiTypeResultTest(TestParameters parameters) { + super(parameters); + } + + @Override + protected Class<? extends RetraceApiBinaryTest> binaryTestClass() { + return ApiTest.class; + } + + public static class ApiTest implements RetraceApiBinaryTest { + + private final TypeReference minifiedName = Reference.typeFromTypeName("a[]"); + private final TypeReference original = Reference.typeFromTypeName("some.Class[]"); + + private static final String mapping = + "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\nsome.Class -> a:\n"; + + @Test + public void testRetraceClassArray() { + List<RetraceTypeElement> collect = + Retracer.createDefault( + ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {}) + .retraceType(minifiedName) + .stream() + .collect(Collectors.toList()); + assertEquals(1, collect.size()); + assertEquals(original, collect.get(0).getType().getTypeReference()); + } + + @Test + public void testRetracePrimitiveArray() { + TypeReference intArr = Reference.typeFromTypeName("int[][]"); + List<RetraceTypeElement> collect = + Retracer.createDefault( + ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {}) + .retraceType(intArr) + .stream() + .collect(Collectors.toList()); + assertEquals(1, collect.size()); + assertEquals(intArr, collect.get(0).getType().getTypeReference()); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java index 4a76a6a..513dc73 100644 --- a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayWithDataLengthRewriteTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.graph.AppView; @@ -15,6 +16,7 @@ import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -33,7 +35,7 @@ this.parameters = parameters; } - private static final String[] expectedOutput = {"3"}; + private static final String[] expectedOutput = {"3", "2"}; @Test public void d8() throws Exception { @@ -44,7 +46,7 @@ .addOptionsModification(opt -> opt.testing.irModifier = this::transformArray) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines(expectedOutput) - .inspect(this::assertNoArrayLength); + .inspect(i -> inspect(i, true)); } @Test @@ -54,32 +56,52 @@ .addProgramClasses(Main.class) .addOptionsModification(opt -> opt.testing.irModifier = this::transformArray) .addKeepMainRule(Main.class) + .enableInliningAnnotations() .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines(expectedOutput) - .inspect(this::assertNoArrayLength); + .inspect(i -> inspect(i, false)); } private void transformArray(IRCode irCode, AppView<?> appView) { - if (irCode.context().getReference().getName().toString().contains("main")) { new CodeRewriter(appView).simplifyArrayConstruction(irCode); + String name = irCode.context().getReference().getName().toString(); + if (name.contains("filledArrayData")) { assertTrue(irCode.streamInstructions().anyMatch(Instruction::isNewArrayFilledData)); + } else if (name.contains("filledNewArray")) { + assertTrue(irCode.streamInstructions().anyMatch(Instruction::isInvokeNewArray)); } } - private void assertNoArrayLength(CodeInspector inspector) { + private void inspect(CodeInspector inspector, boolean d8) { ClassSubject mainClass = inspector.clazz(Main.class); assertTrue(mainClass.isPresent()); - assertTrue( - mainClass.mainMethod().streamInstructions().noneMatch(InstructionSubject::isArrayLength)); + MethodSubject filledArrayData = mainClass.uniqueMethodWithOriginalName("filledArrayData"); + assertTrue(filledArrayData.streamInstructions().noneMatch(InstructionSubject::isArrayLength)); + if (!d8) { + MethodSubject filledNewArray = mainClass.uniqueMethodWithOriginalName("filledNewArray"); + assertTrue(filledNewArray.streamInstructions().noneMatch(InstructionSubject::isArrayLength)); + } } public static final class Main { + @NeverInline + public static void filledArrayData() { + short[] values = new short[3]; + values[0] = 5; + values[1] = 6; + values[2] = 1; + System.out.println(values.length); + } + + @NeverInline + public static void filledNewArray() { + int[] values = new int[] {7, 8}; + System.out.println(values.length); + } + public static void main(String[] args) { - int[] ints = new int[3]; - ints[0] = 5; - ints[1] = 6; - ints[2] = 1; - System.out.println(ints.length); + filledArrayData(); + filledNewArray(); } } }
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java new file mode 100644 index 0000000..939753f --- /dev/null +++ b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
@@ -0,0 +1,774 @@ +// Copyright (c) 2022, 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.rewrite.arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.Keep; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.dex.code.DexFillArrayData; +import com.android.tools.r8.dex.code.DexFilledNewArray; +import com.android.tools.r8.dex.code.DexFilledNewArrayRange; +import com.android.tools.r8.dex.code.DexNewArray; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.transformers.ClassFileTransformer; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import com.beust.jcommander.internal.Lists; +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SimplifyArrayConstructionTest extends TestBase { + @Parameters(name = "{0}, mode = {1}") + public static Iterable<?> data() { + return buildParameters( + getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(), + CompilationMode.values()); + } + + private final TestParameters parameters; + private final CompilationMode compilationMode; + + public SimplifyArrayConstructionTest(TestParameters parameters, CompilationMode compilationMode) { + this.parameters = parameters; + this.compilationMode = compilationMode; + } + + private static final Class<?>[] DEX_ARRAY_INSTRUCTIONS = { + DexNewArray.class, DexFilledNewArray.class, DexFilledNewArrayRange.class, DexFillArrayData.class + }; + + private static final String[] EXPECTED_OUTPUT = { + "[a]", + "[a, 1, null]", + "[1, null]", + "[1, null, 2]", + "[1, null, 2]", + "[1]", + "[1, 2]", + "[1, 2, 3, 4, 5]", + "[1]", + "[a, 1, null, d, e, f]", + "[1, null, 3, null, null, 6]", + "[1, 2, 3, 4, 5, 6]", + "[true, false]", + "[1, 2]", + "[1, 2]", + "[1, 2]", + "[1.0, 2.0]", + "[1.0, 2.0]", + "[]", + "[]", + "[true]", + "[1]", + "[1]", + "[1]", + "[1.0]", + "[1.0]", + "[0, 1]", + "[1, null]", + "[a]", + "[0, 1]", + "200", + "[0, 1, 2, 3, 4]", + "[4, 0, 0, 0, 0]", + "[4, 1, 2, 3, 4]", + "[0, 1, 2]", + "[0]", + "[0, 1, 2]", + "[1, 2, 3]", + "[1, 2, 3, 4, 5, 6]", + "[0]", + "[null, null]", + }; + + private static final byte[] TRANSFORMED_MAIN = transformedMain(); + + @Test + public void testRuntime() throws Exception { + assumeFalse(compilationMode == CompilationMode.DEBUG); + testForRuntime( + parameters.getRuntime(), + d8TestBuilder -> + d8TestBuilder.setMinApi(parameters.getApiLevel()).setMode(compilationMode)) + .addProgramClassFileData(TRANSFORMED_MAIN) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED_OUTPUT) + .inspect(inspector -> inspect(inspector, false)); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification( + options -> + options + .getOpenClosedInterfacesOptions() + .suppressSingleOpenInterface(Reference.classFromClass(Serializable.class))) + .setMode(compilationMode) + .addProgramClassFileData(TRANSFORMED_MAIN) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .addKeepAnnotation() + .addKeepRules("-keepclassmembers class * { @com.android.tools.r8.Keep *; }") + .addKeepRules("-assumenosideeffects class * { *** assumedNullField return null; }") + .addKeepRules("-assumenosideeffects class * { *** assumedNonNullField return _NONNULL_; }") + .addDontObfuscate() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED_OUTPUT) + .inspect(inspector -> inspect(inspector, true)); + } + + private static byte[] transformedMain() { + try { + return transformer(Main.class) + .transformMethodInsnInMethod( + "interfaceArrayWithRawObject", + (opcode, owner, name, descriptor, isInterface, visitor) -> { + if (name.equals("getObjectThatImplementsSerializable")) { + visitor.visitMethodInsn(opcode, owner, name, "()Ljava/lang/Object;", isInterface); + } else { + visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + }) + .setReturnType( + ClassFileTransformer.MethodPredicate.onName("getObjectThatImplementsSerializable"), + Object.class.getTypeName()) + .transform(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void inspect(CodeInspector inspector, boolean isR8) { + if (parameters.isCfRuntime()) { + return; + } + ClassSubject mainClass = inspector.clazz(Main.class); + assertTrue(mainClass.isPresent()); + + MethodSubject stringArrays = mainClass.uniqueMethodWithOriginalName("stringArrays"); + MethodSubject referenceArraysNoCasts = + mainClass.uniqueMethodWithOriginalName("referenceArraysNoCasts"); + MethodSubject referenceArraysWithSubclasses = + mainClass.uniqueMethodWithOriginalName("referenceArraysWithSubclasses"); + MethodSubject interfaceArrayWithRawObject = + mainClass.uniqueMethodWithOriginalName("interfaceArrayWithRawObject"); + + MethodSubject phiFilledNewArray = mainClass.uniqueMethodWithOriginalName("phiFilledNewArray"); + MethodSubject intsThatUseFilledNewArray = + mainClass.uniqueMethodWithOriginalName("intsThatUseFilledNewArray"); + MethodSubject twoDimensionalArrays = + mainClass.uniqueMethodWithOriginalName("twoDimensionalArrays"); + MethodSubject objectArraysFilledNewArrayRange = + mainClass.uniqueMethodWithOriginalName("objectArraysFilledNewArrayRange"); + MethodSubject arraysThatUseFilledData = + mainClass.uniqueMethodWithOriginalName("arraysThatUseFilledData"); + MethodSubject arraysThatUseNewArrayEmpty = + mainClass.uniqueMethodWithOriginalName("arraysThatUseNewArrayEmpty"); + MethodSubject reversedArray = mainClass.uniqueMethodWithOriginalName("reversedArray"); + MethodSubject arrayWithCorrectCountButIncompleteCoverage = + mainClass.uniqueMethodWithOriginalName("arrayWithCorrectCountButIncompleteCoverage"); + MethodSubject arrayWithExtraInitialPuts = + mainClass.uniqueMethodWithOriginalName("arrayWithExtraInitialPuts"); + MethodSubject catchHandlerThrowing = + mainClass.uniqueMethodWithOriginalName("catchHandlerThrowing"); + MethodSubject catchHandlerNonThrowingFilledNewArray = + mainClass.uniqueMethodWithOriginalName("catchHandlerNonThrowingFilledNewArray"); + MethodSubject catchHandlerNonThrowingFillArrayData = + mainClass.uniqueMethodWithOriginalName("catchHandlerNonThrowingFillArrayData"); + MethodSubject assumedValues = mainClass.uniqueMethodWithOriginalName("assumedValues"); + + assertArrayTypes(arraysThatUseNewArrayEmpty, DexNewArray.class); + assertArrayTypes(intsThatUseFilledNewArray, DexFilledNewArray.class); + assertFilledArrayData(arraysThatUseFilledData); + + if (compilationMode == CompilationMode.DEBUG) { + // The explicit assignments can't be collapsed without breaking the debugger's ability to + // visit each line. + assertArrayTypes(reversedArray, DexNewArray.class); + } else { + assertArrayTypes(reversedArray, DexFilledNewArray.class); + } + + // Cannot use filled-new-array of String before K. + if (parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) { + assertArrayTypes(stringArrays, DexNewArray.class); + } else { + assertArrayTypes(stringArrays, DexFilledNewArray.class); + } + // Cannot use filled-new-array of Object before L. + if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) { + assertArrayTypes(referenceArraysNoCasts, DexNewArray.class); + assertArrayTypes(referenceArraysWithSubclasses, DexNewArray.class); + assertArrayTypes(phiFilledNewArray, DexNewArray.class); + assertArrayTypes(objectArraysFilledNewArrayRange, DexNewArray.class); + assertArrayTypes(twoDimensionalArrays, DexNewArray.class); + assertArrayTypes(assumedValues, DexNewArray.class); + } else { + assertArrayTypes(referenceArraysNoCasts, DexFilledNewArray.class); + // TODO(b/246971330): Add support for arrays with subtypes. + if (isR8) { + assertArrayTypes(referenceArraysWithSubclasses, DexFilledNewArray.class); + } else { + assertArrayTypes(referenceArraysWithSubclasses, DexNewArray.class); + } + + // TODO(b/246971330): Add support for arrays whose values have conditionals. + // assertArrayTypes(phiFilledNewArray, DexFilledNewArray.class); + + assertArrayTypes(objectArraysFilledNewArrayRange, DexFilledNewArrayRange.class); + + if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) { + assertArrayTypes(twoDimensionalArrays, DexFilledNewArray.class); + } else { + // No need to assert this case. If it's wrong, Dalvik verify errors cause test failures. + } + + assertArrayTypes(assumedValues, DexFilledNewArray.class); + } + // filled-new-array fails verification when types of parameters are not subclasses (aput-object + // does not). + assertArrayTypes(interfaceArrayWithRawObject, DexNewArray.class); + + // These could be optimized to use InvokeNewArray, but they seem like rare code patterns so we + // haven't bothered. + assertArrayTypes(arrayWithExtraInitialPuts, DexNewArray.class); + assertArrayTypes(arrayWithCorrectCountButIncompleteCoverage, DexNewArray.class); + + assertArrayTypes(catchHandlerThrowing, DexNewArray.class); + assertArrayTypes(catchHandlerNonThrowingFillArrayData, DexNewArray.class); + assertArrayTypes(catchHandlerNonThrowingFilledNewArray, DexFilledNewArray.class); + } + + private static Predicate<InstructionSubject> isInstruction(List<Class<?>> allowlist) { + return (ins) -> allowlist.contains(ins.asDexInstruction().getInstruction().getClass()); + } + + private static Predicate<InstructionSubject> isInstruction(Class<?> clazz) { + return isInstruction(Arrays.asList(clazz)); + } + + private static void assertArrayTypes(MethodSubject method, Class<?> allowedArrayInst) { + assertTrue(method.isPresent()); + List<Class<?>> disallowedClasses = Lists.newArrayList(DEX_ARRAY_INSTRUCTIONS); + disallowedClasses.remove(allowedArrayInst); + + assertTrue(method.streamInstructions().anyMatch(isInstruction(allowedArrayInst))); + assertTrue(method.streamInstructions().noneMatch(isInstruction(disallowedClasses))); + } + + private static void assertFilledArrayData(MethodSubject method) { + assertTrue(method.isPresent()); + List<Class<?>> disallowedClasses = Lists.newArrayList(DEX_ARRAY_INSTRUCTIONS); + disallowedClasses.remove(DexFillArrayData.class); + disallowedClasses.remove(DexNewArray.class); + + assertTrue(method.streamInstructions().noneMatch(isInstruction(disallowedClasses))); + assertTrue(method.streamInstructions().noneMatch(InstructionSubject::isArrayPut)); + long numNewArray = method.streamInstructions().filter(InstructionSubject::isNewArray).count(); + long numFillArray = + method.streamInstructions().filter(isInstruction(DexFillArrayData.class)).count(); + assertEquals(numNewArray, numFillArray); + } + + public static final class Main { + static final String assumedNonNullField = null; + static final String assumedNullField = null; + + public static void main(String[] args) { + stringArrays(); + referenceArraysNoCasts(); + referenceArraysWithSubclasses(); + interfaceArrayWithRawObject(); + phiFilledNewArray(); + intsThatUseFilledNewArray(); + twoDimensionalArrays(); + objectArraysFilledNewArrayRange(); + arraysThatUseFilledData(); + arraysThatUseNewArrayEmpty(); + reversedArray(); + arrayWithCorrectCountButIncompleteCoverage(); + arrayWithExtraInitialPuts(); + catchHandlerThrowing(); + catchHandlerNonThrowingFilledNewArray(); + catchHandlerNonThrowingFillArrayData(); + arrayIntoAnotherArray(); + assumedValues(); + } + + @NeverInline + private static void stringArrays() { + // Test exact class, no null. + String[] stringArr = {"a"}; + System.out.println(Arrays.toString(stringArr)); + } + + @NeverInline + private static void referenceArraysNoCasts() { + // Tests that no type info is needed when array type is Object[]. + Object[] objectArr = {"a", 1, null}; + System.out.println(Arrays.toString(objectArr)); + // Test that interface arrays work when we have the exact interface already. + Serializable[] interfaceArr = {getSerializable(1), null}; + System.out.println(Arrays.toString(interfaceArr)); + } + + @Keep + private static Serializable getSerializable(Integer value) { + return value; + } + + @NeverInline + private static void referenceArraysWithSubclasses() { + Serializable[] interfaceArr = {1, null, 2}; + System.out.println(Arrays.toString(interfaceArr)); + Number[] objArray = {1, null, 2}; + System.out.println(Arrays.toString(objArray)); + } + + @NeverInline + private static void interfaceArrayWithRawObject() { + // Interfaces can use filled-new-array only when we know types implement the interface. + Serializable[] arr = new Serializable[1]; + // Transformed from `I get()` to `Object get()`. + arr[0] = getObjectThatImplementsSerializable(); + System.out.println(Arrays.toString(arr)); + } + + @Keep + private static /*Object*/ Serializable getObjectThatImplementsSerializable() { + return 1; + } + + @NeverInline + private static void reversedArray() { + int[] arr = new int[5]; + arr[4] = 4; + arr[3] = 3; + arr[2] = 2; + arr[1] = 1; + arr[0] = 0; + System.out.println(Arrays.toString(arr)); + } + + @NeverInline + private static void arrayWithCorrectCountButIncompleteCoverage() { + int[] arr = new int[5]; + arr[0] = 0; + arr[0] = 1; + arr[0] = 2; + arr[0] = 3; + arr[0] = 4; + System.out.println(Arrays.toString(arr)); + } + + @NeverInline + private static void arrayWithExtraInitialPuts() { + int[] arr = new int[5]; + arr[0] = 0; + arr[0] = 1; + arr[0] = 2; + arr[0] = 3; + arr[0] = 4; + arr[1] = 1; + arr[2] = 2; + arr[3] = 3; + arr[4] = 4; + System.out.println(Arrays.toString(arr)); + } + + @NeverInline + private static void catchHandlerNonThrowingFilledNewArray() { + try { + int[] arr1 = {1, 2, 3}; + System.currentTimeMillis(); + System.out.println(Arrays.toString(arr1)); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + @NeverInline + private static void catchHandlerNonThrowingFillArrayData() { + try { + int[] arr = {1, 2, 3, 4, 5, 6}; + System.currentTimeMillis(); + System.out.println(Arrays.toString(arr)); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + @NeverInline + private static void catchHandlerThrowing() { + int[] arr1 = new int[3]; + arr1[0] = 0; + arr1[1] = 1; + // Since the array is used in only one spot, and that spot is not within the try/catch, it + // should be safe to use filled-new-array, but we don't. + try { + System.currentTimeMillis(); + arr1[2] = 2; + } catch (Throwable t) { + throw new RuntimeException(t); + } + System.out.println(Arrays.toString(arr1)); + + try { + // Test filled-new-array with a throwing instruction before the last array-put. + int[] arr2 = new int[1]; + System.currentTimeMillis(); + arr2[0] = 0; + System.out.println(Arrays.toString(arr2)); + + // Test filled-array-data with a throwing instruction before the last array-put. + short[] arr3 = new short[3]; + arr3[0] = 0; + arr3[1] = 1; + System.currentTimeMillis(); + arr3[2] = 2; + System.out.println(Arrays.toString(arr3)); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + @NeverInline + private static void arrayIntoAnotherArray() { + // Tests that our computeValues() method does not assume all array-put-object users have + // the array as the target. + int[] intArr = new int[1]; + Object[] objArr = new Object[2]; + objArr[0] = intArr; + System.out.println(Arrays.toString((int[]) objArr[0])); + } + + @NeverInline + private static void assumedValues() { + Object[] arr = {assumedNonNullField, assumedNullField}; + System.out.println(Arrays.toString(arr)); + } + + @NeverInline + private static void phiFilledNewArray() { + // The presence of ? should not affect use of filled-new-array. + Integer[] phiArray = {1, System.nanoTime() > 0 ? 2 : 3}; + System.out.println(Arrays.toString(phiArray)); + } + + @NeverInline + private static void intsThatUseFilledNewArray() { + // Up to 5 ints uses filled-new-array rather than filled-array-data. + int[] intArr = {1, 2, 3, 4, 5}; + System.out.println(Arrays.toString(intArr)); + } + + @NeverInline + private static void twoDimensionalArrays() { + Integer[][] twoDimensions = {new Integer[] {1}, null}; + System.out.println(Arrays.toString(Arrays.asList(twoDimensions).get(0))); + } + + @NeverInline + private static void objectArraysFilledNewArrayRange() { + // 6 or more elements use /range variant. + Object[] objectArr = {"a", 1, null, "d", "e", "f"}; + System.out.println(Arrays.toString(objectArr)); + Serializable[] interfaceArr = { + getSerializable(1), null, getSerializable(3), null, null, getSerializable(6) + }; + System.out.println(Arrays.toString(interfaceArr)); + } + + @NeverInline + private static void arraysThatUseFilledData() { + // For int[], <= 5 elements should use NewArrayFilledData (otherwise NewFilledArray is used). + int[] intArr = {1, 2, 3, 4, 5, 6}; + // For other primitives, > 1 element leads to fill-array-data. + System.out.println(Arrays.toString(intArr)); + boolean[] boolArr = {true, false}; + System.out.println(Arrays.toString(boolArr)); + byte[] byteArr = {1, 2}; + System.out.println(Arrays.toString(byteArr)); + char[] charArr = {'1', '2'}; + System.out.println(Arrays.toString(charArr)); + long[] longArr = {1, 2}; + System.out.println(Arrays.toString(longArr)); + float[] floatArr = {1, 2}; + System.out.println(Arrays.toString(floatArr)); + double[] doubleArr = {1, 2}; + System.out.println(Arrays.toString(doubleArr)); + } + + @NeverInline + private static void arraysThatUseNewArrayEmpty() { + // int/object of size zero should not use filled-new-array. + int[] intArr = {}; + System.out.println(Arrays.toString(intArr)); + String[] strArr = {}; + System.out.println(Arrays.toString(strArr)); + + // Other primitives with size <= 1 should not use filled-array-data. + boolean[] boolArr = {true}; + System.out.println(Arrays.toString(boolArr)); + byte[] byteArr = {1}; + System.out.println(Arrays.toString(byteArr)); + char[] charArr = {'1'}; + System.out.println(Arrays.toString(charArr)); + long[] longArr = {1}; + System.out.println(Arrays.toString(longArr)); + float[] floatArr = {1}; + System.out.println(Arrays.toString(floatArr)); + double[] doubleArr = {1}; + System.out.println(Arrays.toString(doubleArr)); + + // Array used before all members are set. + int[] readArray = new int[2]; + readArray[0] = (int) (System.currentTimeMillis() / System.nanoTime()); + readArray[1] = readArray[0] + 1; + System.out.println(Arrays.toString(readArray)); + + // Array does not have all elements set (we could make this work, but likely this is rare). + Integer[] partialArray = new Integer[2]; + partialArray[0] = 1; + System.out.println(Arrays.toString(partialArray)); + + // Non-constant array size. + int trickyZero = (int) (System.currentTimeMillis() / System.nanoTime()); + Object[] nonConstSize = new Object[trickyZero + 1]; + nonConstSize[0] = "a"; + System.out.println(Arrays.toString(nonConstSize)); + + // Non-constant index. + Object[] nonConstIndex = new Object[2]; + nonConstIndex[trickyZero] = 0; + nonConstIndex[trickyZero + 1] = 1; + System.out.println(Arrays.toString(nonConstIndex)); + + // Exceeds our (arbitrary) size limit for /range. + String[] bigArr = new String[201]; + bigArr[0] = "0"; + bigArr[1] = "1"; + bigArr[2] = "2"; + bigArr[3] = "3"; + bigArr[4] = "4"; + bigArr[5] = "5"; + bigArr[6] = "6"; + bigArr[7] = "7"; + bigArr[8] = "8"; + bigArr[9] = "9"; + bigArr[10] = "10"; + bigArr[11] = "11"; + bigArr[12] = "12"; + bigArr[13] = "13"; + bigArr[14] = "14"; + bigArr[15] = "15"; + bigArr[16] = "16"; + bigArr[17] = "17"; + bigArr[18] = "18"; + bigArr[19] = "19"; + bigArr[20] = "20"; + bigArr[21] = "21"; + bigArr[22] = "22"; + bigArr[23] = "23"; + bigArr[24] = "24"; + bigArr[25] = "25"; + bigArr[26] = "26"; + bigArr[27] = "27"; + bigArr[28] = "28"; + bigArr[29] = "29"; + bigArr[30] = "30"; + bigArr[31] = "31"; + bigArr[32] = "32"; + bigArr[33] = "33"; + bigArr[34] = "34"; + bigArr[35] = "35"; + bigArr[36] = "36"; + bigArr[37] = "37"; + bigArr[38] = "38"; + bigArr[39] = "39"; + bigArr[40] = "40"; + bigArr[41] = "41"; + bigArr[42] = "42"; + bigArr[43] = "43"; + bigArr[44] = "44"; + bigArr[45] = "45"; + bigArr[46] = "46"; + bigArr[47] = "47"; + bigArr[48] = "48"; + bigArr[49] = "49"; + bigArr[50] = "50"; + bigArr[51] = "51"; + bigArr[52] = "52"; + bigArr[53] = "53"; + bigArr[54] = "54"; + bigArr[55] = "55"; + bigArr[56] = "56"; + bigArr[57] = "57"; + bigArr[58] = "58"; + bigArr[59] = "59"; + bigArr[60] = "60"; + bigArr[61] = "61"; + bigArr[62] = "62"; + bigArr[63] = "63"; + bigArr[64] = "64"; + bigArr[65] = "65"; + bigArr[66] = "66"; + bigArr[67] = "67"; + bigArr[68] = "68"; + bigArr[69] = "69"; + bigArr[70] = "70"; + bigArr[71] = "71"; + bigArr[72] = "72"; + bigArr[73] = "73"; + bigArr[74] = "74"; + bigArr[75] = "75"; + bigArr[76] = "76"; + bigArr[77] = "77"; + bigArr[78] = "78"; + bigArr[79] = "79"; + bigArr[80] = "80"; + bigArr[81] = "81"; + bigArr[82] = "82"; + bigArr[83] = "83"; + bigArr[84] = "84"; + bigArr[85] = "85"; + bigArr[86] = "86"; + bigArr[87] = "87"; + bigArr[88] = "88"; + bigArr[89] = "89"; + bigArr[90] = "90"; + bigArr[91] = "91"; + bigArr[92] = "92"; + bigArr[93] = "93"; + bigArr[94] = "94"; + bigArr[95] = "95"; + bigArr[96] = "96"; + bigArr[97] = "97"; + bigArr[98] = "98"; + bigArr[99] = "99"; + bigArr[100] = "100"; + bigArr[101] = "101"; + bigArr[102] = "102"; + bigArr[103] = "103"; + bigArr[104] = "104"; + bigArr[105] = "105"; + bigArr[106] = "106"; + bigArr[107] = "107"; + bigArr[108] = "108"; + bigArr[109] = "109"; + bigArr[110] = "110"; + bigArr[111] = "111"; + bigArr[112] = "112"; + bigArr[113] = "113"; + bigArr[114] = "114"; + bigArr[115] = "115"; + bigArr[116] = "116"; + bigArr[117] = "117"; + bigArr[118] = "118"; + bigArr[119] = "119"; + bigArr[120] = "120"; + bigArr[121] = "121"; + bigArr[122] = "122"; + bigArr[123] = "123"; + bigArr[124] = "124"; + bigArr[125] = "125"; + bigArr[126] = "126"; + bigArr[127] = "127"; + bigArr[128] = "128"; + bigArr[129] = "129"; + bigArr[130] = "130"; + bigArr[131] = "131"; + bigArr[132] = "132"; + bigArr[133] = "133"; + bigArr[134] = "134"; + bigArr[135] = "135"; + bigArr[136] = "136"; + bigArr[137] = "137"; + bigArr[138] = "138"; + bigArr[139] = "139"; + bigArr[140] = "140"; + bigArr[141] = "141"; + bigArr[142] = "142"; + bigArr[143] = "143"; + bigArr[144] = "144"; + bigArr[145] = "145"; + bigArr[146] = "146"; + bigArr[147] = "147"; + bigArr[148] = "148"; + bigArr[149] = "149"; + bigArr[150] = "150"; + bigArr[151] = "151"; + bigArr[152] = "152"; + bigArr[153] = "153"; + bigArr[154] = "154"; + bigArr[155] = "155"; + bigArr[156] = "156"; + bigArr[157] = "157"; + bigArr[158] = "158"; + bigArr[159] = "159"; + bigArr[160] = "160"; + bigArr[161] = "161"; + bigArr[162] = "162"; + bigArr[163] = "163"; + bigArr[164] = "164"; + bigArr[165] = "165"; + bigArr[166] = "166"; + bigArr[167] = "167"; + bigArr[168] = "168"; + bigArr[169] = "169"; + bigArr[170] = "170"; + bigArr[171] = "171"; + bigArr[172] = "172"; + bigArr[173] = "173"; + bigArr[174] = "174"; + bigArr[175] = "175"; + bigArr[176] = "176"; + bigArr[177] = "177"; + bigArr[178] = "178"; + bigArr[179] = "179"; + bigArr[180] = "180"; + bigArr[181] = "181"; + bigArr[182] = "182"; + bigArr[183] = "183"; + bigArr[184] = "184"; + bigArr[185] = "185"; + bigArr[186] = "186"; + bigArr[187] = "187"; + bigArr[188] = "188"; + bigArr[189] = "189"; + bigArr[190] = "190"; + bigArr[191] = "191"; + bigArr[192] = "192"; + bigArr[193] = "193"; + bigArr[194] = "194"; + bigArr[195] = "195"; + bigArr[196] = "196"; + bigArr[197] = "197"; + bigArr[198] = "198"; + bigArr[199] = "199"; + bigArr[200] = "200"; + System.out.println(Arrays.asList(bigArr).get(200)); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideDefaultMethodTest.java new file mode 100644 index 0000000..939b14a --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideDefaultMethodTest.java
@@ -0,0 +1,98 @@ +// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking.librarymethodoverride; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.Enqueuer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class LibraryMethodOverrideDefaultMethodTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(ProgramI.class, ProgramClass.class, Main.class) + .addLibraryClasses(LibraryI.class, LibraryClass.class) + .addRunClasspathFiles(buildOnDexRuntime(parameters, LibraryI.class, LibraryClass.class)) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("ProgramI::foo"); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(ProgramI.class, ProgramClass.class, Main.class) + .addDefaultRuntimeLibrary(parameters) + .addOptionsModification( + options -> options.testing.enqueuerInspector = this::verifyLibraryOverrideInformation) + .addLibraryClasses(LibraryI.class, LibraryClass.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(Main.class) + .compile() + .addBootClasspathClasses(LibraryI.class, LibraryClass.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("ProgramI::foo"); + } + + private void verifyLibraryOverrideInformation(AppInfoWithLiveness appInfo, Enqueuer.Mode mode) { + DexItemFactory dexItemFactory = appInfo.dexItemFactory(); + DexProgramClass clazz = + appInfo + .definitionFor(dexItemFactory.createType(descriptor(ProgramI.class))) + .asProgramClass(); + DexEncodedMethod method = + clazz.lookupVirtualMethod(m -> m.getReference().name.toString().equals("foo")); + assertTrue(method.isLibraryMethodOverride().isTrue()); + } + + public interface LibraryI { + + void foo(); + } + + public static class LibraryClass { + + public static void callI(LibraryI i) { + i.foo(); + } + } + + public interface ProgramI extends LibraryI { + + @Override + default void foo() { + System.out.println("ProgramI::foo"); + } + } + + public static class ProgramClass implements ProgramI {} + + public static class Main { + + public static void main(String[] args) { + LibraryClass.callI(new ProgramClass()); + } + } +}
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1 index 023f1c9..e86ba01 100644 --- a/third_party/retrace/binary_compatibility.tar.gz.sha1 +++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@ -ec4f1b68e126b5b5e8a53078c74c79c65e190cf2 \ No newline at end of file +8eea4fa493bb7cb63d11e8d2a0ac34775726825e \ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py index 63d02c6..08f453e 100755 --- a/tools/archive_desugar_jdk_libs.py +++ b/tools/archive_desugar_jdk_libs.py
@@ -140,12 +140,31 @@ + github_account + '/' + GITHUB_REPRO, checkout_dir) git_utils.GitCheckout(desugar_jdk_libs_hash, checkout_dir) -def GetJavaEnv(): +def GetJavaEnv(androidHomeTemp): java_env = dict(os.environ, JAVA_HOME = jdk.GetJdk11Home()) java_env['PATH'] = java_env['PATH'] + os.pathsep + os.path.join(jdk.GetJdk11Home(), 'bin') java_env['GRADLE_OPTS'] = '-Xmx1g' + java_env['ANDROID_HOME'] = androidHomeTemp return java_env +def setUpFakeAndroidHome(androidHomeTemp): + # Bazel will check if 30 is present then extract android.jar from 32. + # We copy android.jar from third_party to mimic repository structure. + subpath = os.path.join(androidHomeTemp, "platforms") + cmd = ["mkdir", subpath] + subprocess.check_call(cmd) + subpath30 = os.path.join(subpath, "android-30") + cmd = ["mkdir", subpath30] + subprocess.check_call(cmd) + subpath = os.path.join(subpath, "android-32") + cmd = ["mkdir", subpath] + subprocess.check_call(cmd) + dest = os.path.join(subpath, "android.jar") + sha = os.path.join(utils.THIRD_PARTY, "android_jar", "lib-v32.tar.gz.sha1") + utils.DownloadFromGoogleCloudStorage(sha) + src = os.path.join(utils.THIRD_PARTY, "android_jar", "lib-v32", "android.jar") + cmd = ["cp", src, dest] + subprocess.check_call(cmd) def BuildDesugaredLibrary(checkout_dir, variant, version = None): if not variant in MAVEN_RELEASE_TARGET_MAP: @@ -153,19 +172,22 @@ if variant != 'jdk8' and variant != 'jdk11_legacy' and version is None: raise Exception('Variant ' + variant + ' require version for undesugaring') with utils.ChangedWorkingDirectory(checkout_dir): - bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel') - cmd = [ + with utils.TempDir() as androidHomeTemp: + setUpFakeAndroidHome(androidHomeTemp) + javaEnv = GetJavaEnv(androidHomeTemp) + bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel') + cmd = [ bazel, '--bazelrc=/dev/null', 'build', '--spawn_strategy=local', '--verbose_failures', MAVEN_RELEASE_TARGET_MAP[variant]] - utils.PrintCmd(cmd) - subprocess.check_call(cmd, env=GetJavaEnv()) - cmd = [bazel, 'shutdown'] - utils.PrintCmd(cmd) - subprocess.check_call(cmd, env=GetJavaEnv()) + utils.PrintCmd(cmd) + subprocess.check_call(cmd, env=javaEnv) + cmd = [bazel, 'shutdown'] + utils.PrintCmd(cmd) + subprocess.check_call(cmd, env=javaEnv) # Locate the library jar and the maven zip with the jar from the # bazel build.
diff --git a/tools/dex2oat.py b/tools/dex2oat.py index 04422cf..10c4732 100755 --- a/tools/dex2oat.py +++ b/tools/dex2oat.py
@@ -12,16 +12,21 @@ LINUX_DIR = os.path.join(utils.TOOLS_DIR, 'linux') +LATEST = '12.0.0' + VERSIONS = [ - 'default', - '9.0.0', - '8.1.0', - '7.0.0', + '12.0.0', + # TODO(b/258170524): Fix the broken dex2oat versions. + # 'default', + # '9.0.0', + # '8.1.0', + # '7.0.0', '6.0.1', - '5.1.1', + # '5.1.1', ] DIRS = { + '12.0.0': 'host/art-12.0.0-beta4', 'default': 'art', '9.0.0': 'art-9.0.0', '8.1.0': 'art-8.1.0', @@ -31,6 +36,7 @@ } PRODUCTS = { + '12.0.0': 'redfin', 'default': 'angler', '9.0.0': 'marlin', '8.1.0': 'marlin', @@ -40,6 +46,7 @@ } ARCHS = { + '12.0.0': 'x86_64', 'default': 'arm64', '9.0.0': 'arm64', '8.1.0': 'arm64', @@ -58,12 +65,16 @@ 'all', ] +BOOT_IMAGE = { + '12.0.0': 'apex/art_boot_images/javalib/boot.art' +} + def ParseOptions(): parser = optparse.OptionParser() parser.add_option('--version', - help='Version of dex2oat. (defaults to latest, eg, tools/linux/art).', + help='Version of dex2oat. (defaults to latest: ' + LATEST + ').', choices=VERSIONS, - default='default') + default=LATEST) parser.add_option('--all', help='Run dex2oat on all possible versions', default=False, @@ -96,7 +107,9 @@ print("") return 0 -def run(dexfile, oatfile=None, version='default', verbose=[]): +def run(dexfile, oatfile=None, version=None, verbose=[]): + if not version: + version = LATEST # dex2oat accepts non-existent dex files, check here instead if not os.path.exists(dexfile): raise Exception('DEX file not found: "{}"'.format(dexfile)) @@ -117,9 +130,12 @@ ] for flag in verbose: cmd += ['--runtime-arg', '-verbose:' + flag] + if version in BOOT_IMAGE: + cmd += ['--boot-image=' + BOOT_IMAGE[version]] env = {"LD_LIBRARY_PATH": os.path.join(base, 'lib')} utils.PrintCmd(cmd) - subprocess.check_call(cmd, env = env) + with utils.ChangedWorkingDirectory(base): + subprocess.check_call(cmd, env = env) if __name__ == '__main__': sys.exit(Main())
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions index 5e38ebd..611c06e 100644 --- a/tools/linux/README.art-versions +++ b/tools/linux/README.art-versions
@@ -115,6 +115,10 @@ --art-dir host/art-12.0.0-beta4 \ --android-product redfin +The dex2oat tool expects to find core-oj.dex in out/host/linux-x86 so copy it. + +(cd tools/linux/host; cp -r apex out/host/linux-x86/apex) + (cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-12.0.0-beta4) art-10.0.0 (Android Q)
diff --git a/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1 b/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1 index b95d909..9f80774 100644 --- a/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1 +++ b/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
@@ -1 +1 @@ -9b6d0b061669e0fb1b09ba92c3650d5c7bdc9e85 \ No newline at end of file +df7267e9eff9cc1812b5a7b4111e7175d5e186e9 \ No newline at end of file