jdk/src/share/classes/sun/dyn/MethodHandleImpl.java
changeset 2707 5a17df307cbc
child 2764 2e45af54c0f9
equal deleted inserted replaced
2706:ead715aebca2 2707:5a17df307cbc
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.dyn;
       
    27 
       
    28 import java.dyn.MethodHandle;
       
    29 import java.dyn.MethodHandles;
       
    30 import java.dyn.MethodHandles.Lookup;
       
    31 import java.dyn.MethodType;
       
    32 import sun.dyn.util.VerifyType;
       
    33 import java.dyn.NoAccessException;
       
    34 import static sun.dyn.MemberName.newIllegalArgumentException;
       
    35 import static sun.dyn.MemberName.newNoAccessException;
       
    36 
       
    37 /**
       
    38  * Base class for method handles, containing JVM-specific fields and logic.
       
    39  * TO DO:  It should not be a base class.
       
    40  * @author jrose
       
    41  */
       
    42 public abstract class MethodHandleImpl {
       
    43 
       
    44     // Fields which really belong in MethodHandle:
       
    45     private byte       vmentry;    // adapter stub or method entry point
       
    46     //private int      vmslots;    // optionally, hoist type.form.vmslots
       
    47     protected Object   vmtarget;   // VM-specific, class-specific target value
       
    48     //MethodType       type;       // defined in MethodHandle
       
    49 
       
    50     // TO DO:  vmtarget should be invisible to Java, since the JVM puts internal
       
    51     // managed pointers into it.  Making it visible exposes it to debuggers,
       
    52     // which can cause errors when they treat the pointer as an Object.
       
    53 
       
    54     // These two dummy fields are present to force 'I' and 'J' signatures
       
    55     // into this class's constant pool, so they can be transferred
       
    56     // to vmentry when this class is loaded.
       
    57     static final int  INT_FIELD = 0;
       
    58     static final long LONG_FIELD = 0;
       
    59 
       
    60     // type is defined in java.dyn.MethodHandle, which is platform-independent
       
    61 
       
    62     // vmentry (a void* field) is used *only* by by the JVM.
       
    63     // The JVM adjusts its type to int or long depending on system wordsize.
       
    64     // Since it is statically typed as neither int nor long, it is impossible
       
    65     // to use this field from Java bytecode.  (Please don't try to, either.)
       
    66 
       
    67     // The vmentry is an assembly-language stub which is jumped to
       
    68     // immediately after the method type is verified.
       
    69     // For a direct MH, this stub loads the vmtarget's entry point
       
    70     // and jumps to it.
       
    71 
       
    72     /**
       
    73      * VM-based method handles must have a security token.
       
    74      * This security token can only be obtained by trusted code.
       
    75      * Do not create method handles directly; use factory methods.
       
    76      */
       
    77     public MethodHandleImpl(Access token) {
       
    78         Access.check(token);
       
    79     }
       
    80 
       
    81     /** Initialize the method type form to participate in JVM calls.
       
    82      *  This is done once for each erased type.
       
    83      */
       
    84     public static void init(Access token, MethodType self) {
       
    85         Access.check(token);
       
    86         if (MethodHandleNatives.JVM_SUPPORT)
       
    87             MethodHandleNatives.init(self);
       
    88     }
       
    89 
       
    90     /// Factory methods to create method handles:
       
    91 
       
    92     private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
       
    93 
       
    94     static private Lookup IMPL_LOOKUP_INIT;
       
    95 
       
    96     public static void initLookup(Access token, Lookup lookup) {
       
    97         Access.check(token);
       
    98         if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != Access.class)
       
    99             throw new InternalError();
       
   100         IMPL_LOOKUP_INIT = lookup;
       
   101     }
       
   102 
       
   103     public static Lookup getLookup(Access token) {
       
   104         Access.check(token);
       
   105         return IMPL_LOOKUP;
       
   106     }
       
   107 
       
   108     static {
       
   109         // Force initialization:
       
   110         Lookup.PUBLIC_LOOKUP.lookupClass();
       
   111         if (IMPL_LOOKUP_INIT == null)
       
   112             throw new InternalError();
       
   113     }
       
   114 
       
   115     public static void initStatics() {
       
   116         // Trigger preceding sequence.
       
   117     }
       
   118 
       
   119     /** Shared secret with MethodHandles.Lookup, a copy of Lookup.IMPL_LOOKUP. */
       
   120     static final Lookup IMPL_LOOKUP = IMPL_LOOKUP_INIT;
       
   121 
       
   122 
       
   123     /** Look up a given method.
       
   124      * Callable only from java.dyn and related packages.
       
   125      * <p>
       
   126      * The resulting method handle type will be of the given type,
       
   127      * with a receiver type {@code rcvc} prepended if the member is not static.
       
   128      * <p>
       
   129      * Access checks are made as of the given lookup class.
       
   130      * In particular, if the method is protected and {@code defc} is in a
       
   131      * different package from the lookup class, then {@code rcvc} must be
       
   132      * the lookup class or a subclass.
       
   133      * @param token Proof that the lookup class has access to this package.
       
   134      * @param member Resolved method or constructor to call.
       
   135      * @param name Name of the desired method.
       
   136      * @param rcvc Receiver type of desired non-static method (else null)
       
   137      * @param doDispatch whether the method handle will test the receiver type
       
   138      * @param lookupClass access-check relative to this class
       
   139      * @return a direct handle to the matching method
       
   140      * @throws NoAccessException if the given method cannot be accessed by the lookup class
       
   141      */
       
   142     public static
       
   143     MethodHandle findMethod(Access token, MemberName method,
       
   144             boolean doDispatch, Class<?> lookupClass) {
       
   145         Access.check(token);  // only trusted calls
       
   146         MethodType mtype = method.getMethodType();
       
   147         if (method.isStatic()) {
       
   148             doDispatch = false;
       
   149         } else {
       
   150             // adjust the advertised receiver type to be exactly the one requested
       
   151             // (in the case of invokespecial, this will be the calling class)
       
   152             mtype = mtype.insertParameterType(0, method.getDeclaringClass());
       
   153             if (method.isConstructor())
       
   154                 doDispatch = true;
       
   155         }
       
   156         DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
       
   157         if (!mh.isValid())
       
   158             throw newNoAccessException(method, lookupClass);
       
   159         return mh;
       
   160     }
       
   161 
       
   162     public static
       
   163     MethodHandle accessField(Access token,
       
   164                            MemberName member, boolean isSetter,
       
   165                            Class<?> lookupClass) {
       
   166         Access.check(token);
       
   167         // FIXME: Use sun.misc.Unsafe to dig up the dirt on the field.
       
   168         throw new UnsupportedOperationException("Not yet implemented");
       
   169     }
       
   170 
       
   171     public static
       
   172     MethodHandle accessArrayElement(Access token,
       
   173                            Class<?> arrayClass, boolean isSetter) {
       
   174         Access.check(token);
       
   175         if (!arrayClass.isArray())
       
   176             throw newIllegalArgumentException("not an array: "+arrayClass);
       
   177         // FIXME: Use sun.misc.Unsafe to dig up the dirt on the array.
       
   178         throw new UnsupportedOperationException("Not yet implemented");
       
   179     }
       
   180 
       
   181     /** Bind a predetermined first argument to the given direct method handle.
       
   182      * Callable only from MethodHandles.
       
   183      * @param token Proof that the caller has access to this package.
       
   184      * @param target Any direct method handle.
       
   185      * @param receiver Receiver (or first static method argument) to pre-bind.
       
   186      * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
       
   187      */
       
   188     public static
       
   189     MethodHandle bindReceiver(Access token,
       
   190                               MethodHandle target, Object receiver) {
       
   191         Access.check(token);
       
   192         if (target instanceof DirectMethodHandle)
       
   193             return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
       
   194         return null;   // let caller try something else
       
   195     }
       
   196 
       
   197     /** Bind a predetermined argument to the given arbitrary method handle.
       
   198      * Callable only from MethodHandles.
       
   199      * @param token Proof that the caller has access to this package.
       
   200      * @param target Any method handle.
       
   201      * @param receiver Argument (which can be a boxed primitive) to pre-bind.
       
   202      * @return a suitable BoundMethodHandle
       
   203      */
       
   204     public static
       
   205     MethodHandle bindArgument(Access token,
       
   206                               MethodHandle target, int argnum, Object receiver) {
       
   207         Access.check(token);
       
   208         throw new UnsupportedOperationException("NYI");
       
   209     }
       
   210 
       
   211     public static MethodHandle convertArguments(Access token,
       
   212                                                 MethodHandle target,
       
   213                                                 MethodType newType,
       
   214                                                 MethodType oldType,
       
   215                                                 int[] permutationOrNull) {
       
   216         Access.check(token);
       
   217         MethodHandle res = AdapterMethodHandle.makePairwiseConvert(token, newType, target);
       
   218         if (res != null)
       
   219             return res;
       
   220         int argc = oldType.parameterCount();
       
   221         // The JVM can't do it directly, so fill in the gap with a Java adapter.
       
   222         // TO DO: figure out what to put here from case-by-case experience
       
   223         // Use a heavier method:  Convert all the arguments to Object,
       
   224         // then back to the desired types.  We might have to use Java-based
       
   225         // method handles to do this.
       
   226         MethodType objType = MethodType.makeGeneric(argc);
       
   227         MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(token, objType, target);
       
   228         if (objTarget == null)
       
   229             objTarget = FromGeneric.make(target);
       
   230         res = AdapterMethodHandle.makePairwiseConvert(token, newType, objTarget);
       
   231         if (res != null)
       
   232             return res;
       
   233         return ToGeneric.make(newType, objTarget);
       
   234     }
       
   235 
       
   236     public static MethodHandle spreadArguments(Access token,
       
   237                                                MethodHandle target,
       
   238                                                MethodType newType,
       
   239                                                int spreadArg) {
       
   240         Access.check(token);
       
   241         // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
       
   242         MethodType oldType = target.type();
       
   243         // spread the last argument of newType to oldType
       
   244         int spreadCount = oldType.parameterCount() - spreadArg;
       
   245         Class<Object[]> spreadArgType = Object[].class;
       
   246         MethodHandle res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount);
       
   247         if (res != null)
       
   248             return res;
       
   249         // try an intermediate adapter
       
   250         Class<?> spreadType = null;
       
   251         if (spreadArg < 0 || spreadArg >= newType.parameterCount()
       
   252             || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
       
   253             throw newIllegalArgumentException("no restarg in "+newType);
       
   254         Class<?>[] ptypes = oldType.parameterArray();
       
   255         for (int i = 0; i < spreadCount; i++)
       
   256             ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
       
   257         MethodType midType = MethodType.make(newType.returnType(), ptypes);
       
   258         // after spreading, some arguments may need further conversion
       
   259         target = convertArguments(token, target, midType, oldType, null);
       
   260         if (target == null)
       
   261             throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
       
   262         res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount);
       
   263         return res;
       
   264     }
       
   265 
       
   266     public static MethodHandle collectArguments(Access token,
       
   267                                                 MethodHandle target,
       
   268                                                 MethodType newType,
       
   269                                                 int collectArg) {
       
   270         if (collectArg > 0)
       
   271             throw new UnsupportedOperationException("NYI");
       
   272         throw new UnsupportedOperationException("NYI");
       
   273     }
       
   274     public static
       
   275     MethodHandle dropArguments(Access token, MethodHandle target,
       
   276                                MethodType newType, int argnum) {
       
   277         Access.check(token);
       
   278         throw new UnsupportedOperationException("NYI");
       
   279     }
       
   280 
       
   281     public static
       
   282     MethodHandle makeGuardWithTest(Access token,
       
   283                                    final MethodHandle test,
       
   284                                    final MethodHandle target,
       
   285                                    final MethodHandle fallback) {
       
   286         Access.check(token);
       
   287         // %%% This is just a sketch.  It needs to be de-boxed.
       
   288         // Adjust the handles to accept varargs lists.
       
   289         MethodType type = target.type();
       
   290         Class<?>  rtype = type.returnType();
       
   291         if (type.parameterCount() != 1 || type.parameterType(0).isPrimitive()) {
       
   292             MethodType vatestType   = MethodType.make(boolean.class, Object[].class);
       
   293             MethodType vatargetType = MethodType.make(rtype, Object[].class);
       
   294             MethodHandle vaguard = makeGuardWithTest(token,
       
   295                     MethodHandles.spreadArguments(test, vatestType),
       
   296                     MethodHandles.spreadArguments(target, vatargetType),
       
   297                     MethodHandles.spreadArguments(fallback, vatargetType));
       
   298             return MethodHandles.collectArguments(vaguard, type);
       
   299         }
       
   300         if (rtype.isPrimitive()) {
       
   301             MethodType boxtype = type.changeReturnType(Object.class);
       
   302             MethodHandle boxguard = makeGuardWithTest(token,
       
   303                     test,
       
   304                     MethodHandles.convertArguments(target, boxtype),
       
   305                     MethodHandles.convertArguments(fallback, boxtype));
       
   306             return MethodHandles.convertArguments(boxguard, type);
       
   307         }
       
   308         // Got here?  Reduced calling sequence to Object(Object).
       
   309         class Guarder {
       
   310             Object invoke(Object x) {
       
   311                 // If javac supports MethodHandle.invoke directly:
       
   312                 //z = vatest.invoke<boolean>(arguments);
       
   313                 // If javac does not support direct MH.invoke calls:
       
   314                 boolean z = (Boolean) MethodHandles.invoke_1(test, x);
       
   315                 MethodHandle mh = (z ? target : fallback);
       
   316                 return MethodHandles.invoke_1(mh, x);
       
   317             }
       
   318             MethodHandle handle() {
       
   319                 MethodType invokeType = MethodType.makeGeneric(0, true);
       
   320                 MethodHandle vh = IMPL_LOOKUP.bind(this, "invoke", invokeType);
       
   321                 return MethodHandles.collectArguments(vh, target.type());
       
   322             }
       
   323         }
       
   324         return new Guarder().handle();
       
   325     }
       
   326 
       
   327     public static
       
   328     MethodHandle combineArguments(Access token, MethodHandle target, MethodHandle checker, int pos) {
       
   329         Access.check(token);
       
   330         throw new UnsupportedOperationException("Not yet implemented");
       
   331     }
       
   332 
       
   333     protected static String basicToString(MethodHandle target) {
       
   334         MemberName name = null;
       
   335         if (target != null)
       
   336             name = MethodHandleNatives.getMethodName(target);
       
   337         if (name == null)
       
   338             return "<unknown>";
       
   339         return name.getName();
       
   340     }
       
   341 
       
   342     protected static String addTypeString(MethodHandle target, String name) {
       
   343         if (target == null)  return name;
       
   344         return name+target.type();
       
   345     }
       
   346     static RuntimeException newIllegalArgumentException(String string) {
       
   347         return new IllegalArgumentException(string);
       
   348     }
       
   349 
       
   350     @Override
       
   351     public String toString() {
       
   352         MethodHandle self = (MethodHandle) this;
       
   353         return addTypeString(self, basicToString(self));
       
   354     }
       
   355 }