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 impl.java.dyn;
  27 
  28 import java.dyn.MethodHandle;
  29 import java.dyn.MethodHandles;
  30 import java.dyn.MethodType;
  31 import impl.java.dyn.JavaMethodHandle;
  32 import impl.java.dyn.util.MethodHandleInvoker;
  33 import java.dyn.NoAccessException;
  34 import static impl.java.dyn.MemberName.newIllegalArgumentException;
  35 import static impl.java.dyn.MemberName.newNoAccessException;
  36 
  37 /**
  38  * Base class for method handles which are known to the Hotspot JVM.
  39  * @author jrose
  40  */
  41 public abstract class MethodHandleImpl {
  42 
  43     // Fields in MethodHandle:
  44     private byte       vmentry;    // adapter stub or method entry point
  45     //private int      vmslots;    // optionally, hoist type.form.vmslots
  46     protected Object   vmtarget;   // VM-specific, class-specific target value
  47     //MethodType       type;       // defined in MethodHandle
  48 
  49     // These two dummy fields are present to force 'I' and 'J' signatures
  50     // into this class's constant pool, so they can be transferred
  51     // to vmentry when this class is loaded.
  52     static final int  INT_FIELD = 0;
  53     static final long LONG_FIELD = 0;
  54 
  55     // type is defined in java.dyn.MethodHandle, which is platform-independent
  56 
  57     // vmentry (a void* field) is used *only* by by the JVM.
  58     // The JVM adjusts its type to int or long depending on system wordsize.
  59     // Since it is statically typed as neither int nor long, it is impossible
  60     // to use this field from Java bytecode.  (Please don't try to, either.)
  61 
  62     // The vmentry is an assembly-language stub which is jumped to
  63     // immediately after the method type is verified.
  64     // For a direct MH, this stub loads the vmtarget's entry point
  65     // and jumps to it.
  66 
  67     /**
  68      * VM-based method handles must have a security token.
  69      * This security token can only be obtained by trusted code.
  70      * Do not create method handles directly; use factory methods.
  71      */
  72     public MethodHandleImpl(Access token) {
  73         Access.check(token);
  74     }
  75 
  76     /** Initialize the method type form to participate in JVM calls.
  77      *  This is done once for each erased type.
  78      */
  79     public static void init(Access token, MethodType self) {
  80         Access.check(token);
  81         if (MethodHandleNatives.JVM_SUPPORT)
  82             MethodHandleNatives.init(self);
  83     }
  84 
  85     /// Factory methods to create method handles:
  86 
  87     private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
  88 
  89 
  90     /** Look up a given method.
  91      * Callable only from java.dyn and related packages.
  92      * <p>
  93      * The resulting method handle type will be of the given type,
  94      * with a receiver type {@code rcvc} prepended if the member is not static.
  95      * <p>
  96      * Access checks are made as of the given caller.
  97      * In particular, if the method is protected and {@code defc} is in a
  98      * different package from the caller, then {@code rcvc} must be
  99      * caller or a subclass.
 100      * @param token Proof that the caller has access to this package.
 101      * @param member Resolved method or constructor to call.
 102      * @param name Name of the desired method.
 103      * @param rcvc Receiver type of desired non-static method (else null)
 104      * @param doDispatch whether the method handle will test the receiver type
 105      * @param caller if not null, access-check relative to this class ????
 106      * @return a direct handle to the matching method
 107      * @throws NoAccessException if the given method cannot be accessed by caller
 108      */
 109     public static
 110     MethodHandle findMethod(Access token, MemberName method,
 111             boolean doDispatch, Class<?> caller) {
 112         Access.check(token);  // only trusted calls
 113         MethodType mtype = method.getMethodType();
 114         if (method.isStatic()) {
 115             doDispatch = false;
 116         } else {
 117             // adjust the advertised receiver type to be exactly the one requested
 118             // (in the case of invokespecial, this will be the calling class)
 119             mtype = mtype.insertParameterType(0, method.getDeclaringClass());
 120             if (method.isConstructor())
 121                 doDispatch = true;
 122         }
 123         DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, caller);
 124         if (!mh.isValid())
 125             throw newNoAccessException(method, caller);
 126         return mh;
 127     }
 128 
 129     public static
 130     MethodHandle accessField(Access token,
 131                            MemberName member, boolean isSetter,
 132                            Class<?> caller) {
 133         Access.check(token);
 134         // FIXME: Use sun.misc.Unsafe to dig up the dirt on the field.
 135         throw new UnsupportedOperationException("Not yet implemented");
 136     }
 137 
 138     public static
 139     MethodHandle accessArrayElement(Access token,
 140                            Class<?> arrayClass, boolean isSetter,
 141                            Class<?> caller) {
 142         Access.check(token);
 143         if (!arrayClass.isArray())
 144             throw newIllegalArgumentException("not an array: "+arrayClass);
 145         // FIXME: Use sun.misc.Unsafe to dig up the dirt on the array.
 146         throw new UnsupportedOperationException("Not yet implemented");
 147     }
 148 
 149     /** Bind a predetermined first argument to the given direct method handle.
 150      * Callable only from MethodHandles.
 151      * @param token Proof that the caller has access to this package.
 152      * @param target Any direct method handle.
 153      * @param receiver Receiver (or first static method argument) to pre-bind.
 154      * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
 155      */
 156     public static
 157     MethodHandle bindReceiver(Access token,
 158                               MethodHandle target, Object receiver) {
 159         Access.check(token);
 160         if (target instanceof DirectMethodHandle)
 161             return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
 162         return null;   // let caller try something else
 163     }
 164 
 165     /** Bind a predetermined argument to the given arbitrary method handle.
 166      * Callable only from MethodHandles.
 167      * @param token Proof that the caller has access to this package.
 168      * @param target Any method handle.
 169      * @param receiver Argument (which can be a boxed primitive) to pre-bind.
 170      * @return a suitable BoundMethodHandle
 171      */
 172     public static
 173     MethodHandle bindArgument(Access token,
 174                               MethodHandle target, int argnum, Object receiver) {
 175         Access.check(token);
 176         throw new UnsupportedOperationException("NYI");
 177     }
 178 
 179     public static MethodHandle convertArguments(Access token,
 180                                                 MethodHandle target,
 181                                                 MethodType newType, boolean newVarargs,
 182                                                 MethodType oldType, boolean oldVarargs,
 183                                                 String permutationOrNull) {
 184         Access.check(token);
 185         throw new UnsupportedOperationException("Not yet implemented");
 186     }
 187 
 188     public static
 189     MethodHandle dropArguments(Access token, MethodHandle target,
 190                                MethodType newType, int argnum) {
 191         Access.check(token);
 192         throw new UnsupportedOperationException("NYI");
 193     }
 194 
 195     public static
 196     MethodHandle makeGuardWithTest(Access token,
 197                                    final MethodHandle test,
 198                                    final MethodHandle target,
 199                                    final MethodHandle fallback) {
 200         Access.check(token);
 201         // %%% This is just a sketch.  It needs to be de-boxed.
 202         // Adjust the handles to accept varargs lists.
 203         MethodType type = target.type();
 204         Class<?>  rtype = type.returnType();
 205         if (type.parameterCount() != 1 || type.parameterType(0).isPrimitive()) {
 206             MethodType vatestType   = MethodType.make(boolean.class, Object[].class);
 207             MethodType vatargetType = MethodType.make(rtype, Object[].class);
 208             MethodHandle vaguard = makeGuardWithTest(token,
 209                     MethodHandles.spreadArguments(test, vatestType),
 210                     MethodHandles.spreadArguments(target, vatargetType),
 211                     MethodHandles.spreadArguments(fallback, vatargetType));
 212             return MethodHandles.collectArguments(vaguard, type);
 213         }
 214         if (rtype.isPrimitive()) {
 215             MethodType boxtype = type.changeReturnType(Object.class);
 216             MethodHandle boxguard = makeGuardWithTest(token,
 217                     test,
 218                     MethodHandles.convertArguments(target, boxtype),
 219                     MethodHandles.convertArguments(fallback, boxtype));
 220             return MethodHandles.convertArguments(boxguard, type);
 221         }
 222         // Got here?  Reduced calling sequence to Object(Object).
 223         final MethodHandleInvoker invoke1
 224                 = MethodHandleInvoker.make(test.type());
 225         final MethodHandleInvoker invoke2
 226                 = MethodHandleInvoker.make(target.type());
 227         class Guarder {
 228             Object invoke(Object x) {
 229                 // If javac supports MethodHandle.invoke directly:
 230                 //z = vatest.invoke<boolean>(arguments);
 231                 // If javac does not support direct MH.invoke calls:
 232                 boolean z = (Boolean) invoke1.invoke_1(test, x);
 233                 MethodHandle mh = (z ? target : fallback);
 234                 return invoke2.invoke_1(mh, x);
 235             }
 236             MethodHandle handle() {
 237                 MethodType invokeType = MethodType.makeGeneric(0, true);
 238                 MethodHandle vh = MethodHandles.bind(this, "invoke", invokeType);
 239                 return MethodHandles.collectArguments(vh, target.type());
 240             }
 241         }
 242         return new Guarder().handle();
 243     }
 244 
 245     public static
 246     MethodHandle checkArguments(Access token, MethodHandle target, MethodHandle checker, int pos) {
 247         Access.check(token);
 248         throw new UnsupportedOperationException("Not yet implemented");
 249     }
 250 
 251     protected static String basicToString(MethodHandle target) {
 252         MemberName name = MethodHandleNatives.getMethodName(target);
 253         if (name == null)
 254             name = new MemberName(null, "<unknown>", target.type());
 255         return name.toString();
 256     }
 257 
 258     static RuntimeException newIllegalArgumentException(String string) {
 259         return new IllegalArgumentException(string);
 260     }
 261 
 262     @Override
 263     public String toString() {
 264         return basicToString((MethodHandle)this);
 265     }
 266 }