Merge commit '4667b25930ebdbe237fc7e161a54f686f1b37815' into dev-release
diff --git a/build.gradle b/build.gradle
index 85e3fc6..ea3f498 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,16 +7,15 @@
 import desugaredlibrary.CustomConversionAsmRewriterTask
 import net.ltgt.gradle.errorprone.CheckSeverity
 import org.gradle.internal.os.OperatingSystem
-import tasks.DownloadDependency
 import smali.SmaliTask
+import tasks.DownloadDependency
 import tasks.GetJarsFromConfiguration
-import utils.Utils
 
 buildscript {
     repositories {
+        google()
         mavenCentral()
         gradlePluginPortal()
-        jcenter()
     }
 }
 
@@ -32,8 +31,8 @@
     asmVersion = '9.5'  // When updating update tools/asmifier.py, build.src and Toolhelper as well.
     javassistVersion = '3.29.2-GA'
     espressoVersion = '3.0.0'
-    fastutilVersion = '7.2.0'
-    guavaVersion = '30.1.1-jre'
+    fastutilVersion = '7.2.1'
+    guavaVersion = '31.1-jre'
     joptSimpleVersion = '4.6'
     gsonVersion = '2.7'
     junitVersion = '4.13-beta-2'
@@ -42,7 +41,7 @@
     // all kotlin compilations are done in tests.
     kotlinVersion = '1.8.0'
     kotlinExtMetadataJVMVersion = '0.6.0'
-    smaliVersion = '2.2b4'
+    smaliVersion = '3.0.3'
     errorproneVersion = '2.18.0'
     testngVersion = '6.10'
 }
@@ -260,7 +259,7 @@
     testCompile "com.google.guava:guava:$guavaVersion"
     testCompile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
-    testCompile group: 'org.smali', name: 'smali', version: smaliVersion
+    testCompile group: 'com.android.tools.smali', name: 'smali', version: smaliVersion
     testCompile files('third_party/jasmin/jasmin-2.4.jar')
     testCompile files('third_party/jdwp-tests/apache-harmony-jdwp-tests-host.jar')
     testCompile files('third_party/ddmlib/ddmlib.jar')
@@ -1140,7 +1139,7 @@
             "Main",
             ["src/main/keep.txt", generateR8LibKeepRules.outputs.files[0]],
             r8NoManifestWithRelocatedDeps,
-            r8LibPath,
+            r8LibPath
     ).dependsOn(generateR8LibKeepRules)
     inputs.files r8NoManifestWithRelocatedDeps.outputs.files
     outputs.file r8LibPath
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index bfe46c2..85815bd 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -5,16 +5,19 @@
 apply plugin: 'idea'
 
 repositories {
+    google()
     mavenCentral()
 }
 
 ext {
+    guavaVersion = '31.1-jre'
     asmVersion = '9.5'
+    smaliVersion = '3.0.3'
 }
 
 dependencies {
-    implementation group: 'com.google.guava', name: 'guava', version: '30.1.1-jre'
-    implementation group: 'org.smali', name: 'smali', version: '2.2b4'
+    implementation group: 'com.google.guava', name: 'guava', version: guavaVersion
+    implementation group: 'com.android.tools.smali', name: 'smali', version: smaliVersion
     implementation group: 'org.ow2.asm', name: 'asm', version: asmVersion
     implementation group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
     implementation group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
diff --git a/buildSrc/src/main/java/smali/SmaliTask.java b/buildSrc/src/main/java/smali/SmaliTask.java
index dd4e10c..d35102e 100644
--- a/buildSrc/src/main/java/smali/SmaliTask.java
+++ b/buildSrc/src/main/java/smali/SmaliTask.java
@@ -5,6 +5,8 @@
 
 import static java.util.stream.Collectors.toList;
 
+import com.android.tools.smali.smali.Smali;
+import com.android.tools.smali.smali.SmaliOptions;
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -20,8 +22,6 @@
 import org.gradle.workers.WorkAction;
 import org.gradle.workers.WorkParameters;
 import org.gradle.workers.WorkerExecutor;
-import org.jf.smali.Smali;
-import org.jf.smali.SmaliOptions;
 
 public class SmaliTask extends DefaultTask {
 
diff --git a/commonBuildSrc/settings.gradle.kts b/commonBuildSrc/settings.gradle.kts
index b7d4523..75c6675 100644
--- a/commonBuildSrc/settings.gradle.kts
+++ b/commonBuildSrc/settings.gradle.kts
@@ -10,6 +10,7 @@
 
 dependencyResolutionManagement {
     repositories {
+        google()
         mavenCentral()
         gradlePluginPortal()
     }
diff --git a/commonBuildSrc/src/main/kotlin/Dependencies.kt b/commonBuildSrc/src/main/kotlin/Dependencies.kt
index c10a576..66e88d1 100644
--- a/commonBuildSrc/src/main/kotlin/Dependencies.kt
+++ b/commonBuildSrc/src/main/kotlin/Dependencies.kt
@@ -35,15 +35,15 @@
 }
 
 object Versions {
-  const val asmVersion = "9.4"
+  const val asmVersion = "9.5"
   const val fastUtilVersion = "7.2.0"
   const val gsonVersion = "2.7"
-  const val guavaVersion = "30.1.1-jre"
+  const val guavaVersion = "31.1-jre"
   const val joptSimpleVersion = "4.6"
   const val junitVersion = "4.13-beta-2"
   const val kotlinVersion = "1.8.0"
   const val kotlinMetadataVersion = "0.6.0"
-  const val smaliVersion = "2.2b4"
+  const val smaliVersion = "3.0.3"
   const val errorproneVersion = "2.18.0"
 }
 
@@ -60,6 +60,6 @@
     "org.jetbrains.kotlinx:kotlinx-metadata-jvm:${Versions.kotlinMetadataVersion}" }
   val kotlinStdLib by lazy { "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlinVersion}" }
   val kotlinReflect by lazy { "org.jetbrains.kotlin:kotlin-reflect:${Versions.kotlinVersion}" }
-  val smali by lazy { "org.smali:smali:${Versions.smaliVersion}" }
+  val smali by lazy { "com.android.tools.smali:smali:${Versions.smaliVersion}" }
   val errorprone by lazy { "com.google.errorprone:error_prone_core:${Versions.errorproneVersion}" }
 }
diff --git a/compatibility-faq.md b/compatibility-faq.md
index f0d9c05..cb33077 100644
--- a/compatibility-faq.md
+++ b/compatibility-faq.md
@@ -175,6 +175,34 @@
 See also https://github.com/square/retrofit/issues/3005 ("Insufficient keep
 rules for R8 in full mode").
 
+### Return type must be parameterized
+
+Consider a service as the following:
+```
+interface Api {
+
+    @GET("<uri>")
+    fun getData(): Observable<Data>
+
+}
+```
+
+Retrofit instantiate a return type by inspecting the generic signature, here
+`Observable` from `io.reactivex.rxjava3.core`. Those classes are not guarded by
+keep rules prior to https://github.com/square/retrofit/pull/3886 so one has to
+manually add keep rules to prevent R8 in full mode stripping the generic
+signature. The proposed rule in https://github.com/square/retrofit/pull/3886 is:
+```
+-if interface * { @retrofit2.http.* public *** *(...); }
+-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
+```
+After https://github.com/square/retrofit/pull/3886 is merged, the above rule
+is automatically included in your build. You can add the rule to your build
+until then.
+
+Note, the `Data` class probably also needs to be kept if used since this is
+also constructed reflectively.
+
 ### Kotlin suspend functions and generic signatures
 
 For Kotlin suspend functions the generic signature is reflectively read.
