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