Merge "Update duplicate guards test"
diff --git a/.gitignore b/.gitignore
index 7b33dbf..d9cb978 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,8 @@
 third_party/jdwp-tests
 third_party/kotlin.tar.gz
 third_party/kotlin
+third_party/nest/*
+!third_party/nest/*.sha1
 third_party/photos/*
 !third_party/photos/*.sha1
 third_party/proguard/*
diff --git a/build.gradle b/build.gradle
index 2276798..a542ea4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -339,6 +339,7 @@
         "gmscore/gmscore_v9",
         "gmscore/gmscore_v10",
         "gmscore/latest",
+        "nest/nest_20180926_7c6cfb",
         "photos/2017-06-06",
         "youtube/youtube.android_12.10",
         "youtube/youtube.android_12.17",
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index bbc5922..3df31fd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -14,6 +14,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 public abstract class DexClass extends DexDefinition {
 
@@ -432,6 +433,10 @@
     innerClasses.clear();
   }
 
+  public void removeInnerClasses(Predicate<InnerClassAttribute> predicate) {
+    innerClasses.removeIf(predicate::test);
+  }
+
   /** Returns kotlin class info if the class is synthesized by kotlin compiler. */
   public abstract KotlinInfo getKotlinInfo();
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index 6a0b9bc..ffbd3fd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -51,6 +51,13 @@
     analyze();
   }
 
