Merge "Use same repository url in all configs"
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 95c4545..6e9531a 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -95,9 +95,11 @@
     }
     builders {
       name: "d8-linux"
-      properties: "tool:d8"
-      properties: "aosp_jar:True"
       mixins: "linux"
+      recipe {
+        properties: "tool:d8"
+        properties: "aosp_jar:True"
+      }
     }
     builders {
       name: "d8-linux-android-4.0.4"
@@ -133,9 +135,11 @@
     }
     builders {
       name: "d8-linux-android-7.0.0"
-      properties: "tool:d8"
-      properties: "dex_vm:7.0.0"
       mixins: "linux"
+      recipe {
+        properties: "tool:d8"
+        properties: "dex_vm:7.0.0"
+      }
     }
     builders {
       name: "d8-linux-android-7.0.0_release"
@@ -156,7 +160,9 @@
     builders {
       name: "linux"
       mixins: "linux"
-      properties: "tool:r8"
+      recipe {
+        properties: "tool:r8"
+      }
     }
     builders {
       name: "linux-android-4.0.4"
@@ -192,9 +198,11 @@
     }
     builders {
       name: "linux-android-7.0.0"
-      properties: "tool:r8"
-      properties: "dex_vm:7.0.0"
       mixins: "linux"
+      recipe {
+        properties: "tool:r8"
+        properties: "dex_vm:7.0.0"
+      }
     }
     builders {
       name: "linux-android-7.0.0_release"
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index 9dfddb2..8522f1f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -26,7 +26,9 @@
       "transient",
       "synchronized",
       "native",
-      "strictfp"
+      "strictfp",
+      "synthetic",
+      "bridge"
   );
 
   // Get ordered list of flag predicates. Must be consistent with getNames.
@@ -42,7 +44,9 @@
         this::isTransient,
         this::isSynchronized,
         this::isNative,
-        this::isStrict);
+        this::isStrict,
+        this::isSynthetic,
+        this::isBridge);
   }
 
   private boolean containsAll(int other) {
@@ -163,6 +167,22 @@
     return isSet(Constants.ACC_STRICT);
   }
 
+  public void setSynthetic() {
+    set(Constants.ACC_SYNTHETIC);
+  }
+
+  public boolean isSynthetic() {
+    return isSet(Constants.ACC_SYNTHETIC);
+  }
+
+  public void setBridge() {
+    set(Constants.ACC_BRIDGE);
+  }
+
+  public boolean isBridge() {
+    return isSet(Constants.ACC_BRIDGE);
+  }
+
   private boolean isSet(int flag) {
     return (flags & flag) != 0;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index eaad9a4..a27f4b0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -953,6 +953,11 @@
               flags.setAbstract();
             }
             break;
+          case 'b':
+            if ((found = acceptString("bridge"))) {
+              flags.setBridge();
+            }
+            break;
           case 'f':
             if ((found = acceptString("final"))) {
               flags.setFinal();
@@ -979,6 +984,8 @@
               flags.setStatic();
             } else if ((found = acceptString("strictfp"))) {
               flags.setStrict();
+            } else if ((found = acceptString("synthetic"))) {
+              flags.setSynthetic();
             }
             break;
           case 't':
