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 }