Thu, 31 Aug 2017 15:30:47 +0800
merge
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.internal.lookup;
28 import java.io.ByteArrayOutputStream;
29 import java.io.PrintStream;
30 import java.lang.invoke.MethodHandle;
31 import java.lang.invoke.MethodHandles;
32 import java.lang.invoke.MethodType;
33 import java.lang.invoke.SwitchPoint;
34 import java.lang.reflect.Method;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.logging.Level;
39 import jdk.nashorn.internal.runtime.ConsString;
40 import jdk.nashorn.internal.runtime.Debug;
41 import jdk.nashorn.internal.runtime.DebugLogger;
42 import jdk.nashorn.internal.runtime.ScriptObject;
43 import jdk.nashorn.internal.runtime.options.Options;
45 /**
46 * This class is abstraction for all method handle, switchpoint and method type
47 * operations. This enables the functionality interface to be subclassed and
48 * intrumensted, as it has been proven vital to keep the number of method
49 * handles in the system down.
50 *
51 * All operations of the above type should go through this class, and not
52 * directly into java.lang.invoke
53 *
54 */
55 public final class MethodHandleFactory {
57 private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
58 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
60 private static final Level TRACE_LEVEL = Level.INFO;
62 private MethodHandleFactory() {
63 }
65 /**
66 * Runtime exception that collects every reason that a method handle lookup operation can go wrong
67 */
68 @SuppressWarnings("serial")
69 public static class LookupException extends RuntimeException {
70 /**
71 * Constructor
72 * @param e causing exception
73 */
74 public LookupException(final Exception e) {
75 super(e);
76 }
77 }
79 /**
80 * Helper function that takes a class or an object with a toString override
81 * and shortens it to notation after last dot. This is used to facilitiate
82 * pretty printouts in various debug loggers - internal only
83 *
84 * @param obj class or object
85 *
86 * @return pretty version of object as string
87 */
88 public static String stripName(final Object obj) {
89 if (obj == null) {
90 return "null";
91 }
93 if (obj instanceof Class) {
94 return ((Class<?>)obj).getSimpleName();
95 }
96 return obj.toString();
97 }
99 private static final MethodHandleFunctionality STANDARD = new StandardMethodHandleFunctionality();
100 private static final MethodHandleFunctionality FUNC;
102 private static final String DEBUG_PROPERTY = "nashorn.methodhandles.debug";
103 private static final DebugLogger LOG = new DebugLogger("methodhandles", DEBUG_PROPERTY);
105 static {
106 if (LOG.isEnabled() || Options.getBooleanProperty(DEBUG_PROPERTY)) {
107 if (Options.getStringProperty(DEBUG_PROPERTY, "").equals("create")) {
108 FUNC = new TraceCreateMethodHandleFunctionality();
109 } else {
110 FUNC = new TraceMethodHandleFunctionality();
111 }
112 } else {
113 FUNC = STANDARD;
114 }
115 }
117 private static final boolean PRINT_STACKTRACE = Options.getBooleanProperty("nashorn.methodhandles.debug.stacktrace");
120 /**
121 * Return the method handle functionality used for all method handle operations
122 * @return a method handle functionality implementation
123 */
124 public static MethodHandleFunctionality getFunctionality() {
125 return FUNC;
126 }
128 private static final MethodHandle TRACE = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceArgs", MethodType.methodType(void.class, DebugLogger.class, String.class, int.class, Object[].class));
129 private static final MethodHandle TRACE_RETURN = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturn", MethodType.methodType(Object.class, DebugLogger.class, Object.class));
131 /**
132 * Tracer that is applied before a value is returned from the traced function. It will output the return
133 * value and its class
134 *
135 * @param value return value for filter
136 * @return return value unmodified
137 */
138 static Object traceReturn(final DebugLogger logger, final Object value) {
139 final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
140 logger.log(TRACE_LEVEL, str);
141 return value;
142 }
144 /**
145 * Tracer that is applied before a function is called, printing the arguments
146 *
147 * @param tag tag to start the debug printout string
148 * @param paramStart param index to start outputting from
149 * @param args arguments to the function
150 */
151 static void traceArgs(final DebugLogger logger, final String tag, final int paramStart, final Object... args) {
152 final StringBuilder sb = new StringBuilder();
154 sb.append(tag);
156 for (int i = paramStart; i < args.length; i++) {
157 if (i == paramStart) {
158 sb.append(" => args: ");
159 }
161 sb.append('\'').
162 append(stripName(argString(args[i]))).
163 append('\'').
164 append(' ').
165 append('[').
166 append("type=").
167 append(args[i] == null ? "null" : stripName(args[i].getClass())).
168 append(']');
170 if (i + 1 < args.length) {
171 sb.append(", ");
172 }
173 }
175 assert logger != null;
176 logger.log(TRACE_LEVEL, sb);
177 stacktrace(logger);
178 }
180 private static void stacktrace(final DebugLogger logger) {
181 if (!PRINT_STACKTRACE) {
182 return;
183 }
184 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
185 final PrintStream ps = new PrintStream(baos);
186 new Throwable().printStackTrace(ps);
187 logger.log(TRACE_LEVEL, baos.toString());
188 }
190 private static String argString(final Object arg) {
191 if (arg == null) {
192 return "null";
193 }
195 if (arg.getClass().isArray()) {
196 final List<Object> list = new ArrayList<>();
197 for (final Object elem : (Object[])arg) {
198 list.add('\'' + argString(elem) + '\'');
199 }
201 return list.toString();
202 }
204 if (arg instanceof ScriptObject) {
205 return arg.toString() +
206 " (map=" + Debug.id((((ScriptObject)arg).getMap())) +
207 ")";
208 }
210 return arg.toString();
211 }
213 /**
214 * Add a debug printout to a method handle, tracing parameters and return values
215 *
216 * @param logger a specific logger to which to write the output
217 * @param mh method handle to trace
218 * @param tag start of trace message
219 * @return traced method handle
220 */
221 public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final Object tag) {
222 return addDebugPrintout(logger, mh, 0, true, tag);
223 }
226 /**
227 * Add a debug printout to a method handle, tracing parameters and return values
228 *
229 * @param logger a specific logger to which to write the output
230 * @param mh method handle to trace
231 * @param paramStart first param to print/trace
232 * @param printReturnValue should we print/trace return value if available?
233 * @param tag start of trace message
234 * @return traced method handle
235 */
236 public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
237 final MethodType type = mh.type();
239 if (logger != null && logger.levelAbove(TRACE_LEVEL)) {
240 return mh;
241 }
243 assert logger != null;
244 assert TRACE != null;
246 MethodHandle trace = MethodHandles.insertArguments(TRACE, 0, logger, tag, paramStart);
248 trace = MethodHandles.foldArguments(
249 mh,
250 trace.asCollector(
251 Object[].class,
252 type.parameterCount()).
253 asType(type.changeReturnType(void.class)));
255 final Class<?> retType = type.returnType();
256 if (retType != void.class && printReturnValue) {
257 final MethodHandle traceReturn = MethodHandles.insertArguments(TRACE_RETURN, 0, logger);
258 trace = MethodHandles.filterReturnValue(trace,
259 traceReturn.asType(
260 traceReturn.type().changeParameterType(0, retType).changeReturnType(retType)));
261 }
263 return trace;
264 }
266 /**
267 * The standard class that marshalls all method handle operations to the java.lang.invoke
268 * package. This exists only so that it can be subclassed and method handles created from
269 * Nashorn made possible to instrument.
270 *
271 * All Nashorn classes should use the MethodHandleFactory for their method handle operations
272 */
273 private static class StandardMethodHandleFunctionality implements MethodHandleFunctionality {
275 @Override
276 public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
277 return MethodHandles.filterArguments(target, pos, filters);
278 }
280 @Override
281 public MethodHandle filterReturnValue(final MethodHandle target, final MethodHandle filter) {
282 return MethodHandles.filterReturnValue(target, filter);
283 }
285 @Override
286 public MethodHandle guardWithTest(final MethodHandle test, final MethodHandle target, final MethodHandle fallback) {
287 return MethodHandles.guardWithTest(test, target, fallback);
288 }
290 @Override
291 public MethodHandle insertArguments(final MethodHandle target, final int pos, final Object... values) {
292 return MethodHandles.insertArguments(target, pos, values);
293 }
295 @Override
296 public MethodHandle dropArguments(final MethodHandle target, final int pos, final Class<?>... valueTypes) {
297 return MethodHandles.dropArguments(target, pos, valueTypes);
298 }
300 @Override
301 public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> valueTypes) {
302 return MethodHandles.dropArguments(target, pos, valueTypes);
303 }
305 @Override
306 public MethodHandle asType(final MethodHandle handle, final MethodType type) {
307 return handle.asType(type);
308 }
310 @Override
311 public MethodHandle bindTo(final MethodHandle handle, final Object x) {
312 return handle.bindTo(x);
313 }
315 @Override
316 public MethodHandle foldArguments(final MethodHandle target, final MethodHandle combiner) {
317 return MethodHandles.foldArguments(target, combiner);
318 }
320 @Override
321 public MethodHandle explicitCastArguments(final MethodHandle target, final MethodType type) {
322 return MethodHandles.explicitCastArguments(target, type);
323 }
325 @Override
326 public MethodHandle arrayElementGetter(final Class<?> type) {
327 return MethodHandles.arrayElementGetter(type);
328 }
330 @Override
331 public MethodHandle arrayElementSetter(final Class<?> type) {
332 return MethodHandles.arrayElementSetter(type);
333 }
335 @Override
336 public MethodHandle throwException(final Class<?> returnType, final Class<? extends Throwable> exType) {
337 return MethodHandles.throwException(returnType, exType);
338 }
340 @Override
341 public MethodHandle constant(final Class<?> type, final Object value) {
342 return MethodHandles.constant(type, value);
343 }
345 @Override
346 public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
347 return handle.asCollector(arrayType, arrayLength);
348 }
350 @Override
351 public MethodHandle asSpreader(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
352 return handle.asSpreader(arrayType, arrayLength);
353 }
355 @Override
356 public MethodHandle getter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
357 try {
358 return explicitLookup.findGetter(clazz, name, type);
359 } catch (final NoSuchFieldException | IllegalAccessException e) {
360 throw new LookupException(e);
361 }
362 }
364 @Override
365 public MethodHandle staticGetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
366 try {
367 return explicitLookup.findStaticGetter(clazz, name, type);
368 } catch (final NoSuchFieldException | IllegalAccessException e) {
369 throw new LookupException(e);
370 }
371 }
374 @Override
375 public MethodHandle setter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
376 try {
377 return explicitLookup.findSetter(clazz, name, type);
378 } catch (final NoSuchFieldException | IllegalAccessException e) {
379 throw new LookupException(e);
380 }
381 }
383 @Override
384 public MethodHandle staticSetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
385 try {
386 return explicitLookup.findStaticSetter(clazz, name, type);
387 } catch (final NoSuchFieldException | IllegalAccessException e) {
388 throw new LookupException(e);
389 }
390 }
392 @Override
393 public MethodHandle find(final Method method) {
394 try {
395 return PUBLIC_LOOKUP.unreflect(method);
396 } catch (final IllegalAccessException e) {
397 throw new LookupException(e);
398 }
399 }
401 @Override
402 public MethodHandle findStatic(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
403 try {
404 return explicitLookup.findStatic(clazz, name, type);
405 } catch (final NoSuchMethodException | IllegalAccessException e) {
406 throw new LookupException(e);
407 }
408 }
410 @Override
411 public MethodHandle findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
412 try {
413 return explicitLookup.findVirtual(clazz, name, type);
414 } catch (final NoSuchMethodException | IllegalAccessException e) {
415 throw new LookupException(e);
416 }
417 }
419 @Override
420 public SwitchPoint createSwitchPoint() {
421 return new SwitchPoint();
422 }
424 @Override
425 public MethodHandle guardWithTest(final SwitchPoint sp, final MethodHandle before, final MethodHandle after) {
426 return sp.guardWithTest(before, after);
427 }
429 @Override
430 public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
431 return MethodType.methodType(returnType, paramTypes);
432 }
434 }
436 /**
437 * Class used for instrumenting and debugging Nashorn generated method handles
438 */
439 private static class TraceMethodHandleFunctionality extends StandardMethodHandleFunctionality {
441 protected static String describe(final Object... data) {
442 final StringBuilder sb = new StringBuilder();
444 for (int i = 0; i < data.length; i++) {
445 final Object d = data[i];
446 if (d == null) {
447 sb.append("<null> ");
448 } else if (d instanceof String || d instanceof ConsString) {
449 sb.append(d.toString());
450 sb.append(' ');
451 } else if (d.getClass().isArray()) {
452 sb.append("[ ");
453 for (final Object da : (Object[])d) {
454 sb.append(describe(new Object[]{ da })).append(' ');
455 }
456 sb.append("] ");
457 } else {
458 sb.append(d)
459 .append('{')
460 .append(Integer.toHexString(System.identityHashCode(d)))
461 .append('}');
462 }
464 if (i + 1 < data.length) {
465 sb.append(", ");
466 }
467 }
469 return sb.toString();
470 }
472 public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
473 return addDebugPrintout(LOG, master, Integer.MAX_VALUE, false, str + ' ' + describe(args));
474 }
476 @Override
477 public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
478 final MethodHandle mh = super.filterArguments(target, pos, filters);
479 return debug(mh, "filterArguments", target, pos, filters);
480 }
482 @Override
483 public MethodHandle filterReturnValue(final MethodHandle target, final MethodHandle filter) {
484 final MethodHandle mh = super.filterReturnValue(target, filter);
485 return debug(mh, "filterReturnValue", target, filter);
486 }
488 @Override
489 public MethodHandle guardWithTest(final MethodHandle test, final MethodHandle target, final MethodHandle fallback) {
490 final MethodHandle mh = super.guardWithTest(test, target, fallback);
491 return debug(mh, "guardWithTest", test, target, fallback);
492 }
494 @Override
495 public MethodHandle insertArguments(final MethodHandle target, final int pos, final Object... values) {
496 final MethodHandle mh = super.insertArguments(target, pos, values);
497 return debug(mh, "insertArguments", target, pos, values);
498 }
500 @Override
501 public MethodHandle dropArguments(final MethodHandle target, final int pos, final Class<?>... values) {
502 final MethodHandle mh = super.dropArguments(target, pos, values);
503 return debug(mh, "dropArguments", target, pos, values);
504 }
506 @Override
507 public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> values) {
508 final MethodHandle mh = super.dropArguments(target, pos, values);
509 return debug(mh, "dropArguments", target, pos, values);
510 }
512 @Override
513 public MethodHandle asType(final MethodHandle handle, final MethodType type) {
514 final MethodHandle mh = super.asType(handle, type);
515 return debug(mh, "asType", handle, type);
516 }
518 @Override
519 public MethodHandle bindTo(final MethodHandle handle, final Object x) {
520 final MethodHandle mh = super.bindTo(handle, x);
521 return debug(mh, "bindTo", handle, x);
522 }
524 @Override
525 public MethodHandle foldArguments(final MethodHandle target, final MethodHandle combiner) {
526 final MethodHandle mh = super.foldArguments(target, combiner);
527 return debug(mh, "foldArguments", target, combiner);
528 }
530 @Override
531 public MethodHandle explicitCastArguments(final MethodHandle target, final MethodType type) {
532 final MethodHandle mh = super.explicitCastArguments(target, type);
533 return debug(mh, "explicitCastArguments", target, type);
534 }
536 @Override
537 public MethodHandle arrayElementGetter(final Class<?> type) {
538 final MethodHandle mh = super.arrayElementGetter(type);
539 return debug(mh, "arrayElementGetter", type);
540 }
542 @Override
543 public MethodHandle arrayElementSetter(final Class<?> type) {
544 final MethodHandle mh = super.arrayElementSetter(type);
545 return debug(mh, "arrayElementSetter", type);
546 }
548 @Override
549 public MethodHandle throwException(final Class<?> returnType, final Class<? extends Throwable> exType) {
550 final MethodHandle mh = super.throwException(returnType, exType);
551 return debug(mh, "throwException", returnType, exType);
552 }
554 @Override
555 public MethodHandle constant(final Class<?> type, final Object value) {
556 final MethodHandle mh = super.constant(type, value);
557 return debug(mh, "constant", type, value);
558 }
560 @Override
561 public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
562 final MethodHandle mh = super.asCollector(handle, arrayType, arrayLength);
563 return debug(mh, "asCollector", handle, arrayType, arrayLength);
564 }
566 @Override
567 public MethodHandle asSpreader(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
568 final MethodHandle mh = super.asSpreader(handle, arrayType, arrayLength);
569 return debug(mh, "asSpreader", handle, arrayType, arrayLength);
570 }
572 @Override
573 public MethodHandle getter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
574 final MethodHandle mh = super.getter(explicitLookup, clazz, name, type);
575 return debug(mh, "getter", explicitLookup, clazz, name, type);
576 }
578 @Override
579 public MethodHandle staticGetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
580 final MethodHandle mh = super.staticGetter(explicitLookup, clazz, name, type);
581 return debug(mh, "static getter", explicitLookup, clazz, name, type);
582 }
584 @Override
585 public MethodHandle setter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
586 final MethodHandle mh = super.setter(explicitLookup, clazz, name, type);
587 return debug(mh, "setter", explicitLookup, clazz, name, type);
588 }
590 @Override
591 public MethodHandle staticSetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
592 final MethodHandle mh = super.staticSetter(explicitLookup, clazz, name, type);
593 return debug(mh, "static setter", explicitLookup, clazz, name, type);
594 }
596 @Override
597 public MethodHandle find(final Method method) {
598 final MethodHandle mh = super.find(method);
599 return debug(mh, "find", method);
600 }
602 @Override
603 public MethodHandle findStatic(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
604 final MethodHandle mh = super.findStatic(explicitLookup, clazz, name, type);
605 return debug(mh, "findStatic", explicitLookup, clazz, name, type);
606 }
608 @Override
609 public MethodHandle findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
610 final MethodHandle mh = super.findVirtual(explicitLookup, clazz, name, type);
611 return debug(mh, "findVirtual", explicitLookup, clazz, name, type);
612 }
614 @Override
615 public SwitchPoint createSwitchPoint() {
616 final SwitchPoint sp = super.createSwitchPoint();
617 LOG.log(TRACE_LEVEL, "createSwitchPoint ", sp);
618 return sp;
619 }
621 @Override
622 public MethodHandle guardWithTest(final SwitchPoint sp, final MethodHandle before, final MethodHandle after) {
623 final MethodHandle mh = super.guardWithTest(sp, before, after);
624 return debug(mh, "guardWithTest", sp, before, after);
625 }
627 @Override
628 public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
629 final MethodType mt = super.type(returnType, paramTypes);
630 LOG.log(TRACE_LEVEL, "methodType ", returnType, " ", Arrays.toString(paramTypes), " ", mt);
631 return mt;
632 }
633 }
635 /**
636 * Class used for debugging Nashorn generated method handles
637 */
638 private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
639 @Override
640 public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
641 LOG.log(TRACE_LEVEL, str, " ", describe(args));
642 stacktrace(LOG);
643 return master;
644 }
645 }
646 }