diff --git a/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
new file mode 100644
index 0000000..7f205f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.modifiers;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class SyntheticAndBridgeModifiersTest extends TestBase {
+
+  static final List<Class<?>> CLASSES = ImmutableList.of(
+      SyntheticAndBridgeModifiersTestClass.class,
+      SyntheticAndBridgeModifiersTestClass.Super.class,
+      SyntheticAndBridgeModifiersTestClass.TestClass.class);
+
+  private void runTest(String keepRules, Consumer<ClassSubject> classConsumer) throws Exception {
+    testForProguard()
+        .addProgramClasses(CLASSES)
+        .addKeepRules(keepRules)
+        .noMinification()
+        .compile()
+        .disassemble()
+        .inspect(inspector -> {
+          ClassSubject clazz =
+              inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+          assertThat(clazz, isPresent());
+          classConsumer.accept(clazz);
+        });
+
+    testForR8(Backend.DEX)
+        .addProgramClasses(CLASSES)
+        .addKeepRules(keepRules)
+        .noMinification()
+        .compile()
+        .disassemble()
+        .inspect(inspector -> {
+          ClassSubject clazz =
+              inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+          assertThat(clazz, isPresent());
+          classConsumer.accept(clazz);
+        });
+  }
+
+  private long methodsWithNameStartingWith(ClassSubject clazz, String prefix) {
+    return clazz
+        .allMethods()
+        .stream()
+        .filter(method -> method.getOriginalName().startsWith(prefix))
+        .count();
+  }
+
+  @Test
+  public void testBridgeNotKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { java.lang.String x(); }",
+        clazz -> {
+          assertThat(clazz.method("java.lang.Object", "x", ImmutableList.of()), not(isPresent()));
+          assertThat(clazz.method("java.lang.String", "x", ImmutableList.of()), isPresent());
+        });
+  }
+
+  @Test
+  public void testBridgeKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { bridge ** x(); }",
+        clazz ->
+            assertThat(clazz.method("java.lang.Object", "x", ImmutableList.of()), isPresent()));
+  }
+
+  @Test
+  public void testSyntheticFieldNotKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { java.lang.String x(); }",
+        clazz -> assertEquals(0, clazz.allFields().size()));
+  }
+
+  @Test
+  public void testSyntheticFieldKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { synthetic ** this*; }",
+        clazz -> assertEquals(1, clazz.allFields().size()));
+  }
+
+  @Test
+  public void testSyntheticAccessorNotKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { java.lang.String x(); }",
+        clazz -> assertEquals(0, methodsWithNameStartingWith(clazz, "access")));
+  }
+
+  @Test
+  public void testSyntheticAccessorKept() throws Exception {
+    runTest(
+        "-keep class **TestClass$TestClass { static synthetic *; }",
+        clazz -> assertEquals(1, methodsWithNameStartingWith(clazz, "access")));
+  }
+}
+
+class SyntheticAndBridgeModifiersTestClass {
+  class Super {
+    Object x() {
+      return null;
+    }
+  }
+
+  // Non static inner classes will have a synthetic field for the outer instance (javac
+  // named this$X).
+  class TestClass extends Super {
+    // This will have a synthetic static accessor (javac named access$XXX).
+    private void test() {
+
+    }
+
+    // javac will create a forwarding bridge with signature 'Object x()'.
+    String x() {
+      return null;
+    }
+  }
+
+  void accessPrivate() {
+    new TestClass().test();
+  }
+}
\ No newline at end of file
diff --git a/tools/apk_masseur.py b/tools/apk_masseur.py
index be380ed..4b13a32 100755
--- a/tools/apk_masseur.py
+++ b/tools/apk_masseur.py
@@ -16,7 +16,12 @@
 def parse_options():
   parser = optparse.OptionParser(usage=USAGE)
   parser.add_option('--dex',
-                    help='directory with dex files to use instead of those in the apk',
+                    help=('directory with dex files to use instead of those in '
+                        + 'the apk'),
+                    default=None)
+  parser.add_option('--resources',
+                    help=('pattern that matches resources to use instead of '
+                        + 'those in the apk'),
                     default=None)
   parser.add_option('--out',
                     help='output file (default ./$(basename <apk>))',
@@ -43,32 +48,39 @@
 def findKeystore():
   return os.path.join(os.getenv('HOME'), '.android', 'app.keystore')
 
-def repack(processed_out, original_apk, temp, quiet):
+def repack(apk, processed_out, resources, temp, quiet):
   processed_apk = os.path.join(temp, 'processed.apk')
-  shutil.copyfile(original_apk, processed_apk)
+  shutil.copyfile(apk, processed_apk)
   if not processed_out:
     utils.Print('Using original APK as is', quiet=quiet)
     return processed_apk
   utils.Print(
       'Repacking APK with dex files from {}'.format(processed_apk), quiet=quiet)
+
+  # Delete original dex files in APK.
   with utils.ChangedWorkingDirectory(temp, quiet=quiet):
     cmd = ['zip', '-d', 'processed.apk', '*.dex']
     utils.RunCmd(cmd, quiet=quiet)
+
+  # Unzip the jar or zip file into `temp`.
   if processed_out.endswith('.zip') or processed_out.endswith('.jar'):
     cmd = ['unzip', processed_out, '-d', temp]
     if quiet:
       cmd.insert(1, '-q')
     utils.RunCmd(cmd, quiet=quiet)
     processed_out = temp
+
+  # Insert the new dex and resource files from `processed_out` into the APK.
   with utils.ChangedWorkingDirectory(processed_out, quiet=quiet):
-    dex = glob.glob('*.dex')
-    cmd = ['zip', '-u', '-9', processed_apk] + dex
+    dex_files = glob.glob('*.dex')
+    resource_files = glob.glob(resources) if resources else []
+    cmd = ['zip', '-u', '-9', processed_apk] + dex_files + resource_files
     utils.RunCmd(cmd, quiet=quiet)
   return processed_apk
 
 def sign(unsigned_apk, keystore, temp, quiet):
   signed_apk = os.path.join(temp, 'unaligned.apk')
-  apk_utils.sign(unsigned_apk, signed_apk, keystore, quiet=quiet)
+  apk_utils.sign_with_apksigner(unsigned_apk, signed_apk, keystore, quiet=quiet)
   return signed_apk
 
 def align(signed_apk, temp, quiet):
@@ -88,8 +100,8 @@
   return signed_apk
 
 def masseur(
-    apk, dex=None, out=None, adb_options=None, keystore=None, install=False,
-    quiet=False):
+    apk, dex=None, resources=None, out=None, adb_options=None, keystore=None,
+    install=False, quiet=False):
   if not out:
     out = os.path.basename(apk)
   if not keystore:
