diff -r 3470bc26128f src/jdk/nashorn/internal/runtime/ScriptFunction.java --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Fri Oct 04 16:21:29 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Fri Oct 04 22:39:55 2013 +0200 @@ -33,6 +33,12 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -73,6 +79,8 @@ private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH("addZerothElement", Object[].class, Object[].class, Object.class); + private static final MethodHandle INVOKE_WITH_STATS = findOwnMH("invokeWithStats", Object.class, String.class, MethodHandle.class, MethodType.class, Object[].class); + /** The parent scope. */ private final ScriptObject scope; @@ -307,7 +315,102 @@ * @return invoke method handle */ private MethodHandle getBestInvoker(final MethodType type, final Object[] args) { - return data.getBestInvoker(type, args); + final MethodHandle invoker = data.getBestInvoker(type, args); + if (data.isBuiltin()) { + return getProfilingInvoker(invoker, type, args); + } + return invoker; + } + + static private Map methodCount = new HashMap<>(); + static private Map boxedMethodCount = new HashMap<>(); + + private MethodHandle getProfilingInvoker(final MethodHandle invoker, final MethodType type, final Object[] args) { + final int argCount = invoker.type().parameterCount(); + final MethodType bit = invoker.type(); + final boolean varargs = bit.parameterCount() > 0 && bit.parameterType(bit.parameterCount() - 1) == Object[].class; + String methodName = getBaseName(args[1]) + data.getName(); + MethodType optimalType = null; + for (int i = 0; i < type.parameterCount(); i++) { + if (type.parameterType(i).isPrimitive() && (i >= bit.parameterCount() || !bit.parameterType(i).isPrimitive())) { + // record original callsite type if the available type requires boxing of primitives + optimalType = type; + break; + } + } + MethodHandle mh = MethodHandles + .insertArguments(INVOKE_WITH_STATS, 0, methodName, invoker.asFixedArity(), optimalType) + .asCollector(Object[].class, argCount); + return varargs ? mh.asVarargsCollector(Object[].class) : mh; + } + + @SuppressWarnings("unused") + private static Object invokeWithStats(final String methodName, final MethodHandle handle, final MethodType optimalType, + Object... args) throws Throwable { + incMethodCount(methodCount, methodName); + if (optimalType != null) { + incMethodCount(boxedMethodCount, methodName + ' ' +optimalType); + } + return handle.invokeWithArguments(args); + } + + private static void incMethodCount(Map map, String methodName) { + if (map.containsKey(methodName)) { + map.put(methodName, map.get(methodName) + 1); + } else { + map.put(methodName, 1); + } + + } + + private static String getBaseName(final Object base) { + String baseName = ""; + if (base instanceof ScriptObject) { + final String className = base.getClass().getSimpleName(); + if (className.endsWith("$Constructor")) { + baseName = className.substring(6, className.indexOf('$')) + "."; + } else { + baseName = ((ScriptObject)base).getClassName() + ".prototype."; + } + } else if (base instanceof CharSequence) { + baseName = "String.prototype."; + } else if (base instanceof Number) { + baseName = "Number.prototype."; + } + return baseName; + } + + public static void dumpStats() { + dumpMap(methodCount); + } + + public static void dumpBoxedStats() { + dumpMap(boxedMethodCount); + } + + private static void dumpMap(Map stats) { + if (stats.isEmpty()) { + return; + } + final List> entries = new ArrayList<>(stats.entrySet()); + Collections.sort(entries, new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return o2.getValue() - o1.getValue(); + } + }); + + final int keyLength = Collections.max(entries, new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return o1.getKey().length() - o2.getKey().length(); + } + }).getKey().length(); + final String formatString = "%1$-" + (keyLength + 2) + "s %2$10d%n"; + + for (Map.Entry entry : entries) { + System.out.format(formatString, entry.getKey(), entry.getValue()); + } } /**