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.util;
27
28 import impl.java.dyn.Access;
29 import impl.java.dyn.AdapterMethodHandle;
30 import java.dyn.AnonymousClassLoader;
31 import java.dyn.ConstantPoolParser;
32 import java.dyn.ConstantPoolPatch;
33 import java.dyn.ConstantPoolVisitor;
34 import java.dyn.InvalidConstantPoolFormatException;
35 import java.dyn.MethodHandle;
36 import java.dyn.MethodHandles;
37 import java.dyn.MethodType;
38 import java.dyn.WrongMethodTypeException;
39 import java.io.IOException;
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.InvocationTargetException;
42 import java.util.IdentityHashMap;
43
44 /**
45 * Emulation of method handle invocation.
46 * Not needed if javac supports direct invocation of MethodHandle.invoke.
47 * @author jrose
48 */
49 public abstract
50 class MethodHandleInvoker implements Cloneable {
51 private static final Access IMPL_TOKEN = Access.getToken();
52
53
54 /** Exact type for all handles targeted by this invoker. */
55 protected final MethodType exactType;
56
57 /** Condensed information about the return type, one of "VLIJFDZBSC". */
58 protected final char rtypec;
59
60 /** Adapter which converts the approximate type to the exact exactType. */
61 protected final MethodHandle adapter;
62
63 /** Maximum number of arguments allowed.
64 * This is an implementation limit.
65 */
66 public static final int ARGUMENT_MAX;
67 /** Maximum number of double or long arguments allowed.
68 * This is an implementation limit.
69 */
70 public static final int LONG_ARGUMENT_MAX;
71 static {
72 LONG_ARGUMENT_MAX = 3; // %%% depends on stack headroom
73 }
74
75 public MethodType type() { return exactType; }
76
77 protected MethodHandleInvoker(MethodType exactType, MethodHandle adapter) {
78 this.exactType = exactType;
79 this.rtypec = Wrappers.basicTypeChar(exactType.returnType());
80 this.adapter = adapter;
81 }
82
83 static MethodHandle makeAdapter(MethodType exactType, MethodType approxType) {
84 // For each argument, convert incoming Object to the exact type needed.
85 int len = exactType.parameterCount();
86 assert(len == approxType.parameterCount());
87 if (exactType.parameterSlotCount() > len + LONG_ARGUMENT_MAX)
88 throw new IllegalArgumentException("too many long arguments in "+exactType);
89 MethodHandle invoker = MethodHandles.findVirtual(MethodHandle.class, "invoke", exactType);
90 MethodType adapterType = approxType.insertParameterType(0, MethodHandle.class);
91 return AdapterMethodHandle.makePairwiseConversion(IMPL_TOKEN, adapterType, invoker);
92 }
93
94 public Object invoke_0(MethodHandle mh)
95 { throw wrongType(mh); }
96 public Object invoke_1(MethodHandle mh, Object a0)
97 { throw wrongType(mh); }
98 public Object invoke_2(MethodHandle mh, Object a0, Object a1)
99 { throw wrongType(mh); }
100 public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2)
101 { throw wrongType(mh); }
102 public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3)
103 { throw wrongType(mh); }
104 public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4)
105 { throw wrongType(mh); }
106
107 /** Reflective style generic invocation. This always delegates
108 * to one of the invoke_X methods.
109 * @param mh method handle to invoke (must be of exactly correct type)
110 * @param args array of arguments to send to method (maybe null if empty)
111 * @return
112 */
113 final // try this...
114 public Object invoke(MethodHandle mh, Object ... args) {
115 switch (args == null ? 0 : args.length) {
116 case 0: return invoke_0(mh);
117 case 1: return invoke_1(mh, args[0]);
118 case 2: return invoke_2(mh, args[0], args[1]);
119 case 3: return invoke_3(mh, args[0], args[1], args[2]);
120 case 4: return invoke_4(mh, args[0], args[1], args[2], args[3]);
121 case 5: return invoke_5(mh, args[0], args[1], args[2], args[3], args[4]);
122 }
123 throw wrongType(mh);
124 }
125
126 // This class is not used after compile time.
127 // It is renamed away to MethodHandle itself, to call the MHI.adapter.
128 // TO DO: Update javac so we can call directly to polymorphic MH.invoke.
129 private static abstract class FakeMethodHandle extends MethodHandle {
130 public FakeMethodHandle() { super(null, null); }
131 // here are all the invokes we need to link against:
132 public abstract void fake_invoke_V0(MethodHandle mh);
133 public abstract Object fake_invoke_L0(MethodHandle mh);
134 public abstract int fake_invoke_I0(MethodHandle mh);
135 public abstract long fake_invoke_J0(MethodHandle mh);
136 public abstract double fake_invoke_F0(MethodHandle mh);
137 public abstract double fake_invoke_D0(MethodHandle mh);
138 public abstract void fake_invoke_V1(MethodHandle mh, Object a0);
139 public abstract Object fake_invoke_L1(MethodHandle mh, Object a0);
140 public abstract int fake_invoke_I1(MethodHandle mh, Object a0);
141 public abstract long fake_invoke_J1(MethodHandle mh, Object a0);
142 public abstract float fake_invoke_F1(MethodHandle mh, Object a0);
143 public abstract double fake_invoke_D1(MethodHandle mh, Object a0);
144 public abstract void fake_invoke_V2(MethodHandle mh, Object a0, Object a1);
145 public abstract Object fake_invoke_L2(MethodHandle mh, Object a0, Object a1);
146 public abstract int fake_invoke_I2(MethodHandle mh, Object a0, Object a1);
147 public abstract long fake_invoke_J2(MethodHandle mh, Object a0, Object a1);
148 public abstract float fake_invoke_F2(MethodHandle mh, Object a0, Object a1);
149 public abstract double fake_invoke_D2(MethodHandle mh, Object a0, Object a1);
150 public abstract void fake_invoke_V3(MethodHandle mh, Object a0, Object a1, Object a2);
151 public abstract Object fake_invoke_L3(MethodHandle mh, Object a0, Object a1, Object a2);
152 public abstract int fake_invoke_I3(MethodHandle mh, Object a0, Object a1, Object a2);
153 public abstract long fake_invoke_J3(MethodHandle mh, Object a0, Object a1, Object a2);
154 public abstract float fake_invoke_F3(MethodHandle mh, Object a0, Object a1, Object a2);
155 public abstract double fake_invoke_D3(MethodHandle mh, Object a0, Object a1, Object a2);
156 public abstract void fake_invoke_V4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
157 public abstract Object fake_invoke_L4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
158 public abstract int fake_invoke_I4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
159 public abstract long fake_invoke_J4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
160 public abstract float fake_invoke_F4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
161 public abstract double fake_invoke_D4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
162 public abstract void fake_invoke_V5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
163 public abstract Object fake_invoke_L5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
164 public abstract int fake_invoke_I5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
165 public abstract long fake_invoke_J5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
166 public abstract float fake_invoke_F5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
167 public abstract double fake_invoke_D5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
168 }
169 private static String FMHInvokeName(MethodType approxType) {
170 assert(isFMHInvokeType(approxType)) : approxType;
171 return "fake_invoke_"
172 + Wrappers.basicTypeChar(approxType.returnType())
173 + approxType.parameterCount();
174 }
175 private static boolean isFMHInvokeType(MethodType approxType) {
176 Class<?> rtype = approxType.returnType();
177 char rtc = Wrappers.basicTypeChar(approxType.returnType());
178 if ("VIJFD".indexOf(rtc) < 0 && rtype != Object.class)
179 return false;
180 int len = approxType.parameterCount();
181 for (int i = 0; i < len; i++)
182 if (approxType.parameterType(i) != Object.class)
183 return false;
184 return true;
185 }
186
187 protected Object wrap(int value) {
188 switch (rtypec) {
189 case 'Z': return (value != 0);
190 case 'B': return (byte)value;
191 case 'S': return (short)value;
192 case 'C': return (char)value;
193 }
194 return value;
195 }
196
197 static class L0 extends MethodHandleInvoker {
198 public L0(MethodType type, MethodHandle adapter) { super(type, adapter); }
199 @Override public Object invoke_0(MethodHandle mh) {
200 checkType(mh);
201 switch (rtypec) {
202 // Note: Could unswitch this into 5 classes, but too messy.
203 case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L0(mh);
204 default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I0(mh));
205 case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J0(mh);
206 case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F0(mh);
207 case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D0(mh);
208 case 'V': ((FakeMethodHandle)adapter).fake_invoke_V0(mh);
209 /* Here is the sort of code we will use when we get javac support:
210 case 'L': return adapter.invoke(mh);
211 default: return wrap(adapter.<int>invoke(mh));
212 case 'J': return adapter.<long>invoke(mh);
213 case 'F': return adapter.<float>invoke(mh);
214 case 'D': return adapter.<double>invoke(mh);
215 case 'V': adapter.<void>invoke(mh);
216 */
217 }
218 return null;
219 }
220 }
221
222 static class L1 extends MethodHandleInvoker {
223 public L1(MethodType type, MethodHandle adapter) { super(type, adapter); }
224 @Override public Object invoke_1(MethodHandle mh, Object a0) {
225 checkType(mh);
226 switch (rtypec) {
227 case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L1(mh, a0);
228 default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I1(mh, a0));
229 case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J1(mh, a0);
230 case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F1(mh, a0);
231 case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D1(mh, a0);
232 case 'V': ((FakeMethodHandle)adapter).fake_invoke_V1(mh, a0);
233 }
234 return null;
235 }
236 }
237
238 static class L2 extends MethodHandleInvoker {
239 public L2(MethodType type, MethodHandle adapter) { super(type, adapter); }
240 @Override public Object invoke_2(MethodHandle mh, Object a0, Object a1) {
241 checkType(mh);
242 switch (rtypec) {
243 case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L2(mh, a0, a1);
244 default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I2(mh, a0, a1));
245 case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J2(mh, a0, a1);
246 case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F2(mh, a0, a1);
247 case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D2(mh, a0, a1);
248 case 'V': ((FakeMethodHandle)adapter).fake_invoke_V2(mh, a0, a1);
249 }
250 return null;
251 }
252 }
253
254 static class L3 extends MethodHandleInvoker {
255 public L3(MethodType type, MethodHandle adapter) { super(type, adapter); }
256 @Override public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2) {
257 checkType(mh);
258 switch (rtypec) {
259 case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L3(mh, a0, a1, a2);
260 default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I3(mh, a0, a1, a2));
261 case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J3(mh, a0, a1, a2);
262 case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F3(mh, a0, a1, a2);
263 case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D3(mh, a0, a1, a2);
264 case 'V': ((FakeMethodHandle)adapter).fake_invoke_V3(mh, a0, a1, a2);
265 }
266 return null;
267 }
268 }
269
270 static class L4 extends MethodHandleInvoker {
271 public L4(MethodType type, MethodHandle adapter) { super(type, adapter); }
272 @Override public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3) {
273 checkType(mh);
274 switch (rtypec) {
275 case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L4(mh, a0, a1, a2, a3);
276 default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I4(mh, a0, a1, a2, a3));
277 case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J4(mh, a0, a1, a2, a3);
278 case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F4(mh, a0, a1, a2, a3);
279 case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D4(mh, a0, a1, a2, a3);
280 case 'V': ((FakeMethodHandle)adapter).fake_invoke_V4(mh, a0, a1, a2, a3);
281 }
282 return null;
283 }
284 }
285
286 static class L5 extends MethodHandleInvoker {
287 public L5(MethodType type, MethodHandle adapter) { super(type, adapter); }
288 @Override public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4) {
289 checkType(mh);
290 switch (rtypec) {
291 case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L5(mh, a0, a1, a2, a3, a4);
292 default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I5(mh, a0, a1, a2, a3, a4));
293 case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J5(mh, a0, a1, a2, a3, a4);
294 case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F5(mh, a0, a1, a2, a3, a4);
295 case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D5(mh, a0, a1, a2, a3, a4);
296 case 'V': ((FakeMethodHandle)adapter).fake_invoke_V5(mh, a0, a1, a2, a3, a4);
297 }
298 return null;
299 }
300 }
301
302 private static Class<?>[] L_CLASSES
303 = { L0.class, L1.class, L2.class, L3.class, L4.class, L5.class };
304 static { ARGUMENT_MAX = L_CLASSES.length + 1; }
305
306 public static MethodHandleInvoker make(MethodType type) {
307 MethodHandleInvoker inv = null;
308 synchronized (invokers) {
309 inv = invokers.get(type);
310 }
311 if (inv != null) return inv;
312 inv = makeNew(type);
313 synchronized (invokers) {
314 MethodHandleInvoker inv2 = invokers.get(type);
315 if (inv2 == null)
316 invokers.put(type, inv);
317 else
318 inv = inv2;
319 }
320 System.out.println("new invoker: "+inv);
321 return inv;
322 }
323
324 static MethodType approxType(MethodType exactType) {
325 int len = exactType.parameterCount();
326 if (len > ARGUMENT_MAX)
327 throw new IllegalArgumentException("too many arguments for invoker: "+exactType);
328 // The JVM can insert casts and unboxing for us in a native adapter.
329 MethodType approxType = MethodType.makeGeneric(len);
330 // But the return type must be exact, except for subword types.
331 // Convert subwords to int, since the JVM an narrow them back down.
332 Class<?> rtype = exactType.returnType();
333 switch (Wrappers.basicTypeChar(rtype)) {
334 case 'L':
335 rtype = Object.class; break;
336 case 'Z': case 'B': case 'S': case 'C':
337 rtype = int.class; break;
338 }
339 approxType = approxType.changeReturnType(rtype);
340 return approxType;
341 }
342
343 private static MethodHandleInvoker makeNew(MethodType exactType) {
344 MethodHandleInvoker inv = null;
345 Exception ex1 = null;
346 MethodType approxType = approxType(exactType);
347 MethodHandle adapter = makeAdapter(exactType, approxType);
348 Class<? extends MethodHandleInvoker> template = null;
349 Class<? extends MethodHandleInvoker> instance = null;
350 Constructor<? extends MethodHandleInvoker> constr = null;
351 template = L_CLASSES[approxType.parameterCount()].asSubclass(MethodHandleInvoker.class);
352 {
353 try {
354 instance = expandTemplate(template, approxType, exactType);
355 // When we get rid of the fakery, it will be just
356 // constr = template.getConstructor
357 constr = instance.getConstructor(MethodType.class, MethodHandle.class);
358 inv = constr.newInstance(exactType, adapter);
359 } catch (IOException ex) {
360 ex1 = ex;
361 } catch (InvalidConstantPoolFormatException ex) {
362 ex1 = ex;
363 } catch (InstantiationException ex) {
364 ex1 = ex;
365 } catch (IllegalAccessException ex) {
366 ex1 = ex;
367 } catch (NoSuchMethodException ex) {
368 ex1 = ex;
369 } catch (IllegalArgumentException ex) {
370 ex1 = ex;
371 } catch (InvocationTargetException ex) {
372 ex1 = ex;
373 }
374 }
375 if (inv == null) {
376 printex(ex1);
377 throw new InternalError();
378 }
379 return inv;
380 }
381 private static void printex(Exception ex) {
382 System.out.println("*** Unexpected exception in "+MethodHandleInvoker.class);
383 System.out.println(ex);
384 ex.printStackTrace(System.out);
385 }
386
387 private static final AnonymousClassLoader LOADER
388 = new AnonymousClassLoader(MethodHandleInvoker.class);
389
390 private static String utf8Name(Class<?> cls) {
391 return cls.getName().replace('.', '/');
392 }
393
394 private static class TemplateExpander extends ConstantPoolVisitor {
395 ConstantPoolParser cp;
396 ConstantPoolPatch patch;
397
398 // Pairs of strings to be rewritten:
399 String fakeMHName = utf8Name(FakeMethodHandle.class);
400 String realMHName = utf8Name(MethodHandle.class);
401 boolean didMHName;
402
403 String fakeInvokeName, realInvokeName = "invoke";
404 boolean didInvokeName;
405
406 @Override
407 public void visitUTF8(int index, byte tag, String utf8) {
408 String orig = utf8;
409 if (utf8.equals(fakeMHName)) {
410 utf8 = realMHName; didMHName = true;
411 }
412 if (utf8.equals(fakeInvokeName)) {
413 utf8 = realInvokeName; didInvokeName = true;
414 }
415 if ((Object)utf8 != orig)
416 patch.putUTF8(index, utf8);
417 }
418
419 public TemplateExpander(Class<? extends MethodHandleInvoker> template,
420 MethodType approxType, MethodType exactType)
421 throws IOException, InvalidConstantPoolFormatException {
422 // construct a descriptor of something like:
423 // int fake_invoke_I2(MethodHandle, Object, Object);
424 fakeInvokeName = FMHInvokeName(approxType);
425 cp = new ConstantPoolParser(template);
426 patch = cp.createPatch();
427 cp.parse(this);
428 if (!(didMHName && didInvokeName))
429 throw new RuntimeException("utf8 rewrites failed: "
430 +(!didMHName?"":fakeMHName)+(!didInvokeName?"":fakeInvokeName));
431 }
432 }
433
434 static final IdentityHashMap<MethodType,MethodHandleInvoker> invokers
435 = new IdentityHashMap<MethodType, MethodHandleInvoker>();
436
437 private static Class<? extends MethodHandleInvoker>
438 expandTemplate(Class<? extends MethodHandleInvoker> template,
439 MethodType approxType, MethodType exactType)
440 throws IOException, InvalidConstantPoolFormatException {
441 TemplateExpander tex = new TemplateExpander(template, approxType, exactType);
442 return LOADER.loadClass(tex.patch).asSubclass(MethodHandleInvoker.class);
443 }
444
445 /** Throw this if a bad entry point is taken. */
446 protected RuntimeException wrongType(MethodHandle mh) {
447 return new WrongMethodTypeException("wrong call type for "+mh+
448 " should be "+exactType+" in "+this);
449 }
450 protected void checkType(MethodHandle mh) {
451 if (mh.type() != exactType)
452 throw wrongType(mh);
453 }
454 }