@@ -97,7 +109,7 @@
   with utils.TempDir() as temp:
     processed_apk = None
     if dex:
-      processed_apk = repack(dex, apk, temp, quiet)
+      processed_apk = repack(apk, dex, resources, temp, quiet)
     else:
       utils.Print(
           'Signing original APK without modifying dex files', quiet=quiet)
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
index af24ec1..41c80c2 100644
--- a/tools/apk_utils.py
+++ b/tools/apk_utils.py
@@ -24,9 +24,9 @@
   utils.RunCmd(cmd, quiet=quiet)
 
 def sign_with_apksigner(
-    build_tools_dir, unsigned_apk, signed_apk, keystore, password, quiet=False):
+    unsigned_apk, signed_apk, keystore, password='android', quiet=False):
   cmd = [
-    os.path.join(build_tools_dir, 'apksigner'),
+    os.path.join(utils.ANDROID_BUILD_TOOLS, 'apksigner'),
     'sign',
     '-v',
     '--ks', keystore,
diff --git a/tools/gradle.py b/tools/gradle.py
index 232e96d..8a3502d 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -7,7 +7,7 @@
 # Will make sure we pulled down gradle before running, and will use the pulled
 # down version to have a consistent developer experience.
 
-import optparse
+import argparse
 import os
 import subprocess
 import sys
@@ -25,10 +25,10 @@
   GRADLE = os.path.join(GRADLE_DIR, 'gradle', 'bin', 'gradle')
 
 def ParseOptions():
-  result = optparse.OptionParser()
-  result.add_option('--java-home', '--java_home',
+  parser = argparse.ArgumentParser(description = 'Call gradle.')
+  parser.add_argument('--java-home', '--java_home',
       help='Use a custom java version to run gradle.')
-  return result.parse_args()
+  return parser.parse_known_args()
 
 def GetJavaEnv(env):
   return dict(env if env else os.environ, JAVA_HOME = jdk.GetJdkHome())
@@ -92,10 +92,9 @@
 
 def Main():
   (options, args) = ParseOptions()
-  gradle_args = sys.argv[1:]
   if options.java_home:
-    gradle_args.append('-Dorg.gradle.java.home=' + options.java_home)
-  return RunGradle(gradle_args)
+    args.append('-Dorg.gradle.java.home=' + options.java_home)
+  return RunGradle(args)
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index ac27750..0a4f740 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -178,10 +178,14 @@
 
 def ExtractMarker(apk, temp_dir, options):
   r8_jar = os.path.join(temp_dir, 'r8.jar')
+  r8lib_jar = os.path.join(temp_dir, 'r8lib.jar')
 
-  # Use the copy of r8.jar if it is there.
+  # Use the copy of r8.jar or r8lib.jar if one is there.
   if os.path.isfile(r8_jar):
     cmd = [jdk.GetJavaExecutable(), '-ea', '-jar', r8_jar, 'extractmarker', apk]
+  elif os.path.isfile(r8lib_jar):
+    cmd = [jdk.GetJavaExecutable(), '-ea', '-cp', r8lib_jar,
+        'com.android.tools.r8.SwissArmyKnife', 'extractmarker', apk]
   else:
     script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
     cmd = ['python', script, apk]
@@ -491,7 +495,6 @@
     assert os.path.isfile(unsigned_apk)
     if options.sign_apks:
       apk_utils.sign_with_apksigner(
-          utils.ANDROID_BUILD_TOOLS,
           unsigned_apk,
           signed_apk,
           options.keystore,
@@ -531,9 +534,9 @@
   # is 'r8'.
   entry_point = 'com.android.tools.r8.R8'
 
-  cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar, entry_point,
-      '--release', '--min-api', str(min_sdk), '--pg-conf', proguard_config_file,
-      '--lib', android_jar, '--output', zip_dest, apk]
+  cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar,
+      entry_point, '--release', '--min-api', str(min_sdk), '--pg-conf',
+      proguard_config_file, '--lib', android_jar, '--output', zip_dest, apk]
 
   for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
     cmd.append('--lib')
@@ -548,7 +551,8 @@
   # Make a copy of the given APK, move the newly generated dex files into the
   # copied APK, and then sign the APK.
   apk_masseur.masseur(
-    apk, dex=zip_dest, out=apk_dest, quiet=options.quiet)
+      apk, dex=zip_dest, resources='META-INF/services/*', out=apk_dest,
+      quiet=options.quiet)
 
 def RunMonkey(app, config, options, apk_dest):
   if not WaitForEmulator():