Merge "Reproduce IllegalAccessError due to a chain of inlinings (b/111080693)."
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/B111080693.java b/src/test/java/com/android/tools/r8/regress/b111080693/B111080693.java
new file mode 100644
index 0000000..d424c39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/B111080693.java
@@ -0,0 +1,51 @@
+// 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.regress.b111080693;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.regress.b111080693.a.Observable;
+import com.android.tools.r8.regress.b111080693.b.RecyclerView;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class B111080693 extends TestBase {
+ @Ignore("b/111080693")
+ @Test
+ public void test() throws Exception {
+ R8Command.Builder builder = R8Command.builder();
+ builder.addProgramFiles(
+ ToolHelper.getClassFilesForTestPackage(Observable.class.getPackage()));
+ builder.addProgramFiles(
+ ToolHelper.getClassFilesForTestPackage(RecyclerView.class.getPackage()));
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestMain.class));
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestMain.TestAdapter.class));
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+ String config = keepMainProguardConfiguration(TestMain.class);
+ builder.addProguardConfiguration(
+ ImmutableList.of(config,
+ "-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*"),
+ Origin.unknown());
+ AndroidApp app = ToolHelper.runR8(builder.build());
+ ProcessResult result = runOnArtRaw(app, TestMain.class);
+ assertEquals(0, result.exitCode);
+ assertThat(result.stderr, not(containsString("IllegalAccessError")));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/TestMain.java b/src/test/java/com/android/tools/r8/regress/b111080693/TestMain.java
new file mode 100644
index 0000000..8979250
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/TestMain.java
@@ -0,0 +1,22 @@
+// 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.regress.b111080693;
+
+import com.android.tools.r8.regress.b111080693.b.RecyclerView;
+import com.android.tools.r8.regress.b111080693.b.RecyclerView.Adapter;
+
+public class TestMain {
+ static final class TestAdapter extends Adapter {
+ TestAdapter() {
+ }
+ }
+
+ public static void main(String[] args) {
+ TestAdapter adapter = new TestAdapter();
+ RecyclerView view = new RecyclerView();
+ view.setAdapter(adapter);
+ adapter.notifyDataSetChanged();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/a/Observable.java b/src/test/java/com/android/tools/r8/regress/b111080693/a/Observable.java
new file mode 100644
index 0000000..c7a4858
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/a/Observable.java
@@ -0,0 +1,15 @@
+// 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.regress.b111080693.a;
+
+import java.util.ArrayList;
+
+public abstract class Observable<T> {
+ /**
+ * The list of observers. An observer can be in the list at most
+ * once and will never be null.
+ */
+ protected final ArrayList<T> mObservers = new ArrayList<T>();
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/b/RecyclerView.java b/src/test/java/com/android/tools/r8/regress/b111080693/b/RecyclerView.java
new file mode 100644
index 0000000..06347fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/b/RecyclerView.java
@@ -0,0 +1,78 @@
+// 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.regress.b111080693.b;
+
+import com.android.tools.r8.regress.b111080693.a.Observable;
+
+public class RecyclerView {
+
+ final State mState = new State();
+
+ public static class State {
+ boolean mStructureChanged = false;
+ }
+
+ public abstract static class AdapterDataObserver {
+ public void onChanged() {
+ // Do nothing
+ }
+ }
+
+ private class RecyclerViewDataObserver extends AdapterDataObserver {
+ RecyclerViewDataObserver() {
+ }
+
+ @Override
+ public void onChanged() {
+ // This is the single target of AdapterDataObserver#onChanged(), and could be inlined to
+ // AdapterDataObservable#notifyChanged() as long as this preserves null check of the receiver.
+ // To do so, access the enclosing class' member to use the receiver.
+ mState.mStructureChanged = true;
+ }
+ }
+
+ static class AdapterDataObservable extends Observable<AdapterDataObserver> {
+ public void registerObserver(AdapterDataObserver observer) {
+ mObservers.add(observer);
+ }
+ public void notifyChanged() {
+ for (int i = mObservers.size() - 1; i >= 0; i--) {
+ // The single target, RecyclerViewDataObserver#onChange is inlined, along with check-cast:
+ // AdapterDataObserver observer_i = mObservers.get(i);
+ // RecyclerViewDataObserver casted_obs = (RecyclerViewDataObserver) observer_i;
+ // // inlining RecyclerViewDataObserver#onChanged():
+ mObservers.get(i).onChanged();
+ }
+ }
+ }
+
+ public abstract static class Adapter {
+ private final AdapterDataObservable mObservable = new AdapterDataObservable();
+
+ public void registerAdapterDataObserver(AdapterDataObserver observer) {
+ mObservable.registerObserver(observer);
+ }
+
+ public final void notifyDataSetChanged() {
+ // Single callee, AdapterDataObservable#notifyChanged(), could be inlined, but should not.
+ // Accessing AdapterDataObservable.mObservers, which is a protected field in Observable,
+ // results in an illegal access error.
+ //
+ // Without the above inlining, the inlining constraint for the target here is SUBCLASS due to
+ // that protected field, and thus decline to inline because the holder, Adapter, is not a
+ // subtype of the target holder, AdapterDataObservable.
+ // However, after the above inlining, check-cast to RecyclerViewDataObserver overrides that
+ // condition to PACKAGE, which accidentally allows the target to be inlined here.
+ mObservable.notifyChanged();
+ }
+ }
+
+ private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
+
+ public void setAdapter(Adapter adapter) {
+ adapter.registerAdapterDataObserver(mObserver);
+ }
+
+}