changeset 14053:f9072be260c9

Interpreter: For final field writes from <clinit>, use reflection instead of putStatic MH. More logging. More checks that proxy classes are loaded before use. Fixed broken IDE configuration that was causing some classes to be on both the execution and interpretatino class path. More tests. Failing tests: 2.
author briangoetz
date Thu, 09 Jun 2016 22:20:10 -0400
parents 1ba172980186
children 2e69293d5e12
files interpreter/interpreter.iml interpreter/src/valhalla/interpreter/ClassModel.java interpreter/src/valhalla/interpreter/InternalHelpers.java interpreter/src/valhalla/interpreter/Interpreter.java interpreter/src/valhalla/interpreter/InterpreterEvent.java interpreter/src/valhalla/interpreter/ProxyClassBuilder.java interpreter/src/valhalla/interpreter/StandardMethodModel.java interpreter/test-common/test-common.iml interpreter/test-helpers/test-helpers.iml interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1.java interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper5.java interpreter/test/valhalla/interpreter/InterpreterTest.java
diffstat 12 files changed, 85 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/interpreter/interpreter.iml	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/interpreter.iml	Thu Jun 09 22:20:10 2016 -0400
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/out/production/interpreter" />
+    <output-test url="file://$MODULE_DIR$/out/test/interpreter" />
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
--- a/interpreter/src/valhalla/interpreter/ClassModel.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/ClassModel.java	Thu Jun 09 22:20:10 2016 -0400
@@ -57,6 +57,11 @@
             this.name = name;
             this.desc = desc;
         }
+
+        @Override
+        public String toString() {
+            return String.format("MemberDesc[%s.%s:%s]", owner, name, desc);
+        }
     }
 
     class IndyDesc {
--- a/interpreter/src/valhalla/interpreter/InternalHelpers.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/InternalHelpers.java	Thu Jun 09 22:20:10 2016 -0400
@@ -58,7 +58,7 @@
         }
     }
 
-    public static Class<?> loadClass(Interpreter interpreter, String name, byte[] bytes) {
+    public static Class<?> defineClass(Interpreter interpreter, String name, byte[] bytes) {
         return UNSAFE.defineClass(name, bytes, 0, bytes.length, interpreter.classLoader, null);
     }
 
--- a/interpreter/src/valhalla/interpreter/Interpreter.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/Interpreter.java	Thu Jun 09 22:20:10 2016 -0400
@@ -37,6 +37,8 @@
 import java.lang.invoke.*;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
@@ -100,10 +102,20 @@
         public final URLClassPath ucp;
 
         public InterpreterClassLoader(Interpreter interpreter, URL[] urls, ClassLoader parent) {
-            super(urls, parent);
+            super(new URL[0], parent); // don't let parent do any actual classloading!
             this.interpreter = interpreter;
             this.ucp = new URLClassPath(urls);
         }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            System.err.println("ClassLoader: " + name);
+            ClassModel cm = interpreter.tryLoad(name.replace('.', '/'));
+            if (cm != null)
+                return cm.getRepresentationClass();
+            else
+                return super.loadClass(name, resolve);
+        }
     }
 
     protected Object loadConstant(Frame f, int index) {
@@ -288,6 +300,29 @@
     protected HandlerAction fieldOp(Frame f) throws InterpreterError {
         ClassModel.MemberDesc ref = f.readMemberRef(1);
         Class type = MethodType.fromMethodDescriptorString("()" + ref.desc, f.curClassLoader()).returnType();
+
+        // Special handling for PUTSTATIC to own fields from <clinit>: use reflection instead of MHI
+        if (f.curOpcode() == PUTSTATIC
+            && f.curMethod().getName().equals("<clinit>")
+            && ref.owner.equals(f.curClass().getName())) {
+            try {
+                if (TRACING)
+                    System.out.printf("%s[bci=%d]: opcode=%d (%s) -- special PUTSTATIC handling %n",
+                                      f.curMethod().getName(), f.curBCI(), f.curOpcode(), opcodeToString(f.curOpcode()));
+                Field field = f.curClass().getRepresentationClass().getDeclaredField(ref.name);
+                field.setAccessible(true);
+                Field modifiersField = Field.class.getDeclaredField("modifiers");
+                modifiersField.setAccessible(true);
+                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+                field.set(null, f.popBoxed(ref.desc));
+                return next();
+            }
+            catch (ReflectiveOperationException e) {
+                if (TRACING)
+                    System.out.printf("Exception looking up field: " + e);
+                // fall through to standard processing
+            }
+        }
         return memberOp(f, toClass(f, ref.owner), ref.name, type);
     }
 
@@ -556,6 +591,9 @@
                             continue;
                         }
                         // else throw out to caller frame
+                        if (TRACING)
+                            System.out.printf("%s[bci=%d]: opcode=%d (%s), throwing %s %n", f.curMethod().getName(), f.curBCI(), f.curOpcode(), opcodeToString(f.curOpcode()), a.returnValue);
+
                         return a;
                 }
             }
