Fix dex cache resolved types and class table mismatch.

Record class table in ClassLinker::DexCacheData and use
it in DexCache.setResolvedType() to store the type also
in the initiating loader's class table if the dex file
has been registered.

Also throw InternalError when trying to register the
same DexFile with multiple class loaders. (Different
DexFile instances referencing the same file are OK.)

Test: 155-java-set-resolved-type
Test: m test-art-host
Bug: 30627598
Bug: 34193123
Bug: 34839984
Change-Id: Ia48acb300337c45880ea1459d2d32789546d67f4
diff --git a/test/155-java-set-resolved-type/expected.txt b/test/155-java-set-resolved-type/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/155-java-set-resolved-type/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/155-java-set-resolved-type/info.txt b/test/155-java-set-resolved-type/info.txt
new file mode 100644
index 0000000..ba5bc0a
--- /dev/null
+++ b/test/155-java-set-resolved-type/info.txt
@@ -0,0 +1,2 @@
+Regression test for Java call to DexCache.setResolvedType() storing the
+type in the dex cache while it was not in the class loader's class table.
diff --git a/test/155-java-set-resolved-type/src-ex/TestInterface.java b/test/155-java-set-resolved-type/src-ex/TestInterface.java
new file mode 100644
index 0000000..037c760
--- /dev/null
+++ b/test/155-java-set-resolved-type/src-ex/TestInterface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface TestInterface {
+  public void foo();
+}
diff --git a/test/155-java-set-resolved-type/src/Main.java b/test/155-java-set-resolved-type/src/Main.java
new file mode 100644
index 0000000..f92363e
--- /dev/null
+++ b/test/155-java-set-resolved-type/src/Main.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class Main {
+    public static String TEST_NAME = "155-java-set-resolved-type";
+
+    public static void main(String[] args) {
+        try {
+            Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+            System.loadLibrary(args[0]);
+        } catch (ClassNotFoundException e) {
+            usingRI = true;
+            // Add expected JNI_OnLoad log line to match expected.txt.
+            System.out.println("JNI_OnLoad called");
+        }
+        try {
+            String dex_location = System.getenv("DEX_LOCATION");
+            ClassLoader systemLoader = ClassLoader.getSystemClassLoader().getParent();
+            ClassLoader exLoader = getClassLoaderFor(dex_location, systemLoader, /* ex */ true);
+            ClassLoader mainLoader = getClassLoaderFor(dex_location, exLoader, /* ex */ false);
+
+            // Resolve TestParameter class. It shall be defined by mainLoader.
+            // This does not resolve method parameter types.
+            Class<?> tpc = Class.forName("TestParameter", false, mainLoader);
+            // Get declared methods of TestParameter.
+            // This still does not resolve method parameter types.
+            Method[] ms = tpc.getDeclaredMethods();
+            if (ms == null || ms.length != 1) { throw new Error("Unexpected methods"); };
+            // Call getParameterTypes() to resolve parameter types. The parameter type
+            // TestInterface shall be defined by the exLoader. This used to store the
+            // TestInterface class in the dex cache resolved types for the mainLoader
+            // but not in the mainLoader's class table. This discrepancy used to cause
+            // a crash further down.
+            ms[0].getParameterTypes();
+
+            // Resolve but do not initialize TestImplementation. During the resolution,
+            // we see the TestInterface in the dex cache, so we do not try to look it up
+            // or resolve it using the mainLoader.
+            Class<?> timpl = Class.forName("TestImplementation", false, mainLoader);
+            // Clear the dex cache resolved types to force a proper lookup the next time
+            // we need to find TestInterface.
+            // TODO: Enable clearing the dex cache when we switch to the hash-based type array
+            // and do a proper lookup. Currently, ClassLinker fully relies on the DexCache.
+            if (false) {
+                clearResolvedTypes(timpl);
+            }
+
+            // Force intialization of TestClass2. This expects the interface type to be
+            // resolved and found through simple lookup.
+            timpl.newInstance();
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    public static ClassLoader getClassLoaderFor(String location, ClassLoader parent, boolean ex)
+            throws Exception {
+        try {
+            Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+            Constructor<?> ctor =
+                    class_loader_class.getConstructor(String.class, ClassLoader.class);
+            /* on Dalvik, this is a DexFile; otherwise, it's null */
+            String path = location + "/" + TEST_NAME + (ex ? "-ex.jar" : ".jar");
+            return (ClassLoader)ctor.newInstance(path, parent);
+        } catch (ClassNotFoundException e) {
+            // Running on RI. Use URLClassLoader.
+            String url = "file://" + location + (ex ? "/classes-ex/" : "/classes/");
+            return new java.net.URLClassLoader(
+                    new java.net.URL[] { new java.net.URL(url) }, parent);
+        }
+    }
+
+    public static void clearResolvedTypes(Class<?> c) {
+        if (!usingRI) {
+            nativeClearResolvedTypes(c);
+        }
+    }
+
+    private static boolean usingRI = false;
+
+    public static native void nativeClearResolvedTypes(Class<?> c);
+}
diff --git a/test/155-java-set-resolved-type/src/TestImplementation.java b/test/155-java-set-resolved-type/src/TestImplementation.java
new file mode 100644
index 0000000..4a3e74d
--- /dev/null
+++ b/test/155-java-set-resolved-type/src/TestImplementation.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class TestImplementation implements TestInterface {
+  public void foo() { }
+}
diff --git a/test/155-java-set-resolved-type/src/TestInterface.java b/test/155-java-set-resolved-type/src/TestInterface.java
new file mode 100644
index 0000000..037c760
--- /dev/null
+++ b/test/155-java-set-resolved-type/src/TestInterface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface TestInterface {
+  public void foo();
+}
diff --git a/test/155-java-set-resolved-type/src/TestParameter.java b/test/155-java-set-resolved-type/src/TestParameter.java
new file mode 100644
index 0000000..c881f3f
--- /dev/null
+++ b/test/155-java-set-resolved-type/src/TestParameter.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class TestParameter {
+  public void bar(TestInterface ti) { }
+}
diff --git a/test/156-register-dex-file-multi-loader/expected.txt b/test/156-register-dex-file-multi-loader/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/156-register-dex-file-multi-loader/expected.txt
diff --git a/test/156-register-dex-file-multi-loader/info.txt b/test/156-register-dex-file-multi-loader/info.txt
new file mode 100644
index 0000000..49d153c
--- /dev/null
+++ b/test/156-register-dex-file-multi-loader/info.txt
@@ -0,0 +1,2 @@
+Regression test to check that we do not allow registering the same dex file
+with multiple class loaders.
diff --git a/test/156-register-dex-file-multi-loader/src/Main.java b/test/156-register-dex-file-multi-loader/src/Main.java
new file mode 100644
index 0000000..ff5a2bd
--- /dev/null
+++ b/test/156-register-dex-file-multi-loader/src/Main.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+class MyClassLoader extends ClassLoader {
+  MyClassLoader() throws Exception {
+    super(MyClassLoader.class.getClassLoader());
+
+    // Some magic to get access to the pathList field of BaseDexClassLoader.
+    ClassLoader loader = getClass().getClassLoader();
+    Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+    Field f = baseDexClassLoader.getDeclaredField("pathList");
+    f.setAccessible(true);
+    Object pathList = f.get(loader);
+
+    // Some magic to get access to the dexField field of pathList.
+    f = pathList.getClass().getDeclaredField("dexElements");
+    f.setAccessible(true);
+    dexElements = (Object[]) f.get(pathList);
+    dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
+    dexFileField.setAccessible(true);
+  }
+
+  Object[] dexElements;
+  Field dexFileField;
+
+  protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+    // Mimic what DexPathList.findClass is doing.
+    try {
+      for (Object element : dexElements) {
+        Object dex = dexFileField.get(element);
+        Method method = dex.getClass().getDeclaredMethod(
+            "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+        if (dex != null) {
+          Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null);
+          if (clazz != null) {
+            return clazz;
+          }
+        }
+      }
+    } catch (InvocationTargetException ite) {
+      throw new ClassNotFoundException(className, ite.getCause());
+    } catch (Exception e) {
+      throw new Error(e);
+    }
+    return getParent().loadClass(className);
+  }
+}
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    MyClassLoader o = new MyClassLoader();
+    try {
+      Class<?> foo = o.loadClass("Main");
+      throw new Error("Unreachable");
+    } catch (ClassNotFoundException cnfe) {
+      boolean unexpected = false;
+      if (!(cnfe.getCause() instanceof InternalError)) {
+        unexpected = true;
+      } else {
+        String message = cnfe.getCause().getMessage();
+        unexpected = !message.startsWith("Attempt to register dex file ") ||
+                     !message.endsWith(" with multiple class loaders");
+      }
+      if (unexpected) {
+        cnfe.getCause().printStackTrace();
+      }
+    }
+  }
+}
diff --git a/test/626-const-class-linking/src/Main.java b/test/626-const-class-linking/src/Main.java
index 0029428..1bc94a7 100644
--- a/test/626-const-class-linking/src/Main.java
+++ b/test/626-const-class-linking/src/Main.java
@@ -23,8 +23,10 @@
 public class Main {
     public static void main(String[] args) throws Exception {
         try {
+            // Check if we're running dalvik or RI.
+            Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
             System.loadLibrary(args[0]);
-        } catch (UnsatisfiedLinkError ule) {
+        } catch (ClassNotFoundException e) {
             usingRI = true;
             // Add expected JNI_OnLoad log line to match expected.txt.
             System.out.println("JNI_OnLoad called");