/* * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package sun.dyn; import java.dyn.MethodHandle; import java.dyn.MethodHandles; import java.dyn.MethodHandles.Lookup; import java.dyn.MethodType; import sun.dyn.util.VerifyType; import java.dyn.NoAccessException; import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newNoAccessException; /** * Base class for method handles, containing JVM-specific fields and logic. * TO DO: It should not be a base class. * @author jrose */ public abstract class MethodHandleImpl { // Fields which really belong in MethodHandle: private byte vmentry; // adapter stub or method entry point //private int vmslots; // optionally, hoist type.form.vmslots protected Object vmtarget; // VM-specific, class-specific target value //MethodType type; // defined in MethodHandle // TO DO: vmtarget should be invisible to Java, since the JVM puts internal // managed pointers into it. Making it visible exposes it to debuggers, // which can cause errors when they treat the pointer as an Object. // These two dummy fields are present to force 'I' and 'J' signatures // into this class's constant pool, so they can be transferred // to vmentry when this class is loaded. static final int INT_FIELD = 0; static final long LONG_FIELD = 0; // type is defined in java.dyn.MethodHandle, which is platform-independent // vmentry (a void* field) is used *only* by by the JVM. // The JVM adjusts its type to int or long depending on system wordsize. // Since it is statically typed as neither int nor long, it is impossible // to use this field from Java bytecode. (Please don't try to, either.) // The vmentry is an assembly-language stub which is jumped to // immediately after the method type is verified. // For a direct MH, this stub loads the vmtarget's entry point // and jumps to it. /** * VM-based method handles must have a security token. * This security token can only be obtained by trusted code. * Do not create method handles directly; use factory methods. */ public MethodHandleImpl(Access token) { Access.check(token); } /** Initialize the method type form to participate in JVM calls. * This is done once for each erased type. */ public static void init(Access token, MethodType self) { Access.check(token); if (MethodHandleNatives.JVM_SUPPORT) MethodHandleNatives.init(self); } /// Factory methods to create method handles: private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE; static private Lookup IMPL_LOOKUP_INIT; public static void initLookup(Access token, Lookup lookup) { Access.check(token); if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != null) throw new InternalError(); IMPL_LOOKUP_INIT = lookup; } public static Lookup getLookup(Access token) { Access.check(token); return IMPL_LOOKUP; } static { // Force initialization: Lookup.PUBLIC_LOOKUP.lookupClass(); if (IMPL_LOOKUP_INIT == null) throw new InternalError(); } public static void initStatics() { // Trigger preceding sequence. } /** Shared secret with MethodHandles.Lookup, a copy of Lookup.IMPL_LOOKUP. */ static final Lookup IMPL_LOOKUP = IMPL_LOOKUP_INIT; /** Look up a given method. * Callable only from java.dyn and related packages. *
* The resulting method handle type will be of the given type, * with a receiver type {@code rcvc} prepended if the member is not static. *
* Access checks are made as of the given lookup class. * In particular, if the method is protected and {@code defc} is in a * different package from the lookup class, then {@code rcvc} must be * the lookup class or a subclass. * @param token Proof that the lookup class has access to this package. * @param member Resolved method or constructor to call. * @param name Name of the desired method. * @param rcvc Receiver type of desired non-static method (else null) * @param doDispatch whether the method handle will test the receiver type * @param lookupClass access-check relative to this class * @return a direct handle to the matching method * @throws NoAccessException if the given method cannot be accessed by the lookup class */ public static MethodHandle findMethod(Access token, MemberName method, boolean doDispatch, Class> lookupClass) { Access.check(token); // only trusted calls MethodType mtype = method.getMethodType(); MethodType rtype = mtype; if (method.isStatic()) { doDispatch = false; } else { // adjust the advertised receiver type to be exactly the one requested // (in the case of invokespecial, this will be the calling class) Class> recvType = method.getDeclaringClass(); mtype = mtype.insertParameterType(0, recvType); if (method.isConstructor()) doDispatch = true; // FIXME: JVM has trouble building MH.invoke sites for // classes off the boot class path rtype = mtype; if (recvType.getClassLoader() != null) rtype = rtype.changeParameterType(0, Object.class); } DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass); if (!mh.isValid()) throw newNoAccessException(method, lookupClass); MethodHandle rmh = AdapterMethodHandle.makePairwiseConvert(token, rtype, mh); if (rmh == null) throw new InternalError(); return rmh; } public static MethodHandle accessField(Access token, MemberName member, boolean isSetter, Class> lookupClass) { Access.check(token); // FIXME: Use sun.misc.Unsafe to dig up the dirt on the field. throw new UnsupportedOperationException("Not yet implemented"); } public static MethodHandle accessArrayElement(Access token, Class> arrayClass, boolean isSetter) { Access.check(token); if (!arrayClass.isArray()) throw newIllegalArgumentException("not an array: "+arrayClass); // FIXME: Use sun.misc.Unsafe to dig up the dirt on the array. throw new UnsupportedOperationException("Not yet implemented"); } /** Bind a predetermined first argument to the given direct method handle. * Callable only from MethodHandles. * @param token Proof that the caller has access to this package. * @param target Any direct method handle. * @param receiver Receiver (or first static method argument) to pre-bind. * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist */ public static MethodHandle bindReceiver(Access token, MethodHandle target, Object receiver) { Access.check(token); if (target instanceof AdapterMethodHandle) { Object info = MethodHandleNatives.getTargetInfo(target); if (info instanceof DirectMethodHandle) { DirectMethodHandle dmh = (DirectMethodHandle) info; if (receiver == null || dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) target = dmh; } } if (target instanceof DirectMethodHandle) return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0); return null; // let caller try something else } /** Bind a predetermined argument to the given arbitrary method handle. * Callable only from MethodHandles. * @param token Proof that the caller has access to this package. * @param target Any method handle. * @param receiver Argument (which can be a boxed primitive) to pre-bind. * @return a suitable BoundMethodHandle */ public static MethodHandle bindArgument(Access token, MethodHandle target, int argnum, Object receiver) { Access.check(token); throw new UnsupportedOperationException("NYI"); } public static MethodHandle convertArguments(Access token, MethodHandle target, MethodType newType, MethodType oldType, int[] permutationOrNull) { Access.check(token); MethodHandle res = AdapterMethodHandle.makePairwiseConvert(token, newType, target); if (res != null) return res; int argc = oldType.parameterCount(); // The JVM can't do it directly, so fill in the gap with a Java adapter. // TO DO: figure out what to put here from case-by-case experience // Use a heavier method: Convert all the arguments to Object, // then back to the desired types. We might have to use Java-based // method handles to do this. MethodType objType = MethodType.makeGeneric(argc); MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(token, objType, target); if (objTarget == null) objTarget = FromGeneric.make(target); res = AdapterMethodHandle.makePairwiseConvert(token, newType, objTarget); if (res != null) return res; return ToGeneric.make(newType, objTarget); } public static MethodHandle spreadArguments(Access token, MethodHandle target, MethodType newType, int spreadArg) { Access.check(token); // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[] MethodType oldType = target.type(); // spread the last argument of newType to oldType int spreadCount = oldType.parameterCount() - spreadArg; Class