Merge "Add initial luci files for bot move"
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 51f9de0..c0008dc 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.cf.FixedLocalValue;
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -618,6 +619,13 @@
       return realRegisterNumberFromAllocated(value.asFixedRegisterValue().getRegister());
     }
     LiveIntervals intervals = value.getLiveIntervals();
+    if (intervals == null) {
+      throw new CompilationError(
+          "Unexpected attempt to get register for a value without a register in method `"
+              + code.method.method.toSourceString()
+              + "`.",
+          code.origin);
+    }
     if (intervals.hasSplits()) {
       intervals = intervals.getSplitCovering(instructionNumber);
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index dc8925a..996f83f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -386,24 +386,44 @@
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isPattern();
             wildcardPattern = wildcard.asPattern();
+
             boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
-            int nextPatternIndex = i + (includeSeparators ? 2 : 1);
-            // Fast cases for the common case where a pattern ends with '**' or '*'.
+            boolean includeAll =
+                includeSeparators && pattern.length() > (i + 2) && pattern.charAt(i + 2) == '*';
+            int nextPatternIndex = i + 1;
+            if (includeAll) {
+              nextPatternIndex += 2;
+            } else if (includeSeparators) {
+              nextPatternIndex += 1;
+            }
+
+            // Fast cases for the common case where a pattern ends with  '*', '**', or '***'.
             if (nextPatternIndex == pattern.length()) {
-              wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+              wildcardPattern.setCaptured(name.substring(nameIndex));
+              if (includeAll) {
+                return true;
+              }
               if (includeSeparators) {
                 return kind == ClassOrType.CLASS || !isArrayType(name);
               }
               boolean hasSeparators = containsSeparatorsStartingAt(name, nameIndex);
               return !hasSeparators && (kind == ClassOrType.CLASS || !isArrayType(name));
             }
+
             // Match the rest of the pattern against the (non-empty) rest of the class name.
             for (int nextNameIndex = nameIndex; nextNameIndex < name.length(); nextNameIndex++) {
               wildcardPattern.setCaptured(name.substring(nameIndex, nextNameIndex));
-              if (!includeSeparators && name.charAt(nextNameIndex) == '.') {
-                return matchClassOrTypeNameImpl(
-                    pattern, nextPatternIndex, name, nextNameIndex, wildcards, wildcardIndex + 1,
-                    kind);
+              if (!includeSeparators) {
+                if (name.charAt(nextNameIndex) == '.') {
+                  return matchClassOrTypeNameImpl(
+                      pattern,
+                      nextPatternIndex,
+                      name,
+                      nextNameIndex,
+                      wildcards,
+                      wildcardIndex + 1,
+                      kind);
+                }
               }
               if (kind == ClassOrType.TYPE && name.charAt(nextNameIndex) == '[') {
                 return matchClassOrTypeNameImpl(
@@ -416,11 +436,12 @@
                 return true;
               }
             }
-            // Finally, check the case where the '*' or '**' eats all of the class name.
-            wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+
+            // Finally, check the case where the '*', '**', or '***' eats all of the class name.
+            wildcardPattern.setCaptured(name.substring(nameIndex));
             return matchClassOrTypeNameImpl(
-                pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1,
-                kind);
+                pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1, kind);
+
           case '?':
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isPattern();
@@ -432,6 +453,7 @@
             nameIndex++;
             wildcardIndex++;
             break;
+
           case '<':
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isBackReference();
@@ -446,6 +468,7 @@
             wildcardIndex++;
             i = pattern.indexOf(">", i);
             break;
+
           default:
             if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
               return false;
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 659a8fc..37a0215 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -89,7 +89,7 @@
 
     // Convince R8 we only use subtypes to get class merging of Foo into FooImpl.
     Foo<String> foo = new FooImpl<>();
-    System.out.println(foo.getClass().getTypeName());
+    System.out.println(foo.getClass().getCanonicalName());
   }
 }
 
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
new file mode 100644
index 0000000..d5f456b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.proguard.rules;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+
+/** Regression test for b/124584385. */
+public class ProguardMatchAllRuleWithPrefixTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    CodeInspector inspector =
+        testForR8(Backend.DEX)
+            .addProgramClasses(TestClass.class)
+            .addKeepRules(
+                "-keep,allowobfuscation class com.android.tools.r8.*** {",
+                "  com.android.tools.r8.*** methodA();",
+                "  com.android.tools.r8.***Class methodB();",
+                "  com.android.tools.r8.***[] methodC();",
+                "  com.android.tools.r8.***Class[] methodD();",
+                "}")
+            .compile()
+            .inspector();
+
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+    assertThat(classSubject, isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodA"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodB"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodC"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodD"), isPresent());
+  }
+
+  static class TestClass {
+
+    TestClass methodA() {
+      return new TestClass();
+    }
+
+    TestClass methodB() {
+      return new TestClass();
+    }
+
+    TestClass[] methodC() {
+      return new TestClass[0];
+    }
+
+    TestClass[] methodD() {
+      return new TestClass[0];
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index aa083da..f585226 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -321,6 +321,14 @@
     return obfuscatedTypeName != null ? obfuscatedTypeName : originalTypeName;
   }
 
+  String getOriginalTypeName(String minifiedTypeName) {
+    String originalTypeName = null;
+    if (mapping != null) {
+      originalTypeName = mapType(obfuscatedToOriginalMapping, minifiedTypeName);
+    }
+    return originalTypeName != null ? originalTypeName : minifiedTypeName;
+  }
+
   InstructionSubject createInstructionSubject(Instruction instruction) {
     DexInstructionSubject dexInst = new DexInstructionSubject(instruction);
     if (dexInst.isInvoke()) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 6581a47..dd7cd85 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -143,18 +143,14 @@
     //     X method(X) -> a
     //
     // whereas the final signature is for X.a is "a (a)"
-    String[] OriginalParameters = new String[signature.parameters.length];
-    for (int i = 0; i < OriginalParameters.length; i++) {
-      String obfuscated = signature.parameters[i];
-      String original = codeInspector.obfuscatedToOriginalMapping.get(obfuscated);
-      OriginalParameters[i] = original != null ? original : obfuscated;
+    String[] originalParameters = new String[signature.parameters.length];
+    for (int i = 0; i < originalParameters.length; i++) {
+      originalParameters[i] = codeInspector.getOriginalTypeName(signature.parameters[i]);
     }
-    String obfuscatedReturnType = signature.type;
-    String originalReturnType = codeInspector.obfuscatedToOriginalMapping.get(obfuscatedReturnType);
-    String returnType = originalReturnType != null ? originalReturnType : obfuscatedReturnType;
+    String returnType = codeInspector.getOriginalTypeName(signature.type);
 
     MethodSignature lookupSignature =
-        new MethodSignature(signature.name, returnType, OriginalParameters);
+        new MethodSignature(signature.name, returnType, originalParameters);
 
     MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
     return memberNaming != null ? (MethodSignature) memberNaming.getOriginalSignature() : signature;
diff --git a/tools/as_utils.py b/tools/as_utils.py
index 817f7ed..363e1b9 100644
--- a/tools/as_utils.py
+++ b/tools/as_utils.py
@@ -47,6 +47,14 @@
       'Unsupported gradle version: {} (must use at least gradle '
           + 'version 3.2)').format(gradle_version)
 
+def add_settings_gradle(checkout_dir, name):
+  settings_file = os.path.join(checkout_dir, 'settings.gradle')
+  if os.path.isfile(settings_file):
+    return
+
+  with open(settings_file, "w+") as f:
+    f.write("rootProject.name = '{}'\n".format(name))
+
 def remove_r8_dependency(checkout_dir):
   build_file = os.path.join(checkout_dir, 'build.gradle')
   assert os.path.isfile(build_file), (
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index d6dd647..f3cc2eb 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -107,6 +107,7 @@
       'app_module': '',
       'flavor': 'play',
       'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
+      'main_dex_rules': 'multidex-config.pro',
       'revision': '85e1a10993e5e9ffe923f0798b26cbc44068ba31',
       'releaseTarget': 'assemblePlayRelease',
       'signed-apk-name': 'Signal-play-release-4.32.7.apk',
@@ -353,6 +354,13 @@
 
           # Now rebuild generated apk.
           previous_apk = apk_dest
+
+          # We may need main dex rules when re-compiling with R8 as standalone.
+          main_dex_rules = None
+          if config.get('main_dex_rules'):
+            main_dex_rules = os.path.join(
+                checkout_dir, config.get('main_dex_rules'))
+
           for i in range(1, options.r8_compilation_steps):
             try:
               recompiled_apk_dest = os.path.join(
@@ -360,7 +368,7 @@
               RebuildAppWithShrinker(
                   app, previous_apk, recompiled_apk_dest,
                   ext_proguard_config_file, shrinker, min_sdk, compile_sdk,
-                  options, temp_dir)
+                  options, temp_dir, main_dex_rules)
               recompilation_result = {
                 'apk_dest': recompiled_apk_dest,
                 'build_status': 'success',
@@ -394,6 +402,11 @@
       shrinker,
       ' for recompilation' if keepRuleSynthesisForRecompilation else ''))
 
+  # Add settings.gradle file if it is not present to prevent gradle from finding
+  # the settings.gradle file in the r8 root when apps are placed under
+  # $R8/build.
+  as_utils.add_settings_gradle(checkout_dir, app)
+
   # Add 'r8.jar' from top-level build.gradle.
   as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
 
@@ -478,7 +491,7 @@
 
 def RebuildAppWithShrinker(
     app, apk, apk_dest, proguard_config_file, shrinker, min_sdk, compile_sdk,
-    options, temp_dir):
+    options, temp_dir, main_dex_rules):
   assert 'r8' in shrinker
   assert apk_dest.endswith('.apk')
 
@@ -502,6 +515,10 @@
     cmd.append('--lib')
     cmd.append(android_optional_jar)
 
+  if main_dex_rules:
+    cmd.append('--main-dex-rules')
+    cmd.append(main_dex_rules)
+
   utils.RunCmd(cmd, quiet=options.quiet)
 
   # Make a copy of the given APK, move the newly generated dex files into the