+  public void widening(Iterable<Value> values) {
+    mode = Mode.WIDENING;
+    assert worklist.isEmpty();
+    values.forEach(this::enqueue);
+    analyze();
+  }
+
   public void narrowing(Iterable<Value> values) {
     mode = Mode.NARROWING;
     assert worklist.isEmpty();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a883959..a662e1e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -161,7 +161,7 @@
     this.enableWholeProgramOptimizations = appView != null;
     if (enableWholeProgramOptimizations) {
       assert appInfo.hasLiveness();
-      this.nonNullTracker = new NonNullTracker();
+      this.nonNullTracker = new NonNullTracker(appInfo);
       this.inliner = new Inliner(appView.withLiveness(), this, options);
       this.outliner = new Outliner(appInfo.withLiveness(), options, this);
       this.memberValuePropagation =
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 fc11b78..479c3e4 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
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -19,6 +20,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IteratorUtils;
 import java.util.BitSet;
@@ -29,6 +31,7 @@
 
 final class DefaultInliningOracle implements InliningOracle, InliningStrategy {
 
+  private final AppView<? extends AppInfoWithLiveness> appView;
   private final Inliner inliner;
   private final DexEncodedMethod method;
   private final IRCode code;
@@ -40,6 +43,7 @@
   private int instructionAllowance;
 
   DefaultInliningOracle(
+      AppView<? extends AppInfoWithLiveness> appView,
       Inliner inliner,
       DexEncodedMethod method,
       IRCode code,
@@ -48,6 +52,7 @@
       InternalOptions options,
       int inliningInstructionLimit,
       int inliningInstructionAllowance) {
+    this.appView = appView;
     this.inliner = inliner;
     this.method = method;
     this.code = code;
@@ -379,8 +384,9 @@
       assert IteratorUtils.peekNext(blockIterator) == block;
 
       // Kick off the tracker to add non-null IRs only to the inlinee blocks.
-      Set<Value> nonNullValues = new NonNullTracker()
-          .addNonNullInPart(code, blockIterator, inlinee.blocks::contains);
+      Set<Value> nonNullValues =
+          new NonNullTracker(appView.appInfo())
+              .addNonNullInPart(code, blockIterator, inlinee.blocks::contains);
       assert !blockIterator.hasNext();
 
       // Restore the old state of the iterator.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 000eac2..2a74cad 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -559,6 +559,7 @@
       int inliningInstructionAllowance) {
 
     return new DefaultInliningOracle(
+        appView,
         this,
         method,
         code,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index e6997d4..536eac8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.ir.optimize;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.IRCode;
@@ -30,7 +32,10 @@
 
 public class NonNullTracker {
 
-  public NonNullTracker() {
+  private final AppInfo appInfo;
+
+  public NonNullTracker(AppInfo appInfo) {
+    this.appInfo = appInfo;
   }
 
   @VisibleForTesting
@@ -260,6 +265,8 @@
   }
 
   public void cleanupNonNull(IRCode code) {
+    Set<Value> affectedValues = Sets.newIdentityHashSet();
+
     InstructionIterator it = code.instructionIterator();
     boolean needToCheckTrivialPhis = false;
     while (it.hasNext()) {
@@ -275,6 +282,16 @@
         NonNull nonNull = instruction.asNonNull();
         Value src = nonNull.src();
         Value dest = nonNull.dest();
+
+        // Add all values whose definition may depend on `dest` to `affectedValues`.
+        for (Instruction user : dest.uniqueUsers()) {
+          if (user.outValue() != null) {
+            affectedValues.add(user.outValue());
+          }
+        }
+        affectedValues.addAll(dest.uniquePhiUsers());
+
+        // Replace `dest` by `src`.
         needToCheckTrivialPhis = needToCheckTrivialPhis || dest.uniquePhiUsers().size() != 0;
         dest.replaceUsers(src);
         it.remove();
@@ -290,6 +307,17 @@
     if (needToCheckTrivialPhis) {
       code.removeAllTrivialPhis();
     }
+
+    // We need to update the types of all values whose definitions depend on a non-null value.
+    // This is needed to preserve soundness of the types after the NonNull instructions have been
+    // removed.
+    //
+    // As an example, consider a check-cast instruction on the form "z = (T) y". If y used to be
+    // defined by a NonNull instruction, then the type analysis could have used this information
+    // to mark z as non-null. However, cleanupNonNull() have now replaced y by a nullable value x.
+    // Since z is defined as "z = (T) x", and x is nullable, it is no longer sound to have that z
+    // is not nullable. This is fixed by rerunning the type analysis for the affected values.
+    new TypeAnalysis(appInfo, code.method).widening(affectedValues);
   }
 
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 3ae9174..0400670 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.KeyedDexItem;
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.logging.Log;
@@ -98,12 +99,18 @@
         clazz.setVirtualMethods(reachableMethods(clazz.virtualMethods(), clazz));
         clazz.setInstanceFields(reachableFields(clazz.instanceFields()));
         clazz.setStaticFields(reachableFields(clazz.staticFields()));
+        clazz.removeInnerClasses(this::isAttributeReferencingPrunedType);
         usagePrinter.visited();
       }
     }
     return newClasses;
   }
 
+  private boolean isAttributeReferencingPrunedType(InnerClassAttribute attr) {
+    return !appInfo.liveTypes.contains(attr.getInner())
+        || !appInfo.liveTypes.contains(attr.getOuter());
+  }
+
   private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
       T[] items, Predicate<S> live) {
     for (int i = 0; i < items.length; i++) {
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 17e49a5..99222d0 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -313,6 +313,12 @@
     return new JUnit3Wrapper.Command.BreakpointCommand(className, methodName, methodSignature, line);
   }
 
+  protected final JUnit3Wrapper.Command breakOnException(String className, String methodName,
+      boolean caught, boolean uncaught) {
+    return new JUnit3Wrapper.Command.BreakOnExceptionCommand(
+        className, methodName, caught, uncaught);
+  }
+
   protected final JUnit3Wrapper.Command stepOver() {
     return stepOver(DEFAULT_FILTER);
   }
@@ -1607,6 +1613,39 @@
         }
       }
 
+      // Break on exceptions thrown in className.methodName.
+      class BreakOnExceptionCommand implements Command {
+        private static final int ALL_EXCEPTIONS = 0;
+        private final String className;
+        private final String methodName;
+        private final boolean caught;
+        private final boolean uncaught;
+
+        public BreakOnExceptionCommand(
+            String className, String methodName, boolean caught, boolean uncaught) {
+          this.className = className;
+          this.methodName = methodName;
+          this.caught = caught;
+          this.uncaught = uncaught;
+        }
+
+        @Override
+        public void perform(JUnit3Wrapper testBase) {
+          ReplyPacket replyPacket =
+              testBase.getMirror().setException(ALL_EXCEPTIONS, caught, uncaught);
+          assert replyPacket.getErrorCode() == Error.NONE;
+          int breakpointId = replyPacket.getNextValueAsInt();
+          testBase.events.put(
+              Integer.valueOf(breakpointId),
+              new BreakOnExceptionHandler(className, methodName));
+        }
+
+        @Override
+        public String toString() {
+          return "breakOnException";
+        }
+      }
+
       class BreakpointCommand implements Command {
 
         private final String className;
@@ -1813,6 +1852,29 @@
       }
     }
 
+    private static class BreakOnExceptionHandler extends DefaultEventHandler {
+      private final String className;
+      private final String methodName;
+
+      BreakOnExceptionHandler(String className, String methodName) {
+        this.className = className;
+        this.methodName = methodName;
+      }
+
+      @Override
+      public void handle(JUnit3Wrapper testBase) {
+        boolean inMethod =
+            testBase.getDebuggeeState().getTopFrame().getMethodName().equals(methodName);
+        boolean inClass =
+            testBase.getDebuggeeState().getTopFrame().getClassName().equals(className);
+        if (!(inClass && inMethod)) {
+          // Not the right place, continue until the next exception.
+          testBase.enqueueCommandFirst(new JUnit3Wrapper.Command.RunCommand());
+        }
+        testBase.setState(State.ProcessCommand);
+      }
+    }
+
     private static class StepEventHandler extends DefaultEventHandler {
 
       private final JUnit3Wrapper.Command.StepCommand stepCommand;
diff --git a/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThis.java b/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThis.java
new file mode 100644
index 0000000..0b26f37
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThis.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.debug;
+
+public class DoNotCrashOnAccessToThis {
+  private String[] array = new String[] { "asdf" };
+
+  public int length() {
+    return array.length;
+  }
+
+  public void mightClobberThis(int i) {
+    // Make a local copy of the receiver so that we can let the receiver be clobbered in
+    // release mode.
+    String[] localArray = array;
+    // Keep receiver alive so that localArray is not what is clobbering the receiver register.
+    // Attempt to get an integer into the receiver register.
+    int index = length();
+    String s = localArray[index + i];
+  }
+
+  public static void main(String[] args) {
+    DoNotCrashOnAccessToThis instance = new DoNotCrashOnAccessToThis();
+    for (int i = 0; i < 100000; i++) {
+      instance.mightClobberThis(-1);
+    }
+    try {
+      instance.mightClobberThis(0);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      System.out.println("Caught ArrayIndexOutOfBoundsException");
+    }
+
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java b/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java
new file mode 100644
index 0000000..3e562cf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DoNotCrashOnAccessToThisRunner.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DoNotCrashOnAccessToThisRunner extends DebugTestBase {
+
+  private static final Class CLASS = DoNotCrashOnAccessToThis.class;
+  private static final String FILE = CLASS.getSimpleName() + ".java";
+  private static final String NAME = CLASS.getCanonicalName();
+
+  private final DebugTestConfig config;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Collection<Object[]> setup() {
+    DelayedDebugTestConfig cf =
+        temp -> new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
+    DelayedDebugTestConfig d8 =
+        temp -> new D8DebugTestConfig().compileAndAdd(
+            temp,
+            ImmutableList.of(ToolHelper.getClassFileForTestClass(CLASS)),
+            options -> {
+              // Release mode so receiver can be clobbered.
+              options.debug = false;
+              // Api level M so that the workarounds for Lollipop verifier doesn't
+              // block the receiver register. We want to check b/116683601 which
+              // happens on at least 7.0.0.
+              options.minApiLevel = AndroidApiLevel.M.getLevel();
+            });
+    return ImmutableList.of(new Object[]{"CF", cf}, new Object[]{"D8", d8});
+  }
+
+  public DoNotCrashOnAccessToThisRunner(String name, DelayedDebugTestConfig config) {
+    this.config = config.getConfig(temp);
+  }
+
+  @Test
+  public void doNotCrash() throws Throwable {
+    Assume.assumeFalse(ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_6_0_1_HOST));
+    runDebugTest(
+        config,
+        NAME,
+        breakOnException(NAME, "mightClobberThis", true, false),
+        run(),
+        checkLine(FILE, 21),
+        checkLocal("this"),
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index 2ec38b4..0607b0e 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -21,7 +21,6 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -48,6 +47,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 import org.junit.ComparisonFailure;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
@@ -61,12 +61,11 @@
       CompilerUnderTest compiler,
       CompilationMode mode,
       String referenceApk,
-      String pgConf,
+      List<String> pgConfs,
       String input)
-      throws ExecutionException, IOException, ProguardRuleParserException,
-          CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     return runAndCheckVerification(
-        compiler, mode, referenceApk, pgConf, null, Collections.singletonList(input));
+        compiler, mode, referenceApk, pgConfs, null, Collections.singletonList(input));
   }
 
   public AndroidApp runAndCheckVerification(D8Command.Builder builder, String referenceApk)
@@ -82,18 +81,18 @@
       CompilerUnderTest compiler,
       CompilationMode mode,
       String referenceApk,
-      String pgConf,
+      List<String> pgConfs,
       Consumer<InternalOptions> optionsConsumer,
       List<String> inputs)
-      throws ExecutionException, IOException, ProguardRuleParserException,
-      CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     assertTrue(referenceApk == null || new File(referenceApk).exists());
     AndroidAppConsumers outputApp;
     if (compiler == CompilerUnderTest.R8) {
       R8Command.Builder builder = R8Command.builder();
       builder.addProgramFiles(ListUtils.map(inputs, Paths::get));
-      if (pgConf != null) {
-        builder.addProguardConfigurationFiles(Paths.get(pgConf));
+      if (pgConfs != null) {
+        builder.addProguardConfigurationFiles(
+            pgConfs.stream().map(Paths::get).collect(Collectors.toList()));
       }
       builder.setMode(mode);
       builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
diff --git a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
index 390105e..4094bb7 100644
--- a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
@@ -16,8 +15,7 @@
       "third_party/photos/2017-06-06/PhotosEnglishOnlyLegacy_proguard.jar";
 
   public void runD8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException,
-      CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     runAndCheckVerification(CompilerUnderTest.D8, mode, version, null, version);
   }
 
diff --git a/src/test/java/com/android/tools/r8/internal/NestCompilationBase.java b/src/test/java/com/android/tools/r8/internal/NestCompilationBase.java
new file mode 100644
index 0000000..3328d0f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/NestCompilationBase.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal;
+
+public abstract class NestCompilationBase extends CompilationTestBase {
+  static final String BASE = "third_party/nest/nest_20180926_7c6cfb/";
+  static final String DEPLOY_JAR = "obsidian-development-debug.jar";
+  static final String PG_CONF = "proguard/proguard.cfg";
+  static final String PG_CONF_NO_OPT = "proguard/proguard-no-optimizations.cfg";
+}
diff --git a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..ed7a5d8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassHierarchyVerifier;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NestTreeShakeJarVerificationTest extends NestCompilationBase {
+
+  @Test
+  public void buildAndTreeShakeFromDeployJar() throws Exception {
+    AndroidApp app =
+        runAndCheckVerification(
+            CompilerUnderTest.R8,
+            CompilationMode.RELEASE,
+            null,
+            ImmutableList.of(BASE + PG_CONF, BASE + PG_CONF_NO_OPT),
+            null,
+            ImmutableList.of(BASE + DEPLOY_JAR));
+
+
+    // Check that all non-abstract classes implement the abstract methods from their super types.
+    // This is a sanity check for the tree shaking and minification.
+    try {
+      CodeInspector inspector = new CodeInspector(app);
+      new ClassHierarchyVerifier(inspector, false).run();
+    } catch (AssertionError error) {
+      // TODO(b/115705526): Remove try-catch when bug is fixed.
+      assertThat(error, hasMessage(containsString("Non-abstract class")));
+      assertThat(error, hasMessage(containsString("must implement method")));
+      return;
+    }
+
+    Assert.fail("Unreachable");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
index 2da83a6..bbfd381 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
@@ -21,15 +21,16 @@
       int maxSize,
       Consumer<InternalOptions> optionsConsumer)
       throws Exception {
-    AndroidApp app = runAndCheckVerification(
-        CompilerUnderTest.R8,
-        mode,
-        hasReference ? base + REFERENCE_APK : null,
-        base + PG_CONF,
-        optionsConsumer,
-        // Don't pass any inputs. The input will be read from the -injars in the Proguard
-        // configuration file.
-        ImmutableList.of());
+    AndroidApp app =
+        runAndCheckVerification(
+            CompilerUnderTest.R8,
+            mode,
+            hasReference ? base + REFERENCE_APK : null,
+            ImmutableList.of(base + PG_CONF),
+            optionsConsumer,
+            // Don't pass any inputs. The input will be read from the -injars in the Proguard
+            // configuration file.
+            ImmutableList.of());
     int bytes = applicationSize(app);
     assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
     return app;
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
index 4fa8497..20b1930 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -27,7 +27,7 @@
             CompilerUnderTest.R8,
             CompilationMode.RELEASE,
             BASE + APK,
-            BASE + PG_CONF,
+            ImmutableList.of(BASE + PG_CONF),
             options -> options.proguardMapConsumer = new FileConsumer(proguardMapPath),
             // Don't pass any inputs. The input will be read from the -injars in the Proguard
             // configuration file.
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 0ea8731..c618dad 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -50,7 +50,7 @@
     DexEncodedMethod foo = codeInspector.clazz(mainClass.getName()).method(signature).getMethod();
     IRCode irCode =
         foo.buildIR(appInfo, GraphLense.getIdentityLense(), TEST_OPTIONS, Origin.unknown());
-    NonNullTracker nonNullTracker = new NonNullTracker();
+    NonNullTracker nonNullTracker = new NonNullTracker(appInfo);
     nonNullTracker.addNonNull(irCode);
     TypeAnalysis analysis = new TypeAnalysis(appInfo, foo);
     analysis.widening(foo, irCode);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 058f017..564a81c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -41,7 +41,7 @@
         foo.buildIR(appInfo, GraphLense.getIdentityLense(), TEST_OPTIONS, Origin.unknown());
     checkCountOfNonNull(irCode, 0);
 
-    NonNullTracker nonNullTracker = new NonNullTracker();
+    NonNullTracker nonNullTracker = new NonNullTracker(appInfo);
 
     nonNullTracker.addNonNull(irCode);
     assertTrue(irCode.isConsistentSSA());
diff --git a/third_party/nest/nest_20180926_7c6cfb.tar.gz.sha1 b/third_party/nest/nest_20180926_7c6cfb.tar.gz.sha1
new file mode 100644
index 0000000..e10bb9e
--- /dev/null
+++ b/third_party/nest/nest_20180926_7c6cfb.tar.gz.sha1
@@ -0,0 +1 @@
+c948f5b97ca768f6190c3e7e1dbfe9044a743839
\ No newline at end of file
diff --git a/tools/nest_data.py b/tools/nest_data.py
new file mode 100644
index 0000000..af3e35a
--- /dev/null
+++ b/tools/nest_data.py
@@ -0,0 +1,36 @@
+# Copyright (c) 2017, 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.
+
+import os
+import utils
+
+THIRD_PARTY = os.path.join(utils.REPO_ROOT, 'third_party')
+ANDROID_L_API = '21'
+BASE = os.path.join(THIRD_PARTY, 'nest')
+
+V20180926_BASE = os.path.join(BASE, 'nest_20180926_7c6cfb')
+
+# NOTE: we always use android.jar for SDK v25, later we might want to revise it
+#       to use proper android.jar version for each of youtube version separately.
+ANDROID_JAR = os.path.join(THIRD_PARTY, 'android_jar', 'lib-v25', 'android.jar')
+
+VERSIONS = {
+  '20180926': {
+    'dex' : {
+      'inputs': [os.path.join(V20180926_BASE, 'obsidian-development-debug.apk')],
+      'libraries' : [ANDROID_JAR],
+      'min-api' : ANDROID_L_API,
+    },
+    'deploy' : {
+      'inputs': [os.path.join(V20180926_BASE, 'obsidian-development-debug.jar')],
+      'libraries' : [ANDROID_JAR],
+      'pgconf': [
+          os.path.join(V20180926_BASE, 'proguard', 'proguard.cfg'),
+          os.path.join(V20180926_BASE, 'proguard', 'proguard-no-optimizations.cfg'),
+          os.path.join(V20180926_BASE, 'proguard', 'proguard-ignore-warnings.cfg')],
+      # Build for native multi dex
+      'min-api' : ANDROID_L_API,
+    }
+  },
+}
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 712549d..ae64322 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -14,13 +14,14 @@
 import gmail_data
 import gmscore_data
 import golem
+import nest_data
 import toolhelper
 import utils
 import youtube_data
 import chrome_data
 
 TYPES = ['dex', 'deploy', 'proguarded']
-APPS = ['gmscore', 'youtube', 'gmail', 'chrome']
+APPS = ['gmscore', 'nest', 'youtube', 'gmail', 'chrome']
 COMPILERS = ['d8', 'r8']
 
 def ParseOptions(argv):
@@ -118,12 +119,13 @@
 # Please add bug number for disabled permutations and please explicitly
 # do Bug: #BUG in the commit message of disabling to ensure re-enabling
 DISABLED_PERMUTATIONS = [
-    ('gmail', '180826.15', 'proguarded'), # b/116840276
+  # (app, version, type), e.g., ('gmail', '180826.15', 'deploy'),
 ]
 
 def get_permutations():
   data_providers = {
       'gmscore': gmscore_data,
+      'nest': nest_data,
       'youtube': youtube_data,
       'chrome': chrome_data,
       'gmail': gmail_data
@@ -171,6 +173,9 @@
   if options.app == 'gmscore':
     options.version = options.version or 'v9'
     data = gmscore_data
+  elif options.app == 'nest':
+    options.version = options.version or '20180926'
+    data = nest_data
   elif options.app == 'youtube':
     options.version = options.version or '12.22'
     data = youtube_data
@@ -206,10 +211,12 @@
   values = version[options.type]
   inputs = None
   # For R8 'deploy' the JAR is located using the Proguard configuration
-  # -injars option. For chrome we don't have the injars in the proguard files.
+  # -injars option. For chrome and nest we don't have the injars in the
+  # proguard files.
   if 'inputs' in values and (options.compiler != 'r8'
                              or options.type != 'deploy'
-                             or options.app == 'chrome'):
+                             or options.app == 'chrome'
+                             or options.app == 'nest'):
     inputs = values['inputs']
 
   args.extend(['--output', outdir])