--- a/interpreter/src/valhalla/interpreter/InterpreterEvent.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/InterpreterEvent.java	Thu Jun 09 22:20:10 2016 -0400
@@ -78,7 +78,7 @@
     @Override
     public String toString() {
         if (kind == LOAD)
-            return String.format("LOAD:%s", name);
+            return String.format("LOAD:%s", clazz);
         else
             return String.format("%s:%s.%s%s", kind, clazz, name, desc);
     }
--- a/interpreter/src/valhalla/interpreter/ProxyClassBuilder.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/ProxyClassBuilder.java	Thu Jun 09 22:20:10 2016 -0400
@@ -27,7 +27,6 @@
 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;
@@ -139,6 +138,6 @@
         for (String s : loadDependencies) {
             ClassModel model = interpreter.tryLoad(s);
         }
-        return InternalHelpers.loadClass(interpreter, className, bytes);
+        return InternalHelpers.defineClass(interpreter, className, bytes);
     }
 }
--- a/interpreter/src/valhalla/interpreter/StandardMethodModel.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/src/valhalla/interpreter/StandardMethodModel.java	Thu Jun 09 22:20:10 2016 -0400
@@ -40,19 +40,21 @@
  */
 public class StandardMethodModel implements MethodModel {
     private final StandardClassModel classModel;
-    private final ClassFile classFile;
     private final Method method;
     private final Code_attribute code;
     private final byte[] bytecode;
     private final Insn[] insns;
+    private final String name;
+    private final String desc;
 
     public StandardMethodModel(StandardClassModel classModel, ClassFile classFile, Method method) {
         this.classModel = classModel;
-        this.classFile = classFile;
         this.method = method;
         this.code = (Code_attribute) method.attributes.get("Code");
         this.bytecode = code == null ? new byte[0] : code.code;
         this.insns = new Insn[bytecode.length];
+        this.name = launder(() -> method.getName(classFile.constant_pool));
+        this.desc = launder(() -> method.descriptor.getValue(classFile.constant_pool));
 
         for (int bci=0; bci < bytecode.length; ) {
             Insn in = classModel.interpreter().recognizeInstruction(bytecode, bci);
@@ -68,12 +70,12 @@
 
     @Override
     public String getName() {
-        return launder(() -> method.getName(classFile.constant_pool));
+        return name;
     }
 
     @Override
     public String getDesc() {
-        return launder(() -> method.descriptor.getValue(classFile.constant_pool));
+        return desc;
     }
 
     @Override
--- a/interpreter/test-common/test-common.iml	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/test-common/test-common.iml	Thu Jun 09 22:20:10 2016 -0400
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/../out/production/test-common" />
+    <output-test url="file://$MODULE_DIR$/../out/test/test-common" />
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
--- a/interpreter/test-helpers/test-helpers.iml	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/test-helpers/test-helpers.iml	Thu Jun 09 22:20:10 2016 -0400
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/../out/production/test-helpers" />
+    <output-test url="file://$MODULE_DIR$/../out/test/test-helpers" />
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="true" />
--- a/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper1.java	Thu Jun 09 22:20:10 2016 -0400
@@ -23,7 +23,10 @@
  * questions.
  */
 
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * InterpreterTestHelper1
@@ -132,6 +135,18 @@
         return f.three();
     }
 
+    public static void testEnum() {
+        Set<TimeUnit> allTime = EnumSet.allOf(TimeUnit.class);
+        Set<TimeUnit> noneTime = EnumSet.noneOf(TimeUnit.class);
+
+        Pets p = Pets.DOG;
+        p = Pets.CAT;
+        Set<Pets> all = EnumSet.allOf(Pets.class);
+        Set<Pets> none = EnumSet.noneOf(Pets.class);
+    }
+
+    enum Pets { DOG, CAT; }
+
     interface AnInterface {
         int three();
     }
--- a/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper5.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/test-helpers/test/valhalla/interpreter/InterpreterTestHelper5.java	Thu Jun 09 22:20:10 2016 -0400
@@ -30,6 +30,8 @@
  */
 class InterpreterTestHelper5 {
     public static int x = 3;
+    public static final String y = InterpreterTestHelper5.class.getName();
 
     public static int x() { return x; }
+    public static String y() { return y; }
 }
--- a/interpreter/test/valhalla/interpreter/InterpreterTest.java	Thu Jun 09 17:04:53 2016 -0400
+++ b/interpreter/test/valhalla/interpreter/InterpreterTest.java	Thu Jun 09 22:20:10 2016 -0400
@@ -73,6 +73,8 @@
     public void testStaticInit() throws Throwable {
         Object o = interpreter.invokestatic(HELPER_5, "x", "()I");
         assertEquals((int) (Integer) o, 3);
+        o = interpreter.invokestatic(HELPER_5, "y", "()Ljava/lang/String;");
+        assertEquals(o, "valhalla.interpreter.InterpreterTestHelper5");
     }
 
     public void testLoadSimple() throws Throwable {
@@ -142,6 +144,10 @@
         assertEquals((int) i, 3);
     }
 
+    public void testEnum() throws Throwable {
+        interpreter.invokestatic(HELPER_1, "testEnum", "()V");
+    }
+
     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");