changeset 17378:3aea46a3cb48

8174962: Better interface invocations Reviewed-by: jrose, coleenp, ahgross, acorn, iignatyev
author vlivanov
date Fri, 26 May 2017 18:39:51 +0300
parents 3abd28d7136d
children db0f0d25f410
files src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
diffstat 1 files changed, 48 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Thu May 25 23:31:47 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Fri May 26 18:39:51 2017 +0300
@@ -80,13 +80,20 @@
             mtype = mtype.insertParameterTypes(0, receiver);
         }
         if (!member.isField()) {
-            if (refKind == REF_invokeSpecial) {
-                member = member.asSpecial();
-                LambdaForm lform = preparedLambdaForm(member);
-                return new Special(mtype, lform, member);
-            } else {
-                LambdaForm lform = preparedLambdaForm(member);
-                return new DirectMethodHandle(mtype, lform, member);
+            switch (refKind) {
+                case REF_invokeSpecial: {
+                    member = member.asSpecial();
+                    LambdaForm lform = preparedLambdaForm(member);
+                    return new Special(mtype, lform, member);
+                }
+                case REF_invokeInterface: {
+                    LambdaForm lform = preparedLambdaForm(member);
+                    return new Interface(mtype, lform, member, receiver);
+                }
+                default: {
+                    LambdaForm lform = preparedLambdaForm(member);
+                    return new DirectMethodHandle(mtype, lform, member);
+                }
             }
         } else {
             LambdaForm lform = preparedFieldLambdaForm(member);
@@ -190,6 +197,7 @@
     static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
         boolean needsInit = (which == LF_INVSTATIC_INIT);
         boolean doesAlloc = (which == LF_NEWINVSPECIAL);
+        boolean needsReceiverCheck = (which == LF_INVINTERFACE);
         String linkerName;
         LambdaForm.Kind kind;
         switch (which) {
@@ -219,6 +227,7 @@
         int nameCursor = ARG_LIMIT;
         final int NEW_OBJ     = (doesAlloc ? nameCursor++ : -1);
         final int GET_MEMBER  = nameCursor++;
+        final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1);
         final int LINKER_CALL = nameCursor++;
         Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
         assert(names.length == nameCursor);
@@ -233,6 +242,10 @@
         }
         assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
         Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
+        if (needsReceiverCheck) {
+            names[CHECK_RECEIVER] = new Name(NF_checkReceiver, names[DMH_THIS], names[ARG_BASE]);
+            outArgs[0] = names[CHECK_RECEIVER];
+        }
         assert(outArgs[outArgs.length-1] == names[GET_MEMBER]);  // look, shifted args!
         int result = LAST_RESULT;
         if (doesAlloc) {
@@ -377,6 +390,29 @@
         }
     }
 
+    /** This subclass represents invokeinterface instructions. */
+    static class Interface extends DirectMethodHandle {
+        private final Class<?> refc;
+        private Interface(MethodType mtype, LambdaForm form, MemberName member, Class<?> refc) {
+            super(mtype, form, member);
+            assert refc.isInterface() : refc;
+            this.refc = refc;
+        }
+        @Override
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            return new Interface(mt, lf, member, refc);
+        }
+
+        Object checkReceiver(Object recv) {
+            if (!refc.isInstance(recv)) {
+                String msg = String.format("Class %s does not implement the requested interface %s",
+                        recv.getClass().getName(), refc.getName());
+                throw new IncompatibleClassChangeError(msg);
+            }
+            return recv;
+        }
+    }
+
     /** This subclass handles constructor references. */
     static class Constructor extends DirectMethodHandle {
         final MemberName initMethod;
@@ -729,7 +765,8 @@
             NF_checkCast,
             NF_allocateInstance,
             NF_constructorMethod,
-            NF_UNSAFE;
+            NF_UNSAFE,
+            NF_checkReceiver;
     static {
         try {
             NamedFunction nfs[] = {
@@ -754,7 +791,9 @@
                     NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
                             .getDeclaredMethod("constructorMethod", Object.class)),
                     NF_UNSAFE = new NamedFunction(new MemberName(MethodHandleStatics.class
-                            .getDeclaredField("UNSAFE")))
+                            .getDeclaredField("UNSAFE"))),
+                    NF_checkReceiver = new NamedFunction(new MemberName(Interface.class
+                            .getDeclaredMethod("checkReceiver", Object.class)))
             };
             // Each nf must be statically invocable or we get tied up in our bootstraps.
             assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));