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  */
25 
26 package jdk.nashorn.internal.lookup;
27 
28 import static jdk.nashorn.internal.runtime.JSType.isString;
29 
30 import java.io.ByteArrayOutputStream;
31 import java.io.PrintStream;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.invoke.SwitchPoint;
36 import java.lang.reflect.Method;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.logging.Level;
41 import jdk.nashorn.internal.runtime.Context;
42 import jdk.nashorn.internal.runtime.Debug;
43 import jdk.nashorn.internal.runtime.ScriptObject;
44 import jdk.nashorn.internal.runtime.logging.DebugLogger;
45 import jdk.nashorn.internal.runtime.logging.Loggable;
46 import jdk.nashorn.internal.runtime.logging.Logger;
47 import jdk.nashorn.internal.runtime.options.Options;
48 
49 /**
50  * This class is abstraction for all method handle, switchpoint and method type
51  * operations. This enables the functionality interface to be subclassed and
52  * instrumented, as it has been proven vital to keep the number of method
53  * handles in the system down.
54  *
55  * All operations of the above type should go through this class, and not
56  * directly into java.lang.invoke
57  *
58  */
59 public final class MethodHandleFactory {
60 
61     private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
62     private static final MethodHandles.Lookup LOOKUP        = MethodHandles.lookup();
63 
64     private static final Level TRACE_LEVEL = Level.INFO;
65 
MethodHandleFactory()66     private MethodHandleFactory() {
67     }
68 
69     /**
70      * Runtime exception that collects every reason that a method handle lookup operation can go wrong
71      */
72     @SuppressWarnings("serial")
73     public static class LookupException extends RuntimeException {
74         /**
75          * Constructor
76          * @param e causing exception
77          */
LookupException(final Exception e)78         public LookupException(final Exception e) {
79             super(e);
80         }
81     }
82 
83     /**
84      * Helper function that takes a class or an object with a toString override
85      * and shortens it to notation after last dot. This is used to facilitiate
86      * pretty printouts in various debug loggers - internal only
87      *
88      * @param obj class or object
89      *
90      * @return pretty version of object as string
91      */
stripName(final Object obj)92     public static String stripName(final Object obj) {
93         if (obj == null) {
94             return "null";
95         }
96 
97         if (obj instanceof Class) {
98             return ((Class<?>)obj).getSimpleName();
99         }
100         return obj.toString();
101     }
102 
103     private static final MethodHandleFunctionality FUNC = new StandardMethodHandleFunctionality();
104     private static final boolean PRINT_STACKTRACE = Options.getBooleanProperty("nashorn.methodhandles.debug.stacktrace");
105 
106     /**
107      * Return the method handle functionality used for all method handle operations
108      * @return a method handle functionality implementation
109      */
getFunctionality()110     public static MethodHandleFunctionality getFunctionality() {
111         return FUNC;
112     }
113 
114     private static final MethodHandle TRACE             = FUNC.findStatic(LOOKUP, MethodHandleFactory.class, "traceArgs",   MethodType.methodType(void.class, DebugLogger.class, String.class, int.class, Object[].class));
115     private static final MethodHandle TRACE_RETURN      = FUNC.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturn", MethodType.methodType(Object.class, DebugLogger.class, Object.class));
116     private static final MethodHandle TRACE_RETURN_VOID = FUNC.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturnVoid", MethodType.methodType(void.class, DebugLogger.class));
117 
118     private static final String VOID_TAG = "[VOID]";
119 
err(final String str)120     private static void err(final String str) {
121         Context.getContext().getErr().println(str);
122     }
123 
124     /**
125      * Tracer that is applied before a value is returned from the traced function. It will output the return
126      * value and its class
127      *
128      * @param value return value for filter
129      * @return return value unmodified
130      */
traceReturn(final DebugLogger logger, final Object value)131     static Object traceReturn(final DebugLogger logger, final Object value) {
132         final String str = "    return" +
133                 (VOID_TAG.equals(value) ?
134                         ";" :
135                             " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']'));
136         if (logger == null) {
137             err(str);
138         } else if (logger.isEnabled()) {
139             logger.log(TRACE_LEVEL, str);
140         }
141 
142         return value;
143     }
144 
traceReturnVoid(final DebugLogger logger)145     static void traceReturnVoid(final DebugLogger logger) {
146         traceReturn(logger, VOID_TAG);
147     }
148 
149     /**
150      * Tracer that is applied before a function is called, printing the arguments
151      *
152      * @param tag  tag to start the debug printout string
153      * @param paramStart param index to start outputting from
154      * @param args arguments to the function
155      */
traceArgs(final DebugLogger logger, final String tag, final int paramStart, final Object... args)156     static void traceArgs(final DebugLogger logger, final String tag, final int paramStart, final Object... args) {
157         final StringBuilder sb = new StringBuilder();
158 
159         sb.append(tag);
160 
161         for (int i = paramStart; i < args.length; i++) {
162             if (i == paramStart) {
163                 sb.append(" => args: ");
164             }
165 
166             sb.append('\'').
167             append(stripName(argString(args[i]))).
168             append('\'').
169             append(' ').
170             append('[').
171             append("type=").
172             append(args[i] == null ? "null" : stripName(args[i].getClass())).
173             append(']');
174 
175             if (i + 1 < args.length) {
176                 sb.append(", ");
177             }
178         }
179 
180         if (logger == null) {
181             err(sb.toString());
182         } else {
183             logger.log(TRACE_LEVEL, sb);
184         }
185         stacktrace(logger);
186     }
187 
stacktrace(final DebugLogger logger)188     private static void stacktrace(final DebugLogger logger) {
189         if (!PRINT_STACKTRACE) {
190             return;
191         }
192         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
193         final PrintStream ps = new PrintStream(baos);
194         new Throwable().printStackTrace(ps);
195         final String st = baos.toString();
196         if (logger == null) {
197             err(st);
198         } else {
199             logger.log(TRACE_LEVEL, st);
200         }
201     }
202 
argString(final Object arg)203     private static String argString(final Object arg) {
204         if (arg == null) {
205             return "null";
206         }
207 
208         if (arg.getClass().isArray()) {
209             final List<Object> list = new ArrayList<>();
210             for (final Object elem : (Object[])arg) {
211                 list.add('\'' + argString(elem) + '\'');
212             }
213 
214             return list.toString();
215         }
216 
217         if (arg instanceof ScriptObject) {
218             return arg.toString() +
219                     " (map=" + Debug.id(((ScriptObject)arg).getMap()) +
220                     ')';
221         }
222 
223         return arg.toString();
224     }
225 
226     /**
227      * Add a debug printout to a method handle, tracing parameters and return values
228      * Output will be unconditional to stderr
229      *
230      * @param mh  method handle to trace
231      * @param tag start of trace message
232      * @return traced method handle
233      */
addDebugPrintout(final MethodHandle mh, final Object tag)234     public static MethodHandle addDebugPrintout(final MethodHandle mh, final Object tag) {
235         return addDebugPrintout(null, Level.OFF, mh, 0, true, tag);
236     }
237 
238     /**
239      * Add a debug printout to a method handle, tracing parameters and return values
240      *
241      * @param logger a specific logger to which to write the output
242      * @param level level over which to print
243      * @param mh  method handle to trace
244      * @param tag start of trace message
245      * @return traced method handle
246      */
addDebugPrintout(final DebugLogger logger, final Level level, final MethodHandle mh, final Object tag)247     public static MethodHandle addDebugPrintout(final DebugLogger logger, final Level level, final MethodHandle mh, final Object tag) {
248         return addDebugPrintout(logger, level, mh, 0, true, tag);
249     }
250 
251     /**
252      * Add a debug printout to a method handle, tracing parameters and return values
253      * Output will be unconditional to stderr
254      *
255      * @param mh  method handle to trace
256      * @param paramStart first param to print/trace
257      * @param printReturnValue should we print/trace return value if available?
258      * @param tag start of trace message
259      * @return  traced method handle
260      */
addDebugPrintout(final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag)261     public static MethodHandle addDebugPrintout(final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
262         return addDebugPrintout(null, Level.OFF, mh, paramStart, printReturnValue, tag);
263     }
264 
265     /**
266      * Add a debug printout to a method handle, tracing parameters and return values
267      *
268      * @param logger a specific logger to which to write the output
269      * @param level level over which to print
270      * @param mh  method handle to trace
271      * @param paramStart first param to print/trace
272      * @param printReturnValue should we print/trace return value if available?
273      * @param tag start of trace message
274      * @return  traced method handle
275      */
addDebugPrintout(final DebugLogger logger, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag)276     public static MethodHandle addDebugPrintout(final DebugLogger logger, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
277         final MethodType type = mh.type();
278 
279         //if there is no logger, or if it's set to log only coarser events
280         //than the trace level, skip and return
281         if (logger == null || !logger.isLoggable(level)) {
282             return mh;
283         }
284 
285         assert TRACE != null;
286 
287         MethodHandle trace = MethodHandles.insertArguments(TRACE, 0, logger, tag, paramStart);
288 
289         trace = MethodHandles.foldArguments(
290                 mh,
291                 trace.asCollector(
292                         Object[].class,
293                         type.parameterCount()).
294                         asType(type.changeReturnType(void.class)));
295 
296         final Class<?> retType = type.returnType();
297         if (printReturnValue) {
298             if (retType != void.class) {
299                 final MethodHandle traceReturn = MethodHandles.insertArguments(TRACE_RETURN, 0, logger);
300                 trace = MethodHandles.filterReturnValue(trace,
301                         traceReturn.asType(
302                                 traceReturn.type().changeParameterType(0, retType).changeReturnType(retType)));
303             } else {
304                 trace = MethodHandles.filterReturnValue(trace, MethodHandles.insertArguments(TRACE_RETURN_VOID, 0, logger));
305             }
306         }
307 
308         return trace;
309     }
310 
311     /**
312      * Class that marshalls all method handle operations to the java.lang.invoke
313      * package. This exists only so that it can be subclassed and method handles created from
314      * Nashorn made possible to instrument.
315      *
316      * All Nashorn classes should use the MethodHandleFactory for their method handle operations
317      */
318     @Logger(name="methodhandles")
319     private static class StandardMethodHandleFunctionality implements MethodHandleFunctionality, Loggable {
320 
321         // for bootstrapping reasons, because a lot of static fields use MH for lookups, we
322         // need to set the logger when the Global object is finished. This means that we don't
323         // get instrumentation for public static final MethodHandle SOMETHING = MH... in the builtin
324         // classes, but that doesn't matter, because this is usually not where we want it
325         private DebugLogger log = DebugLogger.DISABLED_LOGGER;
326 
StandardMethodHandleFunctionality()327         public StandardMethodHandleFunctionality() {
328         }
329 
330         @Override
initLogger(final Context context)331         public DebugLogger initLogger(final Context context) {
332             return this.log = context.getLogger(this.getClass());
333         }
334 
335         @Override
getLogger()336         public DebugLogger getLogger() {
337             return log;
338         }
339 
describe(final Object... data)340         protected static String describe(final Object... data) {
341             final StringBuilder sb = new StringBuilder();
342 
343             for (int i = 0; i < data.length; i++) {
344                 final Object d = data[i];
345                 if (d == null) {
346                     sb.append("<null> ");
347                 } else if (isString(d)) {
348                     sb.append(d.toString());
349                     sb.append(' ');
350                 } else if (d.getClass().isArray()) {
351                     sb.append("[ ");
352                     for (final Object da : (Object[])d) {
353                         sb.append(describe(new Object[]{ da })).append(' ');
354                     }
355                     sb.append("] ");
356                 } else {
357                     sb.append(d)
358                     .append('{')
359                     .append(Integer.toHexString(System.identityHashCode(d)))
360                     .append('}');
361                 }
362 
363                 if (i + 1 < data.length) {
364                     sb.append(", ");
365                 }
366             }
367 
368             return sb.toString();
369         }
370 
debug(final MethodHandle master, final String str, final Object... args)371         public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
372             if (log.isEnabled()) {
373                 if (PRINT_STACKTRACE) {
374                     stacktrace(log);
375                 }
376                 return addDebugPrintout(log, Level.INFO, master, Integer.MAX_VALUE, false, str + ' ' + describe(args));
377             }
378             return master;
379         }
380 
381         @Override
filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters)382         public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
383             final MethodHandle mh = MethodHandles.filterArguments(target, pos, filters);
384             return debug(mh, "filterArguments", target, pos, filters);
385         }
386 
387         @Override
filterReturnValue(final MethodHandle target, final MethodHandle filter)388         public MethodHandle filterReturnValue(final MethodHandle target, final MethodHandle filter) {
389             final MethodHandle mh = MethodHandles.filterReturnValue(target, filter);
390             return debug(mh, "filterReturnValue", target, filter);
391         }
392 
393         @Override
guardWithTest(final MethodHandle test, final MethodHandle target, final MethodHandle fallback)394         public MethodHandle guardWithTest(final MethodHandle test, final MethodHandle target, final MethodHandle fallback) {
395             final MethodHandle mh = MethodHandles.guardWithTest(test, target, fallback);
396             return debug(mh, "guardWithTest", test, target, fallback);
397         }
398 
399         @Override
insertArguments(final MethodHandle target, final int pos, final Object... values)400         public MethodHandle insertArguments(final MethodHandle target, final int pos, final Object... values) {
401             final MethodHandle mh = MethodHandles.insertArguments(target, pos, values);
402             return debug(mh, "insertArguments", target, pos, values);
403         }
404 
405         @Override
dropArguments(final MethodHandle target, final int pos, final Class<?>... values)406         public MethodHandle dropArguments(final MethodHandle target, final int pos, final Class<?>... values) {
407             final MethodHandle mh = MethodHandles.dropArguments(target, pos, values);
408             return debug(mh, "dropArguments", target, pos, values);
409         }
410 
411         @Override
dropArguments(final MethodHandle target, final int pos, final List<Class<?>> values)412         public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> values) {
413             final MethodHandle mh = MethodHandles.dropArguments(target, pos, values);
414             return debug(mh, "dropArguments", target, pos, values);
415         }
416 
417         @Override
asType(final MethodHandle handle, final MethodType type)418         public MethodHandle asType(final MethodHandle handle, final MethodType type) {
419             final MethodHandle mh = handle.asType(type);
420             return debug(mh, "asType", handle, type);
421         }
422 
423         @Override
bindTo(final MethodHandle handle, final Object x)424         public MethodHandle bindTo(final MethodHandle handle, final Object x) {
425             final MethodHandle mh = handle.bindTo(x);
426             return debug(mh, "bindTo", handle, x);
427         }
428 
429         @Override
foldArguments(final MethodHandle target, final MethodHandle combiner)430         public MethodHandle foldArguments(final MethodHandle target, final MethodHandle combiner) {
431             final MethodHandle mh = MethodHandles.foldArguments(target, combiner);
432             return debug(mh, "foldArguments", target, combiner);
433         }
434 
435         @Override
explicitCastArguments(final MethodHandle target, final MethodType type)436         public MethodHandle explicitCastArguments(final MethodHandle target, final MethodType type) {
437             final MethodHandle mh = MethodHandles.explicitCastArguments(target, type);
438             return debug(mh, "explicitCastArguments", target, type);
439         }
440 
441         @Override
arrayElementGetter(final Class<?> type)442         public MethodHandle arrayElementGetter(final Class<?> type) {
443             final MethodHandle mh = MethodHandles.arrayElementGetter(type);
444             return debug(mh, "arrayElementGetter", type);
445         }
446 
447         @Override
arrayElementSetter(final Class<?> type)448         public MethodHandle arrayElementSetter(final Class<?> type) {
449             final MethodHandle mh = MethodHandles.arrayElementSetter(type);
450             return debug(mh, "arrayElementSetter", type);
451         }
452 
453         @Override
throwException(final Class<?> returnType, final Class<? extends Throwable> exType)454         public MethodHandle throwException(final Class<?> returnType, final Class<? extends Throwable> exType) {
455             final MethodHandle mh = MethodHandles.throwException(returnType, exType);
456             return debug(mh, "throwException", returnType, exType);
457         }
458 
459         @Override
catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler)460         public MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
461             final MethodHandle mh = MethodHandles.catchException(target, exType, handler);
462             return debug(mh, "catchException", exType);
463         }
464 
465         @Override
constant(final Class<?> type, final Object value)466         public MethodHandle constant(final Class<?> type, final Object value) {
467             final MethodHandle mh = MethodHandles.constant(type, value);
468             return debug(mh, "constant", type, value);
469         }
470 
471         @Override
identity(final Class<?> type)472         public MethodHandle identity(final Class<?> type) {
473             final MethodHandle mh = MethodHandles.identity(type);
474             return debug(mh, "identity", type);
475         }
476 
477         @Override
asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength)478         public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
479             final MethodHandle mh = handle.asCollector(arrayType, arrayLength);
480             return debug(mh, "asCollector", handle, arrayType, arrayLength);
481         }
482 
483         @Override
asSpreader(final MethodHandle handle, final Class<?> arrayType, final int arrayLength)484         public MethodHandle asSpreader(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
485             final MethodHandle mh = handle.asSpreader(arrayType, arrayLength);
486             return debug(mh, "asSpreader", handle, arrayType, arrayLength);
487         }
488 
489         @Override
getter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type)490         public MethodHandle getter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
491             try {
492                 final MethodHandle mh = explicitLookup.findGetter(clazz, name, type);
493                 return debug(mh, "getter", explicitLookup, clazz, name, type);
494             } catch (final NoSuchFieldException | IllegalAccessException e) {
495                 throw new LookupException(e);
496             }
497         }
498 
499         @Override
staticGetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type)500         public MethodHandle staticGetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
501             try {
502                 final MethodHandle mh = explicitLookup.findStaticGetter(clazz, name, type);
503                 return debug(mh, "static getter", explicitLookup, clazz, name, type);
504             } catch (final NoSuchFieldException | IllegalAccessException e) {
505                 throw new LookupException(e);
506             }
507         }
508 
509         @Override
setter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type)510         public MethodHandle setter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
511             try {
512                 final MethodHandle mh = explicitLookup.findSetter(clazz, name, type);
513                 return debug(mh, "setter", explicitLookup, clazz, name, type);
514             } catch (final NoSuchFieldException | IllegalAccessException e) {
515                 throw new LookupException(e);
516             }
517         }
518 
519         @Override
staticSetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type)520         public MethodHandle staticSetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
521             try {
522                 final MethodHandle mh = explicitLookup.findStaticSetter(clazz, name, type);
523                 return debug(mh, "static setter", explicitLookup, clazz, name, type);
524             } catch (final NoSuchFieldException | IllegalAccessException e) {
525                 throw new LookupException(e);
526             }
527         }
528 
529         @Override
find(final Method method)530         public MethodHandle find(final Method method) {
531             try {
532                 final MethodHandle mh = PUBLIC_LOOKUP.unreflect(method);
533                 return debug(mh, "find", method);
534             } catch (final IllegalAccessException e) {
535                 throw new LookupException(e);
536             }
537         }
538 
539         @Override
findStatic(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type)540         public MethodHandle findStatic(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
541             try {
542                 final MethodHandle mh = explicitLookup.findStatic(clazz, name, type);
543                 return debug(mh, "findStatic", explicitLookup, clazz, name, type);
544             } catch (final NoSuchMethodException | IllegalAccessException e) {
545                 throw new LookupException(e);
546             }
547         }
548 
549         @Override
findSpecial(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type, final Class<?> thisClass)550         public MethodHandle findSpecial(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type, final Class<?> thisClass) {
551             try {
552                 final MethodHandle mh = explicitLookup.findSpecial(clazz, name, type, thisClass);
553                 return debug(mh, "findSpecial", explicitLookup, clazz, name, type);
554             } catch (final NoSuchMethodException | IllegalAccessException e) {
555                 throw new LookupException(e);
556             }
557         }
558 
559         @Override
findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type)560         public MethodHandle findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
561             try {
562                 final MethodHandle mh = explicitLookup.findVirtual(clazz, name, type);
563                 return debug(mh, "findVirtual", explicitLookup, clazz, name, type);
564             } catch (final NoSuchMethodException | IllegalAccessException e) {
565                 throw new LookupException(e);
566             }
567         }
568 
569         @Override
createSwitchPoint()570         public SwitchPoint createSwitchPoint() {
571             final SwitchPoint sp = new SwitchPoint();
572             log.log(TRACE_LEVEL, "createSwitchPoint ", sp);
573             return sp;
574         }
575 
576         @Override
guardWithTest(final SwitchPoint sp, final MethodHandle before, final MethodHandle after)577         public MethodHandle guardWithTest(final SwitchPoint sp, final MethodHandle before, final MethodHandle after) {
578             final MethodHandle mh = sp.guardWithTest(before, after);
579             return debug(mh, "guardWithTest", sp, before, after);
580         }
581 
582         @Override
type(final Class<?> returnType, final Class<?>... paramTypes)583         public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
584             final MethodType mt = MethodType.methodType(returnType, paramTypes);
585             log.log(TRACE_LEVEL, "methodType ", returnType, " ", Arrays.toString(paramTypes), " ", mt);
586             return mt;
587         }
588     }
589 }
590