changeset 14050:2c3e092e1b5d

More test cases; bugfixes for mismatches where native VM and interpreter had different view of a loaded class. Two failing test cases.
author briangoetz
date Thu, 09 Jun 2016 14:51:25 -0400
parents 786c857536a9
children 01f5c9ebab50
files interpreter/src/valhalla/interpreter/Interpreter.java interpreter/src/valhalla/interpreter/ProxyClassBuilder.java interpreter/src/valhalla/interpreter/StandardClassModel.java interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1.java interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1a.java interpreter/test/valhalla/interpreter/InterpreterTest.java
diffstat 6 files changed, 114 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/interpreter/src/valhalla/interpreter/Interpreter.java	Thu Jun 09 12:44:49 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/Interpreter.java	Thu Jun 09 14:51:25 2016 -0400
@@ -237,7 +237,7 @@
         MethodHandle target = (selected != null) ? selected : resolved;
 
         if (interpretable) {
-            ClassModel cm = systemDictionary.get(mhi.getDeclaringClass().getName());
+            ClassModel cm = tryLoad(mhi.getDeclaringClass().getName());
             if (cm != null) {
                 logEvent(InterpreterEvent.interpret(mhi.getDeclaringClass().getName(),
                                                     mhi.getName(),
@@ -351,6 +351,11 @@
     }
 
     protected Class<?> toClass(Frame f, String name) throws InterpreterError {
+        // If the class is (or can be made to be) known to the interpreter, use the proxy class
+        ClassModel cm = tryLoad(name);
+        if (cm != null)
+            return cm.getRepresentationClass();
+
         // FIXME: class name must be access-checked relative to current frame class
         // ...doing it neatly requires a new API point Lookup.findClass
         try {
@@ -559,14 +564,17 @@
 
     // Public interpreter API
 
-    public ClassModel resolveClass(String binaryName) {
+    public ClassModel tryLoad(String binaryName) {
         String humanName = binaryName.replace('/', '.');
         ClassModel model = systemDictionary.get(humanName);
         if (model != null)
             return model;
 
+        Resource r = classLoader.ucp.getResource(binaryName + ".class");
+        if (r == null)
+            return null;
+
         try {
-            Resource r = classLoader.ucp.getResource(binaryName + ".class");
             ClassModel classModel = newClassModel(binaryName, r.getBytes());
             systemDictionary.put(humanName, classModel);
             logEvent(InterpreterEvent.load(humanName));
@@ -579,10 +587,18 @@
             return classModel;
         }
         catch (IOException e) {
-            throw new InterpreterError("Cannot resolve class " + binaryName, e);
+            return null;
         }
     }
 
+    public ClassModel resolveClass(String binaryName) {
+        ClassModel model = tryLoad(binaryName);
+        if (model != null)
+            return model;
+
+        throw new InterpreterError("Cannot resolve class " + binaryName);
+    }
+
     public String addBootclass(Class c) {
         String name = c.getName();
         systemDictionary.put(name, StandardClassModel.interpreted(this, c));
--- a/interpreter/src/valhalla/interpreter/ProxyClassBuilder.java	Thu Jun 09 12:44:49 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/ProxyClassBuilder.java	Thu Jun 09 14:51:25 2016 -0400
@@ -24,6 +24,11 @@
  */
 package valhalla.interpreter;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.org.objectweb.asm.Opcodes;
@@ -62,12 +67,16 @@
     public final String className;
     private final ClassWriter cw;
     private final Interpreter interpreter;
+    private final List<String> loadDependencies = new ArrayList<>();
 
     private ProxyClassBuilder(Interpreter interpreter, int access, String className, String superClass, String[] interfaces) {
         this.interpreter = interpreter;
         this.className = className;
         this.cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);
         cw.visit(52, access, className, null, superClass, interfaces);
+        loadDependencies.add(superClass);
+        if (interfaces != null)
+            Collections.addAll(loadDependencies, interfaces);
     }
 
     public static ProxyClassBuilder make(Interpreter interpreter, int access, String name, String superClass, String[] interfaces) {
@@ -127,6 +136,10 @@
         cw.visitEnd();
         byte[] bytes = cw.toByteArray();
 //        new ClassReader(bytes).accept(new TraceClassVisitor(new PrintWriter(System.out)), 0);
+        for (String s : loadDependencies) {
+            ClassModel model = interpreter.tryLoad(s);
+            System.out.printf("Trying to load %s ... %s%n", s, model == null ? "failed" : "success");
+        }
         return InternalHelpers.loadClass(interpreter, className, bytes);
     }
 }
--- a/interpreter/src/valhalla/interpreter/StandardClassModel.java	Thu Jun 09 12:44:49 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/StandardClassModel.java	Thu Jun 09 14:51:25 2016 -0400
@@ -61,7 +61,6 @@
     private final ConstantPool constantPool;
     private final BootstrapMethods_attribute bootstrapMethods;
     private final MethodModel[] methods;
-    private ProxyClassBuilder proxyClassBuilder;
     private Class<?> representationClass;
 
     private StandardClassModel(Interpreter interpreter, String binaryName, byte[] bytes) {
@@ -81,21 +80,22 @@
     public static ClassModel interpreted(Interpreter interpreter, String binaryName, byte[] bytes) {
         StandardClassModel model = new StandardClassModel(interpreter, binaryName, bytes);
         ClassFile classFile = model.classFile;
-        model.proxyClassBuilder = ProxyClassBuilder.make(interpreter,
-                                                         classFile.access_flags.flags,
-                                                         launder(classFile::getName),
-                                                         launder(classFile::getSuperclassName),
-                                                         IntStream.range(0, classFile.interfaces.length)
-                                                                  .mapToObj(i -> launder(() -> classFile.getInterfaceName(i)))
-                                                                  .toArray(String[]::new));
+        ProxyClassBuilder builder = ProxyClassBuilder.make(interpreter,
+                                                           classFile.access_flags.flags,
+                                                           launder(classFile::getName),
+                                                           launder(classFile::getSuperclassName),
+                                                           IntStream.range(0, classFile.interfaces.length)
+                                                                    .mapToObj(i -> launder(() -> classFile.getInterfaceName(i)))
+                                                                    .toArray(String[]::new));
         Stream.of(classFile.fields)
-              .forEach(f -> model.proxyClassBuilder.addField(f.access_flags.flags,
-                                                             launder(() -> f.getName(classFile.constant_pool)),
-                                                             launder(() -> f.descriptor.getValue(classFile.constant_pool))));
+              .forEach(f -> builder.addField(f.access_flags.flags,
+                                             launder(() -> f.getName(classFile.constant_pool)),
+                                             launder(() -> f.descriptor.getValue(classFile.constant_pool))));
         Stream.of(classFile.methods)
-              .forEach(m -> model.proxyClassBuilder.addMethod(m.access_flags.flags,
-                                                              launder(() -> m.getName(classFile.constant_pool)),
-                                                              launder(() -> m.descriptor.getValue(classFile.constant_pool))));
+              .forEach(m -> builder.addMethod(m.access_flags.flags,
+                                              launder(() -> m.getName(classFile.constant_pool)),
+                                              launder(() -> m.descriptor.getValue(classFile.constant_pool))));
+        model.representationClass = builder.build();
         return model;
     }
 
@@ -130,11 +130,6 @@
 
     @Override
     public Class<?> getRepresentationClass() {
-        if (representationClass == null && proxyClassBuilder != null) {
-            representationClass = proxyClassBuilder.build();
-            proxyClassBuilder = null;
-        }
-
         return representationClass;
     }
 
--- a/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1.java	Thu Jun 09 12:44:49 2016 -0400
+++ b/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1.java	Thu Jun 09 14:51:25 2016 -0400
@@ -122,4 +122,24 @@
     public static InterpreterTestHelper1 make(int i, long l, char c, float f, double d, short s, byte b, boolean z, Object o, Class clazz) {
         return new InterpreterTestHelper1(i, l, c, f, d, s, b, z, o, clazz);
     }
+
+    public static int testOutboard() {
+        return InterpreterTestHelper1a.three();
+    }
+
+    public static int testInterfaceCall() {
+        AnInterface f = new AnImplementingClass();
+        return f.three();
+    }
+
+    interface AnInterface {
+        int three();
+    }
+
+    static class AnImplementingClass implements AnInterface {
+        @Override
+        public int three() {
+            return 3;
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1a.java	Thu Jun 09 14:51:25 2016 -0400
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package valhalla.interpreter;
+
+/**
+ * InterpreterTestHelper1a
+ *
+ * @author Brian Goetz
+ */
+public class InterpreterTestHelper1a {
+    public static int three() {
+        return 3;
+    }
+}
--- a/interpreter/test/valhalla/interpreter/InterpreterTest.java	Thu Jun 09 12:44:49 2016 -0400
+++ b/interpreter/test/valhalla/interpreter/InterpreterTest.java	Thu Jun 09 14:51:25 2016 -0400
@@ -132,12 +132,21 @@
         interpreter.invokestatic(HELPER_1, "assertInstance", "(L" + HELPER_1 + ";)V", instance);
     }
 
+    public void testOutboard() throws Throwable {
+        Integer i = (Integer) interpreter.invokestatic(HELPER_1, "testOutboard", "()I");
+        assertEquals((int) i, 3);
+    }
+
+    public void testInterface() throws Throwable {
+        Integer i = (Integer) interpreter.invokestatic(HELPER_1, "testInterfaceCall", "()I");
+        assertEquals((int) i, 3);
+    }
+
     public void testLambda() throws Throwable {
         assertEquals(interpreter.invokestatic(HELPER_3, "testSaysHello", "()Ljava/lang/String;"), "hello");
         assertEquals(interpreter.invokestatic(HELPER_3, "testSaysHelloThere", "(Ljava/lang/String;)Ljava/lang/String;", "there"), "hellothere");
 
-        // @@@ Invokespecial MH lookup fails
-        // assertEquals(interpreter.invokestatic(HELPER_3, "instanceLambda", "()Ljava/lang/String;"), "wooga");
+        assertEquals(interpreter.invokestatic(HELPER_3, "instanceLambda", "()Ljava/lang/String;"), "wooga");
     }
 
     public void testSwitch() throws Throwable {