diff --git a/src/main/dontwarn.txt b/src/main/dontwarn.txt
index e217663..6706080 100644
--- a/src/main/dontwarn.txt
+++ b/src/main/dontwarn.txt
@@ -1,3 +1,4 @@
 # TODO(b/176783536): Avoid need to use -dontwarn.
 -dontwarn com.google.errorprone.annotations.**
 -dontwarn com.google.j2objc.annotations.*
+-dontwarn javax.annotation.CheckForNull
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 03ae2fd..48f5c36 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -280,8 +280,8 @@
   }
 
   /**
-   * This method must match the lookup in
-   * {@link com.android.tools.r8.JdkClassFileProvider#fromJdkHome}.
+   * This method must match the lookup in {@link
+   * com.android.tools.r8.JdkClassFileProvider#fromJdkHome}.
    */
   private static boolean isJdkHome(Path home) {
     Path jrtFsJar = home.resolve("lib").resolve("jrt-fs.jar");
@@ -295,10 +295,7 @@
     }
     // JRE has rt.jar in lib/rt.jar.
     rtJar = home.resolve("lib").resolve("rt.jar");
-    if (Files.exists(rtJar)) {
-      return true;
-    }
-    return false;
+    return Files.exists(rtJar);
   }
 
   static void addLibraryArgument(BaseCommand.Builder builder, Origin origin, String arg) {
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
new file mode 100644
index 0000000..2efea81
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SelfRetraceTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * The GlobalSyntheticsGenerator, a tool for generating a dex file for all possible global
+ * synthetics.
+ */
+public class GlobalSyntheticsGenerator {
+
+  /**
+   * Main API entry for the global synthetics generator.
+   *
+   * @param command GlobalSyntheticsGenerator command.
+   */
+  public static void run(GlobalSyntheticsGeneratorCommand command)
+      throws CompilationFailedException {
+    runForTesting(command.getInputApp(), command.getInternalOptions());
+  }
+
+  /**
+   * Main API entry for the global synthetics generator.
+   *
+   * @param command GlobalSyntheticsGenerator command.
+   * @param executor executor service from which to get threads for multi-threaded processing.
+   */
+  public static void run(GlobalSyntheticsGeneratorCommand command, ExecutorService executor)
+      throws CompilationFailedException {
+    run(command.getInputApp(), command.getInternalOptions(), executor);
+  }
+
+  static void runForTesting(AndroidApp app, InternalOptions options)
+      throws CompilationFailedException {
+    ExecutorService executorService = ThreadUtils.getExecutorService(options);
+    run(app, options, executorService);
+  }
+
+  private static void run(AndroidApp app, InternalOptions options, ExecutorService executorService)
+      throws CompilationFailedException {
+    try {
+      ExceptionUtils.withD8CompilationHandler(
+          options.reporter,
+          () -> {
+            throw new RuntimeException("Implement GlobalSyntheticsGenerator");
+          });
+    } finally {
+      executorService.shutdown();
+    }
+  }
+
+  private static void run(String[] args) throws CompilationFailedException {
+    GlobalSyntheticsGeneratorCommand command =
+        GlobalSyntheticsGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
+    if (command.isPrintHelp()) {
+      SelfRetraceTest.test();
+      System.out.println(GlobalSyntheticsGeneratorCommandParser.getUsageMessage());
+      return;
+    }
+    if (command.isPrintVersion()) {
+      System.out.println("GlobalSynthetics " + Version.getVersionString());
+      return;
+    }
+    run(command);
+  }
+
+  /**
+   * Command-line entry to GlobalSynthetics.
+   *
+   * <p>See {@link GlobalSyntheticsGeneratorCommandParser#getUsageMessage()} or run {@code
+   * globalsynthetics --help} for usage information.
+   */
+  public static void main(String[] args) {
+    if (args.length == 0) {
+      throw new RuntimeException(
+          StringUtils.joinLines(
+              "Invalid invocation.", GlobalSyntheticsGeneratorCommandParser.getUsageMessage()));
+    }
+    ExceptionUtils.withMainProgramHandler(() -> run(args));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
new file mode 100644
index 0000000..2187b67
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
@@ -0,0 +1,260 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.util.Collection;
+
+/**
+ * Immutable command structure for an invocation of the {@link GlobalSyntheticsGenerator} compiler.
+ */
+public final class GlobalSyntheticsGeneratorCommand extends BaseCommand {
+
+  private final ProgramConsumer programConsumer;
+  private final StringConsumer classNameConsumer;
+  private final Reporter reporter;
+  private final int minApiLevel;
+
+  private final DexItemFactory factory = new DexItemFactory();
+
+  private GlobalSyntheticsGeneratorCommand(
+      AndroidApp androidApp,
+      ProgramConsumer programConsumer,
+      StringConsumer ClassNameConsumer,
+      Reporter reporter,
+      int minApiLevel) {
+    super(androidApp);
+    this.programConsumer = programConsumer;
+    this.classNameConsumer = ClassNameConsumer;
+    this.minApiLevel = minApiLevel;
+    this.reporter = reporter;
+  }
+
+  private GlobalSyntheticsGeneratorCommand(boolean printHelp, boolean printVersion) {
+    super(printHelp, printVersion);
+    this.programConsumer = null;
+    this.classNameConsumer = null;
+    this.minApiLevel = AndroidApiLevel.B.getLevel();
+
+    reporter = new Reporter();
+  }
+
+  /**
+   * Parse the GlobalSyntheticsGenerator command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @return GlobalSyntheticsGenerator command builder with state according to parsed command line.
+   */
+  public static Builder parse(String[] args, Origin origin) {
+    return GlobalSyntheticsGeneratorCommandParser.parse(args, origin);
+  }
+
+  /**
+   * Parse the GlobalSyntheticsGenerator command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @param handler Custom defined diagnostics handler.
+   * @return GlobalSyntheticsGenerator command builder with state according to parsed command line.
+   */
+  public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+    return GlobalSyntheticsGeneratorCommandParser.parse(args, origin, handler);
+  }
+
+  protected static class DefaultR8DiagnosticsHandler implements DiagnosticsHandler {
+
+    @Override
+    public void error(Diagnostic error) {
+      if (error instanceof DexFileOverflowDiagnostic) {
+        DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
+        DiagnosticsHandler.super.error(
+            new StringDiagnostic(
+                overflowDiagnostic.getDiagnosticMessage()
+                    + ". Library too large. GlobalSyntheticsGenerator can only produce a single"
+                    + " .dex file"));
+        return;
+      }
+      DiagnosticsHandler.super.error(error);
+    }
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
+    return new Builder(diagnosticsHandler);
+  }
+
+  @Override
+  InternalOptions getInternalOptions() {
+    InternalOptions internal = new InternalOptions(factory, reporter);
+    assert !internal.debug;
+    assert !internal.minimalMainDex;
+    internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
+    assert !internal.intermediate;
+    assert internal.retainCompileTimeAnnotations;
+    internal.programConsumer = programConsumer;
+
+    // Assert and fixup defaults.
+    assert !internal.isShrinking();
+    assert !internal.isMinifying();
+    assert !internal.passthroughDexCode;
+
+    return internal;
+  }
+
+  /**
+   * Builder for constructing a GlobalSyntheticsGeneratorCommand.
+   *
+   * <p>A builder is obtained by calling {@link GlobalSyntheticsGeneratorCommand#builder}.
+   */
+  public static class Builder
+      extends BaseCommand.Builder<GlobalSyntheticsGeneratorCommand, Builder> {
+
+    private ProgramConsumer programConsumer = null;
+    private StringConsumer globalSyntheticClassesListConsumer = null;
+    private Reporter reporter;
+    private int minApiLevel = AndroidApiLevel.B.getLevel();
+
+    private Builder() {
+      this(new DefaultR8DiagnosticsHandler());
+    }
+
+    private Builder(DiagnosticsHandler diagnosticsHandler) {
+      this.reporter = new Reporter(diagnosticsHandler);
+    }
+
+    @Override
+    Builder self() {
+      return this;
+    }
+
+    public Builder setReporter(Reporter reporter) {
+      this.reporter = reporter;
+      return self();
+    }
+
+    public Builder setMinApiLevel(int minApiLevel) {
+      this.minApiLevel = minApiLevel;
+      return self();
+    }
+
+    @Override
+    void validate() {
+      if (isPrintHelp() || isPrintVersion()) {
+        return;
+      }
+      if (!(programConsumer instanceof DexIndexedConsumer)) {
+        reporter.error("G8 does not support compiling to dex per class or class files");
+      }
+    }
+
+    @Override
+    public GlobalSyntheticsGeneratorCommand makeCommand() {
+      if (isPrintHelp() || isPrintVersion()) {
+        return new GlobalSyntheticsGeneratorCommand(isPrintHelp(), isPrintVersion());
+      }
+      validate();
+      return new GlobalSyntheticsGeneratorCommand(
+          getAppBuilder().build(),
+          programConsumer,
+          globalSyntheticClassesListConsumer,
+          reporter,
+          minApiLevel);
+    }
+
+    public Builder setGlobalSyntheticClassesListOutput(Path path) {
+      return setGlobalSyntheticClassesListConsumer(new StringConsumer.FileConsumer(path));
+    }
+
+    public Builder setGlobalSyntheticClassesListConsumer(
+        StringConsumer globalSyntheticClassesListOutput) {
+      this.globalSyntheticClassesListConsumer = globalSyntheticClassesListOutput;
+      return self();
+    }
+
+    public Builder setProgramConsumerOutput(Path path) {
+      return setProgramConsumer(
+          FileUtils.isArchive(path)
+              ? new DexIndexedConsumer.ArchiveConsumer(path, false)
+              : new DexIndexedConsumer.DirectoryConsumer(path, false));
+    }
+
+    public Builder setProgramConsumer(ProgramConsumer programConsumer) {
+      this.programConsumer = programConsumer;
+      return self();
+    }
+
+    @Override
+    public Builder addProgramFiles(Collection<Path> files) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addClasspathFiles(Path... files) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addClasspathFiles(Collection<Path> files) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addClassProgramData(byte[] data, Origin origin) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    Builder addDexProgramData(byte[] data, Origin origin) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addMainDexListFiles(Path... files) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addMainDexListFiles(Collection<Path> files) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addMainDexClasses(String... classes) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+
+    @Override
+    public Builder addMainDexClasses(Collection<String> classes) {
+      throw new Unreachable("Should not be used for global synthetics generation");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
new file mode 100644
index 0000000..36e5bbd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static com.android.tools.r8.BaseCompilerCommandParser.parsePositiveIntArgument;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+
+public class GlobalSyntheticsGeneratorCommandParser {
+
+  private static final String LOWER_CASE_NAME = "globalsynthetics";
+  private static final String MIN_API_FLAG = "--min-api";
+
+  private static final String USAGE_MESSAGE =
+      StringUtils.lines("Usage: " + LOWER_CASE_NAME + " [options] " + "where options are:");
+
+  public static List<ParseFlagInfo> getFlags() {
+    return ImmutableList.<ParseFlagInfo>builder()
+        .add(ParseFlagInfoImpl.getMinApi())
+        .add(ParseFlagInfoImpl.getLib())
+        .add(ParseFlagInfoImpl.flag1("--output", "<dex-file>", "Output result in <dex-file>."))
+        .add(
+            ParseFlagInfoImpl.flag1(
+                "--classes-list-output", "<file>", "Output list of generated classes in <file>"))
+        .add(ParseFlagInfoImpl.getVersion(LOWER_CASE_NAME))
+        .add(ParseFlagInfoImpl.getHelp())
+        .build();
+  }
+
+  static String getUsageMessage() {
+    StringBuilder builder = new StringBuilder();
+    StringUtils.appendLines(builder, USAGE_MESSAGE);
+    new ParseFlagPrinter().addFlags(getFlags()).appendLinesToBuilder(builder);
+    return builder.toString();
+  }
+
+  private static final Set<String> OPTIONS_WITH_ONE_PARAMETER =
+      ImmutableSet.of("--output", "--lib", MIN_API_FLAG, "---classes-list-output");
+
+  public static GlobalSyntheticsGeneratorCommand.Builder parse(String[] args, Origin origin) {
+    return new GlobalSyntheticsGeneratorCommandParser()
+        .parse(args, origin, GlobalSyntheticsGeneratorCommand.builder());
+  }
+
+  public static GlobalSyntheticsGeneratorCommand.Builder parse(
+      String[] args, Origin origin, DiagnosticsHandler handler) {
+    return new GlobalSyntheticsGeneratorCommandParser()
+        .parse(args, origin, GlobalSyntheticsGeneratorCommand.builder(handler));
+  }
+
+  private GlobalSyntheticsGeneratorCommand.Builder parse(
+      String[] args, Origin origin, GlobalSyntheticsGeneratorCommand.Builder builder) {
+    Path outputPath = null;
+    boolean hasDefinedApiLevel = false;
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error);
+    for (int i = 0; i < expandedArgs.length; i++) {
+      String arg = expandedArgs[i].trim();
+      String nextArg = null;
+      if (OPTIONS_WITH_ONE_PARAMETER.contains(arg)) {
+        if (++i < expandedArgs.length) {
+          nextArg = expandedArgs[i];
+        } else {
+          builder.error(
+              new StringDiagnostic("Missing parameter for " + expandedArgs[i - 1] + ".", origin));
+          break;
+        }
+      }
+      if (arg.length() == 0) {
+        continue;
+      } else if (arg.equals("--help")) {
+        builder.setPrintHelp(true);
+      } else if (arg.equals("--version")) {
+        builder.setPrintVersion(true);
+      } else if (arg.equals("--output")) {
+        if (outputPath != null) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot output both to '" + outputPath + "' and '" + nextArg + "'", origin));
+          continue;
+        }
+        outputPath = Paths.get(nextArg);
+      } else if (arg.equals(MIN_API_FLAG)) {
+        if (hasDefinedApiLevel) {
+          builder.error(
+              new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
+        } else {
+          parsePositiveIntArgument(
+              builder::error, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
+          hasDefinedApiLevel = true;
+        }
+      } else if (arg.equals("--lib")) {
+        builder.addLibraryFiles(Paths.get(nextArg));
+      } else if (arg.equals("--classes-list-output")) {
+        builder.setGlobalSyntheticClassesListOutput(Paths.get(nextArg));
+      } else if (arg.startsWith("--")) {
+        builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+      }
+    }
+    if (outputPath == null) {
+      outputPath = Paths.get(".");
+    }
+    return builder.setProgramConsumerOutput(outputPath);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/JarSizeCompare.java b/src/main/java/com/android/tools/r8/JarSizeCompare.java
index 6f80def..0be3461 100644
--- a/src/main/java/com/android/tools/r8/JarSizeCompare.java
+++ b/src/main/java/com/android/tools/r8/JarSizeCompare.java
@@ -21,7 +21,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -119,8 +118,8 @@
       }
     }
     for (Entry<String, Map<String, InputClass[]>> library : byLibrary(inputClasses)) {
-      System.out.println("");
-      System.out.println(Strings.repeat("=", 100));
+      System.out.println();
+      System.out.println("=".repeat(100));
       String commonPrefix = getCommonPrefix(library.getValue().keySet());
       if (library.getKey().isEmpty()) {
         System.out.println("PROGRAM (" + commonPrefix + ")");
diff --git a/src/main/java/com/android/tools/r8/ParseFlagPrinter.java b/src/main/java/com/android/tools/r8/ParseFlagPrinter.java
index a14556e..3f30806 100644
--- a/src/main/java/com/android/tools/r8/ParseFlagPrinter.java
+++ b/src/main/java/com/android/tools/r8/ParseFlagPrinter.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.utils.StringUtils;
-import com.google.common.base.Strings;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -101,7 +100,7 @@
 
   /** Convenience method to set the prefix to be 'indent' number of spaces. */
   public ParseFlagPrinter setIndent(int indent) {
-    return setPrefix(Strings.repeat(" ", indent));
+    return setPrefix(" ".repeat(indent));
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index 9779fcf..ae0ecd8 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -203,6 +203,9 @@
       case BACKEND_KEY:
         builder.setBackend(Backend.valueOf(value));
         return;
+      case ENABLE_MISSING_LIBRARY_API_MODELING:
+        builder.setEnableMissingLibraryApiModeling(Boolean.parseBoolean(value));
+        return;
       case TOOL_KEY:
         builder.setTool(Tool.valueOf(value));
         return;
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 53ebcee..90faadc 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -7,6 +7,7 @@
 
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.contexts.CompilationContext;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
@@ -78,6 +79,10 @@
     ps.println("# Bytecode for");
     ps.println("# Class: '" + clazzName + "'");
     if (writeAllClassInfo) {
+      ClassSignature signature = clazz.getClassSignature();
+      if (signature != null && signature.hasSignature()) {
+        ps.println("# Signature: " + signature);
+      }
       writeAnnotations(clazz, clazz.annotations(), ps);
       ps.println("# Flags: '" + clazz.accessFlags + "'");
       if (clazz.superType != application.dexItemFactory.objectType) {
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 4df769b..165878d 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -49,7 +49,6 @@
 import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
-import com.google.common.base.Strings;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
@@ -880,7 +879,7 @@
     if (existingThisIndex < 0) {
       return;
     }
-    String fakeThisName = Strings.repeat(FAKE_THIS_PREFIX, largestPrefix + 1) + FAKE_THIS_SUFFIX;
+    String fakeThisName = FAKE_THIS_PREFIX.repeat(largestPrefix + 1) + FAKE_THIS_SUFFIX;
     DebugLocalInfo debugLocalInfo =
         new DebugLocalInfo(factory.createString(fakeThisName), this.originalHolder, null);
     LocalVariableInfo thisLocalInfo = localVariables.get(existingThisIndex);
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 4cb2a24..ae96c5e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -48,7 +48,6 @@
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
-import com.google.common.base.Strings;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import java.nio.ShortBuffer;
 import java.util.ArrayList;
@@ -304,7 +303,7 @@
       }
     }
 
-    String fakeThisName = Strings.repeat(FAKE_THIS_PREFIX, largestPrefix + 1) + FAKE_THIS_SUFFIX;
+    String fakeThisName = FAKE_THIS_PREFIX.repeat(largestPrefix + 1) + FAKE_THIS_SUFFIX;
     DexString[] parameters = eventBasedInfo.parameters;
     DexString[] newParameters = new DexString[parameters.length + 1];
     newParameters[0] = factory.createString(fakeThisName);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ae3f0ac..164797a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -41,7 +41,6 @@
 import com.android.tools.r8.utils.LRUCacheTable;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.SetUtils;
-import com.google.common.base.Strings;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableList;
@@ -2932,7 +2931,7 @@
 
   public DexType createArrayType(int nesting, DexType baseType) {
     assert nesting > 0;
-    return createType(Strings.repeat("[", nesting) + baseType.toDescriptorString());
+    return createType("[".repeat(nesting) + baseType.toDescriptorString());
   }
 
   public DexField createField(DexType clazz, DexType type, DexString name) {
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 920fbc1..f1122b8 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -259,6 +259,9 @@
     }
 
     public ClassSignature visit(GenericSignatureVisitor visitor) {
+      if (hasNoSignature()) {
+        return this;
+      }
       List<FormalTypeParameter> rewrittenParameters =
           visitor.visitFormalTypeParameters(formalTypeParameters);
       ClassTypeSignature rewrittenSuperClass = visitor.visitSuperClass(superClassSignature);
@@ -291,10 +294,6 @@
       return NO_CLASS_SIGNATURE;
     }
 
-    public ClassSignature toObjectBoundWithSameFormals(ClassTypeSignature objectBound) {
-      return new ClassSignature(formalTypeParameters, objectBound, getEmptySuperInterfaces());
-    }
-
     public List<FieldTypeSignature> getGenericArgumentsToSuperType(DexType type) {
       assert hasSignature();
       if (superClassSignature.type == type) {
@@ -314,9 +313,9 @@
 
     public static class ClassSignatureBuilder {
 
-      private List<FormalTypeParameter> formalTypeParameters = new ArrayList<>();
+      private final List<FormalTypeParameter> formalTypeParameters = new ArrayList<>();
       private ClassTypeSignature superClassSignature = null;
-      private List<ClassTypeSignature> superInterfaceSignatures = new ArrayList<>();
+      private final List<ClassTypeSignature> superInterfaceSignatures = new ArrayList<>();
 
       private ClassSignatureBuilder() {}
 
@@ -336,9 +335,8 @@
       }
 
       public ClassSignature build() {
-        ClassSignature classSignature =
-            new ClassSignature(formalTypeParameters, superClassSignature, superInterfaceSignatures);
-        return classSignature;
+        return new ClassSignature(
+            formalTypeParameters, superClassSignature, superInterfaceSignatures);
       }
     }
   }
@@ -628,6 +626,9 @@
     }
 
     public ClassTypeSignature visit(GenericSignatureVisitor visitor) {
+      if (hasNoSignature()) {
+        return this;
+      }
       DexType visitedType = visitor.visitType(type);
       if (visitedType == null) {
         return null;
@@ -855,6 +856,9 @@
     }
 
     public MethodTypeSignature visit(GenericSignatureVisitor visitor) {
+      if (hasNoSignature()) {
+        return this;
+      }
       List<FormalTypeParameter> rewrittenParameters =
           visitor.visitFormalTypeParameters(formalTypeParameters);
       List<TypeSignature> rewrittenSignatures = visitor.visitMethodTypeSignatures(typeSignatures);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 31037b9..422dc72 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
 import com.android.tools.r8.graph.InvalidCode;
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -139,14 +138,7 @@
             kinds -> kinds.COMPANION_CLASS,
             iface,
             appView,
-            builder ->
-                builder
-                    .setSourceFile(iface.sourceFile)
-                    .setGenericSignature(
-                        iface
-                            .getClassSignature()
-                            .toObjectBoundWithSameFormals(
-                                new ClassTypeSignature(appView.dexItemFactory().objectType))),
+            builder -> builder.setSourceFile(iface.sourceFile),
             methodBuilderCallback,
             newMethodCallback);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 03245b3..05c83da 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -47,6 +46,7 @@
 import com.android.tools.r8.shaking.AssumeInfoCollection;
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -57,6 +57,7 @@
 public final class DefaultInliningOracle implements InliningOracle, InliningStrategy {
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final InternalOptions options;
   private final InlinerOptions inlinerOptions;
   private final MainDexInfo mainDexInfo;
   private final ProgramMethod method;
@@ -71,7 +72,8 @@
       MethodProcessor methodProcessor,
       int inliningInstructionAllowance) {
     this.appView = appView;
-    this.inlinerOptions = appView.options().inlinerOptions();
+    this.options = appView.options();
+    this.inlinerOptions = options.inlinerOptions();
     this.reasonStrategy = inliningReasonStrategy;
     this.mainDexInfo = appView.appInfo().getMainDexInfo();
     this.method = method;
@@ -132,15 +134,14 @@
     // Do not inline if the inlinee is greater than the api caller level.
     // TODO(b/188498051): We should not force inline lower api method calls.
     if (reason != Reason.FORCE
-        && !isApiSafeForInlining(
-            method, singleTarget, appView.options(), whyAreYouNotInliningReporter)) {
+        && !isApiSafeForInlining(method, singleTarget, options, whyAreYouNotInliningReporter)) {
       return false;
     }
 
     // We don't inline into constructors when producing class files since this can mess up
     // the stackmap, see b/136250031
     if (method.getDefinition().isInstanceInitializer()
-        && appView.options().isGeneratingClassFiles()
+        && options.isGeneratingClassFiles()
         && reason != Reason.FORCE) {
       whyAreYouNotInliningReporter.reportNoInliningIntoConstructorsWhenGeneratingClassFiles();
       return false;
@@ -205,19 +206,15 @@
   }
 
   private boolean canHaveIssuesWithMonitors(ProgramMethod singleTarget, ProgramMethod context) {
-    if (appView.options().canHaveIssueWithInlinedMonitors()) {
-      if (hasMonitorsOrIsSynchronized(singleTarget.getDefinition())) {
-        if (context.getOptimizationInfo().forceInline()
-            || hasMonitorsOrIsSynchronized(context.getDefinition())) {
-          return true;
-        }
-      }
+    if (options.canHaveIssueWithInlinedMonitors() && hasMonitorsOrIsSynchronized(singleTarget)) {
+      return context.getOptimizationInfo().forceInline() || hasMonitorsOrIsSynchronized(context);
     }
     return false;
   }
 
-  public static boolean hasMonitorsOrIsSynchronized(DexEncodedMethod definition) {
-    return definition.isSynchronized() || definition.getCode().hasMonitorInstructions();
+  public static boolean hasMonitorsOrIsSynchronized(ProgramMethod method) {
+    return method.getAccessFlags().isSynchronized()
+        || method.getDefinition().getCode().hasMonitorInstructions();
   }
 
   public boolean satisfiesRequirementsForSimpleInlining(InvokeMethod invoke, ProgramMethod target) {
@@ -257,9 +254,7 @@
         }
       }
     }
-    if (appView.options().isGeneratingDex()
-        && invoke.hasOutValue()
-        && invoke.outValue().hasNonDebugUsers()) {
+    if (options.isGeneratingDex() && invoke.hasOutValue() && invoke.outValue().hasNonDebugUsers()) {
       assert DexMoveResult.SIZE == DexMoveResultObject.SIZE;
       assert DexMoveResult.SIZE == DexMoveResultWide.SIZE;
       instructionLimit += DexMoveResult.SIZE;
@@ -327,10 +322,9 @@
     // Ensure that we don't introduce several monitors in the same method on old device that can
     // choke on this. If a context is forceinline, e.g., from class merging, don't ever inline
     // monitors, since that may conflict with a similar other constructor.
-    if (appView.options().canHaveIssueWithInlinedMonitors()) {
-      if (hasMonitorsOrIsSynchronized(singleTarget.getDefinition())
-          && (context.getOptimizationInfo().forceInline()
-              || code.metadata().mayHaveMonitorInstruction())) {
+    if (options.canHaveIssueWithInlinedMonitors() && hasMonitorsOrIsSynchronized(singleTarget)) {
+      if (context.getOptimizationInfo().forceInline()
+          || code.metadata().mayHaveMonitorInstruction()) {
         return null;
       }
     }
@@ -366,7 +360,7 @@
       ProgramMethod singleTarget,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     DexMethod singleTargetReference = singleTarget.getReference();
-    if (!appView.getKeepInfo(singleTarget).isInliningAllowed(appView.options())) {
+    if (!appView.getKeepInfo(singleTarget).isInliningAllowed(options)) {
       whyAreYouNotInliningReporter.reportPinned();
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java
index 69a5179..742750d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java
@@ -108,6 +108,10 @@
       return mapping.get(unboxedEnumValue);
     }
 
+    public ImmutableInt2ReferenceSortedMap<AbstractValue> getMapping() {
+      return mapping;
+    }
+
     public void forEach(BiConsumer<? super Integer, ? super AbstractValue> consumer) {
       mapping.forEach(consumer);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 183243b..6b8b450 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -69,8 +69,8 @@
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -739,7 +739,7 @@
             fixupProto(factory.prependHolderToProto(representative.getReference())),
             localUtilityClass.getType(),
             newMethodSignature -> !localUtilityMethods.containsKey(newMethodSignature));
-    Int2ObjectSortedMap<DexMethod> methodMap = new Int2ObjectLinkedOpenHashMap<>();
+    Int2ReferenceSortedMap<DexMethod> methodMap = new Int2ReferenceLinkedOpenHashMap<>();
     IdentityHashMap<DexType, DexMethod> typeToMethod = new IdentityHashMap<>();
     map.forEach(
         (methodReference, newMethodReference) ->
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
index 755f207..abff1ff 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.cf.code.CfConstString;
 import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfIf;
-import com.android.tools.r8.cf.code.CfIfCmp;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfLabel;
@@ -21,6 +20,8 @@
 import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.cf.code.CfStaticFieldRead;
 import com.android.tools.r8.cf.code.CfStaticFieldWrite;
+import com.android.tools.r8.cf.code.CfSwitch;
+import com.android.tools.r8.cf.code.CfSwitch.Kind;
 import com.android.tools.r8.cf.code.CfThrow;
 import com.android.tools.r8.cf.code.frame.FrameType;
 import com.android.tools.r8.errors.Unreachable;
@@ -36,9 +37,12 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldMappingData;
-import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
+import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.BooleanUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.BiConsumer;
 import org.objectweb.asm.Opcodes;
 
 public abstract class EnumUnboxingCfCodeProvider extends SyntheticCfCodeProvider {
@@ -68,17 +72,66 @@
     }
   }
 
+  <T> void addCfSwitch(
+      List<CfInstruction> instructions,
+      BiConsumer<List<CfInstruction>, T> generateCase,
+      Int2ReferenceSortedMap<T> cases,
+      T defaultCase,
+      CfFrame.Builder frameBuilder,
+      boolean defaultThrows) {
+    // The switch is *always* going to be converted to IR then either to dex or back to cf. The IR
+    // representation does not differentiate table and look-up switches, and generates the most
+    // appropriate one in the back-end.
+    // The keys should however be sorted in natural order for packing to table switch to be
+    // generated, which should be implicitly the case with the Int2ObjectSortedMap.
+    assert defaultCase == null || !defaultThrows;
+    boolean hasDefaultCase = defaultCase != null || defaultThrows;
+    assert cases.size() + BooleanUtils.intValue(hasDefaultCase) >= 2;
+    int[] keys = new int[cases.size() - BooleanUtils.intValue(!hasDefaultCase)];
+    List<CfLabel> targets = new ArrayList<>(keys.length);
+    int index = 0;
+    for (int key : cases.keySet()) {
+      if (index < keys.length) {
+        keys[index++] = key;
+        targets.add(new CfLabel());
+      }
+    }
+    CfLabel defaultLabel = new CfLabel();
+    T actualDefaultCase = hasDefaultCase ? defaultCase : cases.get(cases.lastIntKey());
+    assert ArrayUtils.isSorted(keys);
+    assert keys.length == targets.size();
+    // We expect the value to switch on to be in local slot 0.
+    instructions.add(new CfLoad(ValueType.fromDexType(appView.dexItemFactory().intType), 0));
+    instructions.add(new CfSwitch(Kind.LOOKUP, defaultLabel, keys, targets));
+    for (int i = 0; i < keys.length; i++) {
+      instructions.add(targets.get(i));
+      instructions.add(frameBuilder.build());
+      generateCase.accept(instructions, cases.get(keys[i]));
+      assert instructions.get(instructions.size() - 1).isReturn();
+    }
+    instructions.add(defaultLabel);
+    instructions.add(frameBuilder.build());
+    if (defaultThrows) {
+      // default: throw null;
+      instructions.add(new CfConstNull());
+      instructions.add(new CfThrow());
+    } else {
+      generateCase.accept(instructions, actualDefaultCase);
+      assert instructions.get(instructions.size() - 1).isReturn();
+    }
+  }
+
   public static class EnumUnboxingMethodDispatchCfCodeProvider extends EnumUnboxingCfCodeProvider {
 
     private final GraphLens codeLens;
     private final DexMethod superEnumMethod;
-    private final Int2ObjectSortedMap<DexMethod> methodMap;
+    private final Int2ReferenceSortedMap<DexMethod> methodMap;
 
     public EnumUnboxingMethodDispatchCfCodeProvider(
         AppView<?> appView,
         DexType holder,
         DexMethod superEnumMethod,
-        Int2ObjectSortedMap<DexMethod> methodMap) {
+        Int2ReferenceSortedMap<DexMethod> methodMap) {
       super(appView, holder);
       this.codeLens = appView.codeLens();
       this.superEnumMethod = superEnumMethod;
@@ -87,45 +140,15 @@
 
     @Override
     public CfCodeWithLens generateCfCode() {
-      // TODO(b/167942775): Should use a table-switch for large enums (maybe same threshold in the
-      //  rewriter of switchmaps).
-
       assert !methodMap.isEmpty();
-      DexItemFactory factory = appView.dexItemFactory();
-      boolean hasDefaultCase = superEnumMethod != null;
+      List<CfInstruction> instructions = new ArrayList<>();
       DexMethod representative = methodMap.values().iterator().next();
-
-      int invokeSize = representative.getParameters().size() + 2;
-      int branchSize = 5;
-      int instructionsSize =
-          methodMap.size() * (invokeSize + branchSize)
-              + (hasDefaultCase ? invokeSize : -branchSize);
-      List<CfInstruction> instructions = new ArrayList<>(instructionsSize);
-
       CfFrame.Builder frameBuilder = CfFrame.builder();
       for (DexType parameter : representative.getParameters()) {
         frameBuilder.appendLocal(FrameType.initialized(parameter));
       }
-      methodMap.forEach(
-          (unboxedEnumValue, method) -> {
-            boolean lastCase = methodMap.lastIntKey() == unboxedEnumValue && !hasDefaultCase;
-            if (!lastCase) {
-              CfLabel dest = new CfLabel();
-              instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0));
-              instructions.add(new CfConstNumber(unboxedEnumValue, ValueType.INT));
-              instructions.add(new CfIfCmp(IfType.NE, ValueType.INT, dest));
-              addReturnInvoke(instructions, method);
-              instructions.add(dest);
-              instructions.add(frameBuilder.build());
-            } else {
-              addReturnInvoke(instructions, method);
-            }
-          });
-
-      if (hasDefaultCase) {
-        addReturnInvoke(instructions, superEnumMethod);
-      }
-      assert instructions.size() == instructionsSize;
+      addCfSwitch(
+          instructions, this::addReturnInvoke, methodMap, superEnumMethod, frameBuilder, false);
       return new CfCodeWithLens(getHolder(), defaultMaxStack(), defaultMaxLocals(), instructions);
     }
 
@@ -187,44 +210,32 @@
 
     @Override
     public CfCode generateCfCode() {
-      // TODO(b/167942775): Should use a table-switch for large enums (maybe same threshold in the
-      //  rewriter of switchmaps).
       // Generated static method, for class com.x.MyEnum {A(10),B(20);} would look like:
       // String UtilityClass#com.x.MyEnum_toString(int i) {
-      // if (i == 1) { return 10;}
-      // if (i == 2) { return 20;}
-      // throw null;
+      //   switch (i) {
+      //     case 1: return 10;
+      //     case 2: return 20;
+      //     default: throw null; // or throw default value.
+      //   }
+      // }
       DexItemFactory factory = appView.dexItemFactory();
       List<CfInstruction> instructions = new ArrayList<>();
-
-      // if (i == 1) { return 10;}
-      // if (i == 2) { return 20;}
       CfFrame.Builder frameBuilder =
           CfFrame.builder().appendLocal(FrameType.initialized(factory.intType));
-      fieldDataMap.forEach(
-          (unboxedEnumValue, value) -> {
-            CfLabel dest = new CfLabel();
-            instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0));
-            instructions.add(new CfConstNumber(unboxedEnumValue, ValueType.INT));
-            instructions.add(new CfIfCmp(IfType.NE, ValueType.INT, dest));
-            addCfInstructionsForAbstractValue(instructions, value, returnType);
-            instructions.add(new CfReturn(ValueType.fromDexType(returnType)));
-            instructions.add(dest);
-            instructions.add(frameBuilder.build());
-          });
-
-      if (nullValue != null) {
-        // return "null"
-        addCfInstructionsForAbstractValue(instructions, nullValue, returnType);
-        instructions.add(new CfReturn(ValueType.fromDexType(returnType)));
-      } else {
-        // throw null;
-        instructions.add(new CfConstNull());
-        instructions.add(new CfThrow());
-      }
-
+      addCfSwitch(
+          instructions,
+          this::addReturnValue,
+          fieldDataMap.getMapping(),
+          nullValue,
+          frameBuilder,
+          nullValue == null);
       return standardCfCodeFromInstructions(instructions);
     }
+
+    private void addReturnValue(List<CfInstruction> instructions, AbstractValue value) {
+      addCfInstructionsForAbstractValue(instructions, value, returnType);
+      instructions.add(new CfReturn(ValueType.fromDexType(returnType)));
+    }
   }
 
   public static class EnumUnboxingValueOfCfCodeProvider extends EnumUnboxingCfCodeProvider {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 67a900a..bece7b9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -21,7 +21,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.TriFunction;
-import com.google.common.base.Strings;
 import java.util.List;
 import java.util.function.Consumer;
 import kotlin.Metadata;
@@ -106,9 +105,7 @@
       JvmMethodSignature methodSignature, int intArguments) {
     return new JvmMethodSignature(
         methodSignature.getName() + "$default",
-        methodSignature
-            .getDesc()
-            .replace(")", Strings.repeat("I", intArguments) + "Ljava/lang/Object;)"));
+        methodSignature.getDesc().replace(")", "I".repeat(intArguments) + "Ljava/lang/Object;)"));
   }
 
   static class KmPropertyProcessor {
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java
index 68f97d9..59e398c 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/ClassNameComputationInfo.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
-import com.google.common.base.Strings;
 
 public class ClassNameComputationInfo extends NameComputationInfo<DexType> {
 
@@ -45,7 +44,7 @@
         case NAME:
           name = getClassNameFromDescriptor(descriptor);
           if (arrayDepth > 0) {
-            name = Strings.repeat("[", arrayDepth) + "L" + name + ";";
+            name = "[".repeat(arrayDepth) + "L" + name + ";";
           }
           break;
 
@@ -60,7 +59,7 @@
         case CANONICAL_NAME:
           name = getCanonicalNameFromDescriptor(descriptor);
           if (arrayDepth > 0) {
-            name = name + Strings.repeat("[]", arrayDepth);
+            name = name + "[]".repeat(arrayDepth);
           }
           break;
 
@@ -74,7 +73,7 @@
             name = getUnqualifiedClassNameFromDescriptor(descriptor);
           }
           if (arrayDepth > 0) {
-            name = name + Strings.repeat("[]", arrayDepth);
+            name = name + "[]".repeat(arrayDepth);
           }
           break;
 
diff --git a/src/main/java/com/android/tools/r8/retrace/Partition.java b/src/main/java/com/android/tools/r8/retrace/Partition.java
index c7b942f..1582b5e 100644
--- a/src/main/java/com/android/tools/r8/retrace/Partition.java
+++ b/src/main/java/com/android/tools/r8/retrace/Partition.java
@@ -29,7 +29,7 @@
 
   private static final String USAGE_MESSAGE =
       StringUtils.lines(
-          "Usage: partition [options] <proguard-map>"
+          "Usage: partition [options] <proguard-map> "
               + "where <proguard-map> is a generated mapping file and options are:");
 
   public static List<ParseFlagInfo> getFlags() {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 03104f6..fda4b97 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -318,8 +318,7 @@
           (type, info) -> {
             DexType newType = lens.lookupType(type);
             if (newType == options.dexItemFactory().intType) {
-              // If the enum has been unboxed, then the keep info is no longer valid. This
-              // typically happens for conditional keep rules such as -keepclassmembers.
+              assert !info.isPinned(options);
               return;
             }
             assert newType == type
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 301d7b4..4b80427 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -47,7 +47,6 @@
   public final SyntheticKind RETARGET_CLASS = generator.forFixedClass("RetargetClass");
   public final SyntheticKind RETARGET_INTERFACE = generator.forFixedClass("RetargetInterface");
   public final SyntheticKind WRAPPER = generator.forFixedClass("$Wrapper");
-  public final SyntheticKind VIVIFIED = generator.forFixedClass("");
   public final SyntheticKind VIVIFIED_WRAPPER = generator.forFixedClass("$VivifiedWrapper");
   public final SyntheticKind INIT_TYPE_ARGUMENT = generator.forFixedClass("-IA");
   public final SyntheticKind HORIZONTAL_INIT_TYPE_ARGUMENT_1 =
@@ -169,6 +168,7 @@
     }
 
     SyntheticKind forFixedClass(String descriptor) {
+      assert !descriptor.isEmpty();
       return register(new SyntheticFixedClassKind(getNextId(), descriptor, false));
     }
 
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
index 7fef060..1e61b88 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -13,7 +13,6 @@
 // Finally a report is printed by:
 //     t.report();
 
-import com.google.common.base.Strings;
 import java.util.ArrayDeque;
 import java.util.Collection;
 import java.util.Deque;
@@ -251,7 +250,7 @@
 
     void printPrefix(int depth) {
       if (depth > 0) {
-        System.out.print(Strings.repeat("  ", depth));
+        System.out.print("  ".repeat(depth));
         System.out.print("- ");
       }
     }
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index bb2fef7..ef5d163 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -22,7 +22,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.android.tools.r8.utils.StringUtils;
-import com.beust.jcommander.internal.Lists;
+import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 import java.io.File;
 import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index ecfb059..57d23d8 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -159,6 +159,7 @@
                       .addLibraryFiles(dump.getLibraryArchive())
                       .addKeepRuleFiles(dump.getProguardConfigFile())
                       .setMinApi(dumpProperties.getMinApi())
+                      .allowUnnecessaryDontWarnWildcards()
                       .allowUnusedDontWarnPatterns()
                       .allowUnusedProguardConfigurationRules()
                       // TODO(b/222228826): Disallow unrecognized diagnostics and open interfaces.
@@ -215,6 +216,7 @@
                     programOutputs.add(
                         TestBase.testForD8(environment.getTemp(), Backend.DEX)
                             .addProgramFiles(programFile)
+                            .addClasspathFiles(dump.getProgramArchive())
                             .addLibraryFiles(dump.getLibraryArchive())
                             .setMinApi(dumpProperties.getMinApi())
                             .setIntermediate(true)
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index f87ec27..2537fea 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -22,8 +22,8 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
-import com.beust.jcommander.internal.Sets;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java
index c16ec8b..bbfcb4a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesInOutTest.java
@@ -183,12 +183,13 @@
     this.compilationSpecification = compilationSpecification;
   }
 
+  @SuppressWarnings("RedundantCast")
   private String getExpectedResult() {
     if (parameters.isCfRuntime()
         || libraryDesugaringSpecification.usesPlatformFileSystem(parameters)) {
-      return String.format(EXPECTED_RESULT, EXPECTED_RESULT_NO_DESUGARING);
+      return String.format(EXPECTED_RESULT, (Object[]) EXPECTED_RESULT_NO_DESUGARING);
     }
-    return String.format(EXPECTED_RESULT, EXPECTED_RESULT_DESUGARING);
+    return String.format(EXPECTED_RESULT, (Object[]) EXPECTED_RESULT_DESUGARING);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/CompanionClassNoSignatureTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/CompanionClassNoSignatureTest.java
new file mode 100644
index 0000000..47b8383
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/CompanionClassNoSignatureTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2023, 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.desugaring.interfacemethods;
+
+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.GenericSignature.ClassSignature;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class CompanionClassNoSignatureTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("I.foo");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+  }
+
+  public CompanionClassNoSignatureTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(I.class, A.class, TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(
+            inspector -> {
+              if (parameters
+                  .getApiLevel()
+                  .isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) {
+                ClassSignature signature =
+                    inspector.companionClassFor(I.class).getDexProgramClass().getClassSignature();
+                assertTrue(
+                    "Expected no signature, got: " + signature.toString(),
+                    signature.hasNoSignature());
+              }
+            });
+  }
+
+  interface I {
+    default void foo() {
+      System.out.println("I.foo");
+    }
+  }
+
+  static class A implements I {
+    // no override of default.
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/CompanionClassWithSignatureTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/CompanionClassWithSignatureTest.java
new file mode 100644
index 0000000..060af3f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/CompanionClassWithSignatureTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2023, 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.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+
+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.GenericSignature.ClassSignature;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class CompanionClassWithSignatureTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+  }
+
+  public CompanionClassWithSignatureTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public boolean isDesugaring() {
+    return parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeFalse(
+        "Art 7 crashes when resolving the default method on I.",
+        parameters.isDexRuntimeVersion(Version.V7_0_0) && !isDesugaring());
+    boolean resolvedBug280356274 = true;
+    String expected = StringUtils.lines(resolvedBug280356274 && isDesugaring() ? "[]" : "[T]");
+    testForD8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addInnerClasses(getClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(expected)
+        .inspect(
+            inspector -> {
+              // Interface I must retain its signature.
+              assertTrue(
+                  inspector.clazz(I.class).getDexProgramClass().getClassSignature().hasSignature());
+              // If desugaring the companion class should not have a signature.
+              if (isDesugaring()) {
+                ClassSignature signature =
+                    inspector.companionClassFor(I.class).getDexProgramClass().getClassSignature();
+                assertTrue(
+                    "Expected no signature, got: " + signature.toString(),
+                    signature.hasNoSignature());
+              }
+            });
+  }
+
+  interface J {}
+
+  interface I<T extends I> extends J {
+    T self();
+
+    default T foo() {
+      Object o = new Object() {};
+      // The class constant here is either the interface I or its companion class.
+      // The desugared companion class is *not* I, thus its type parameters are not the same as
+      // those of I.
+      Class<?> interfaceOrCompanion = o.getClass().getEnclosingMethod().getDeclaringClass();
+      System.out.println(Arrays.toString(interfaceOrCompanion.getTypeParameters()));
+      return self();
+    }
+  }
+
+  static class A implements I<A> {
+
+    @Override
+    public A self() {
+      return this;
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/loop/UdpServer.java b/src/test/java/com/android/tools/r8/examples/loop/UdpServer.java
index c76a3ba..9bada3a 100644
--- a/src/test/java/com/android/tools/r8/examples/loop/UdpServer.java
+++ b/src/test/java/com/android/tools/r8/examples/loop/UdpServer.java
@@ -15,18 +15,19 @@
   private static final String PREFIX = "RANDOM_DATA_PREFIX_";
   public static void main(String[] args) throws Exception {
     ExecutorService service = Executors.newWorkStealingPool(2);
-    Callable c = new Callable() {
-      @Override
-      public Object call() throws Exception {
-        int counter = 0;
-        byte[] receiveData = new byte[1024];
-        while (true) {
-          // Mimic receiving data via socket. (A use of actual socket is IO blocking.)
-          receiveData = (PREFIX + counter++).getBytes();
-        }
-      }
-    };
-    Future<?> f = service.submit(c);
+    Callable<Object> c =
+        new Callable<Object>() {
+          @Override
+          public Object call() throws Exception {
+            int counter = 0;
+            byte[] receiveData = new byte[1024];
+            while (true) {
+              // Mimic receiving data via socket. (A use of actual socket is IO blocking.)
+              receiveData = (PREFIX + counter++).getBytes();
+            }
+          }
+        };
+    Future<Object> f = service.submit(c);
     try {
       f.get(1, TimeUnit.NANOSECONDS);
     } catch (ExecutionException | InterruptedException | TimeoutException e) {
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index eef3dcf..e220e0a 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -6,8 +6,9 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
 import com.android.tools.r8.R8TestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.StringResource;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -53,20 +54,6 @@
   }
 
   @Test
-  public void testR8Recompilation() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(Backend.CF)
-            .addProgramFiles(outDirectory.resolve("program.jar"))
-            .apply(this::configure)
-            .apply(this::configureCf)
-            .compile();
-    testForR8(Backend.DEX)
-        .addProgramFiles(compileResult.writeToZip())
-        .apply(this::configure)
-        .compile();
-  }
-
-  @Test
   public void testR8Compat() throws Exception {
     testForR8Compat(Backend.DEX)
         .addProgramFiles(outDirectory.resolve("program.jar"))
@@ -74,20 +61,6 @@
         .compile();
   }
 
-  @Test
-  public void testR8CompatRecompilation() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8Compat(Backend.CF)
-            .addProgramFiles(outDirectory.resolve("program.jar"))
-            .apply(this::configure)
-            .apply(this::configureCf)
-            .compile();
-    testForR8Compat(Backend.DEX)
-        .addProgramFiles(compileResult.writeToZip())
-        .apply(this::configure)
-        .compile();
-  }
-
   private void configure(R8TestBuilder<?> testBuilder) {
     testBuilder
         .addClasspathFiles(outDirectory.resolve("classpath.jar"))
@@ -99,10 +72,11 @@
         .allowDiagnosticMessages()
         .allowUnnecessaryDontWarnWildcards()
         .allowUnusedDontWarnPatterns()
-        .allowUnusedProguardConfigurationRules();
-  }
-
-  private void configureCf(R8TestBuilder<?> testBuilder) {
-    testBuilder.addOptionsModification(options -> options.horizontalClassMergerOptions().disable());
+        .allowUnusedProguardConfigurationRules()
+        .enableCoreLibraryDesugaring(
+            LibraryDesugaringTestConfiguration.builder()
+                .addDesugaredLibraryConfiguration(
+                    StringResource.fromFile(outDirectory.resolve("desugared-library.json")))
+                .build());
   }
 }
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
index 6a03cd1..38870bd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
@@ -24,7 +24,7 @@
 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 com.google.common.collect.Lists;
 import java.io.IOException;
 import java.io.Serializable;
 import java.util.Arrays;
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 4321812..ade911f 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -13,6 +13,13 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
+import com.android.tools.smali.dexlib2.Opcodes;
+import com.android.tools.smali.dexlib2.writer.builder.DexBuilder;
+import com.android.tools.smali.dexlib2.writer.io.MemoryDataStore;
+import com.android.tools.smali.smali.LexerErrorInterface;
+import com.android.tools.smali.smali.smaliFlexLexer;
+import com.android.tools.smali.smali.smaliParser;
+import com.android.tools.smali.smali.smaliTreeWalker;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.io.Reader;
@@ -27,13 +34,6 @@
 import org.antlr.runtime.TokenSource;
 import org.antlr.runtime.tree.CommonTree;
 import org.antlr.runtime.tree.CommonTreeNodeStream;
-import org.jf.dexlib2.Opcodes;
-import org.jf.dexlib2.writer.builder.DexBuilder;
-import org.jf.dexlib2.writer.io.MemoryDataStore;
-import org.jf.smali.LexerErrorInterface;
-import org.jf.smali.smaliFlexLexer;
-import org.jf.smali.smaliParser;
-import org.jf.smali.smaliTreeWalker;
 
 // Adapted from org.jf.smali.SmaliTestUtils.
 public class Smali {
@@ -65,7 +65,7 @@
     for (String smaliText : smaliTexts) {
       Reader reader = new StringReader(smaliText);
 
-      LexerErrorInterface lexer = new smaliFlexLexer(reader);
+      LexerErrorInterface lexer = new smaliFlexLexer(reader, apiLevel);
       CommonTokenStream tokens = new CommonTokenStream((TokenSource) lexer);
 
       smaliParser parser = new smaliParser(tokens);
diff --git a/third_party/opensource-apps/tivi.tar.gz.sha1 b/third_party/opensource-apps/tivi.tar.gz.sha1
index dc3e882..1c7e3a4 100644
--- a/third_party/opensource-apps/tivi.tar.gz.sha1
+++ b/third_party/opensource-apps/tivi.tar.gz.sha1
@@ -1 +1 @@
-b5b44fb38064e69308e980fd33651ce03a0b1977
\ No newline at end of file
+97a01f49797a74321ad0181ae078f77d3b226d10
\ No newline at end of file