aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/lookup
diff options
context:
space:
mode:
authorsundar <none@none>2013-03-09 21:49:32 +0530
committersundar <none@none>2013-03-09 21:49:32 +0530
commitce81c68c84d9215b18587a2b25a347d6ffc9d481 (patch)
tree1350b87d7ca7ddd7b3d2a4083b20b48abe394aea /src/jdk/nashorn/internal/lookup
parente5e3ec702d09e19e38c78b6792732ce89e580d0a (diff)
downloadjdk8u_nashorn-ce81c68c84d9215b18587a2b25a347d6ffc9d481.tar.gz
8009559: clean up method handle lookup code.
Reviewed-by: ahgross, jlaskey, attila, sundar
Diffstat (limited to 'src/jdk/nashorn/internal/lookup')
-rw-r--r--src/jdk/nashorn/internal/lookup/Lookup.java212
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFactory.java646
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java317
3 files changed, 1175 insertions, 0 deletions
diff --git a/src/jdk/nashorn/internal/lookup/Lookup.java b/src/jdk/nashorn/internal/lookup/Lookup.java
new file mode 100644
index 00000000..a454527a
--- /dev/null
+++ b/src/jdk/nashorn/internal/lookup/Lookup.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.lookup;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * MethodHandle Lookup management for Nashorn.
+ */
+public final class Lookup {
+
+ /**
+ * A global singleton that points to the {@link MethodHandleFunctionality}. This is basically
+ * a collection of wrappers to the standard methods in {@link MethodHandle}, {@link MethodHandles} and
+ * {@link java.lang.invoke.MethodHandles.Lookup}, but instrumentation and debugging purposes we need
+ * intercept points.
+ * <p>
+ * All method handle operations in Nashorn should go through this field, not directly to the classes
+ * in {@code java.lang.invoke}
+ */
+ public static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
+
+ /** Method handle to the empty getter */
+ public static final MethodHandle EMPTY_GETTER = findOwnMH("emptyGetter", Object.class, Object.class);
+
+ /** Method handle to the empty setter */
+ public static final MethodHandle EMPTY_SETTER = findOwnMH("emptySetter", void.class, Object.class, Object.class);
+
+ /** Method handle to a getter that only throws type error */
+ public static final MethodHandle TYPE_ERROR_THROWER_GETTER = findOwnMH("typeErrorThrowerGetter", Object.class, Object.class);
+
+ /** Method handle to a setter that only throws type error */
+ public static final MethodHandle TYPE_ERROR_THROWER_SETTER = findOwnMH("typeErrorThrowerSetter", void.class, Object.class, Object.class);
+
+ /** Method handle to the most generic of getters, the one that returns an Object */
+ public static final MethodType GET_OBJECT_TYPE = MH.type(Object.class, Object.class);
+
+ /** Method handle to the most generic of setters, the one that takes an Object */
+ public static final MethodType SET_OBJECT_TYPE = MH.type(void.class, Object.class, Object.class);
+
+ private Lookup() {
+ }
+
+ /**
+ * Empty getter implementation. Nop
+ * @param self self reference
+ * @return undefined
+ */
+ public static Object emptyGetter(final Object self) {
+ return UNDEFINED;
+ }
+
+ /**
+ * Empty setter implementation. Nop
+ * @param self self reference
+ * @param value value (ignored)
+ */
+ public static void emptySetter(final Object self, final Object value) {
+ // do nothing!!
+ }
+
+ /**
+ * Return a method handle to the empty getter, with a different
+ * return type value. It will still be undefined cast to whatever
+ * return value property was specified
+ *
+ * @param type return value type
+ *
+ * @return undefined as return value type
+ */
+ public static MethodHandle emptyGetter(final Class<?> type) {
+ return filterReturnType(EMPTY_GETTER, type);
+ }
+
+ /**
+ * Getter function that always throws type error
+ *
+ * @param self self reference
+ * @return undefined (but throws error before return point)
+ */
+ public static Object typeErrorThrowerGetter(final Object self) {
+ throw typeError("strict.getter.setter.poison", ScriptRuntime.safeToString(self));
+ }
+
+ /**
+ * Getter function that always throws type error
+ *
+ * @param self self reference
+ * @param value (ignored)
+ */
+ public static void typeErrorThrowerSetter(final Object self, final Object value) {
+ throw typeError("strict.getter.setter.poison", ScriptRuntime.safeToString(self));
+ }
+
+ /**
+ * Create a new {@link Property}
+ *
+ * @param map property map
+ * @param key property key
+ * @param flags property flags
+ * @param propertyGetter getter for property if available, null otherwise
+ * @param propertySetter setter for property if available, null otherwise
+ *
+ * @return new property map, representing {@code PropertyMap} with the new property added to it
+ */
+ @SuppressWarnings("fallthrough")
+ public static PropertyMap newProperty(final PropertyMap map, final String key, final int flags, final MethodHandle propertyGetter, final MethodHandle propertySetter) {
+ MethodHandle getter = propertyGetter;
+ MethodHandle setter = propertySetter;
+
+ // TODO: this is temporary code. This code exists to support reflective
+ // field reader/writer handles generated by "unreflect" lookup.
+
+ switch (getter.type().parameterCount()) {
+ case 0:
+ // A static field reader, so drop the 'self' argument.
+ getter = MH.dropArguments(getter, 0, Object.class);
+ if (setter != null) {
+ setter = MH.dropArguments(setter, 0, Object.class);
+ }
+ // fall through
+ case 1:
+ // standard getter that accepts 'self'.
+ break;
+ default:
+ // Huh!! something wrong..
+ throw new IllegalArgumentException("getter/setter has wrong arguments");
+ }
+
+ return map.newProperty(key, flags, -1, getter, setter);
+ }
+
+ /**
+ * This method filters primitive return types using JavaScript semantics. For example,
+ * an (int) cast of a double in Java land is not the same thing as invoking toInt32 on it.
+ * If you are returning values to JavaScript that have to be of a specific type, this is
+ * the correct return value filter to use, as the explicitCastArguments just uses the
+ * Java boxing equivalents
+ *
+ * @param mh method handle for which to filter return value
+ * @param type new return type
+ * @return method handle for appropriate return type conversion
+ */
+ public static MethodHandle filterReturnType(final MethodHandle mh, final Class<?> type) {
+ final Class<?> retType = mh.type().returnType();
+
+ if (retType == int.class) {
+ //fallthru
+ } else if (retType == long.class) {
+ //fallthru
+ } else if (retType == double.class) {
+ if (type == int.class) {
+ return MH.filterReturnValue(mh, JSType.TO_INT32_D.methodHandle());
+ } else if (type == long.class) {
+ return MH.filterReturnValue(mh, JSType.TO_UINT32_D.methodHandle());
+ }
+ //fallthru
+ } else if (!retType.isPrimitive()) {
+ if (type == int.class) {
+ return MH.filterReturnValue(mh, JSType.TO_INT32.methodHandle());
+ } else if (type == long.class) {
+ return MH.filterReturnValue(mh, JSType.TO_UINT32.methodHandle());
+ } else if (type == double.class) {
+ return MH.filterReturnValue(mh, JSType.TO_NUMBER.methodHandle());
+ } else if (!type.isPrimitive()) {
+ return mh;
+ }
+
+ assert false : "unsupported Lookup.filterReturnType type " + retType + " -> " + type;
+ }
+
+ //use a standard cast - we don't need to check JavaScript special cases
+ return MH.explicitCastArguments(mh, mh.type().changeReturnType(type));
+ }
+
+ private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+ return MH.findStatic(MethodHandles.lookup(), Lookup.class, name, MH.type(rtype, types));
+ }
+
+}
diff --git a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
new file mode 100644
index 00000000..2161c782
--- /dev/null
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
@@ -0,0 +1,646 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.lookup;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import jdk.nashorn.internal.runtime.ConsString;
+import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.options.Options;
+
+/**
+ * This class is abstraction for all method handle, switchpoint and method type
+ * operations. This enables the functionality interface to be subclassed and
+ * intrumensted, as it has been proven vital to keep the number of method
+ * handles in the system down.
+ *
+ * All operations of the above type should go through this class, and not
+ * directly into java.lang.invoke
+ *
+ */
+public final class MethodHandleFactory {
+
+ private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+ private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ private static final Level TRACE_LEVEL = Level.INFO;
+
+ private MethodHandleFactory() {
+ }
+
+ /**
+ * Runtime exception that collects every reason that a method handle lookup operation can go wrong
+ */
+ @SuppressWarnings("serial")
+ public static class LookupException extends RuntimeException {
+ /**
+ * Constructor
+ * @param e causing exception
+ */
+ public LookupException(final Exception e) {
+ super(e);
+ }
+ }
+
+ /**
+ * Helper function that takes a class or an object with a toString override
+ * and shortens it to notation after last dot. This is used to facilitiate
+ * pretty printouts in various debug loggers - internal only
+ *
+ * @param obj class or object
+ *
+ * @return pretty version of object as string
+ */
+ public static String stripName(final Object obj) {
+ if (obj == null) {
+ return "null";
+ }
+
+ if (obj instanceof Class) {
+ return ((Class<?>)obj).getSimpleName();
+ }
+ return obj.toString();
+ }
+
+ private static final MethodHandleFunctionality STANDARD = new StandardMethodHandleFunctionality();
+ private static final MethodHandleFunctionality FUNC;
+
+ private static final String DEBUG_PROPERTY = "nashorn.methodhandles.debug";
+ private static final DebugLogger LOG = new DebugLogger("methodhandles", DEBUG_PROPERTY);
+
+ static {
+ if (LOG.isEnabled() || Options.getBooleanProperty(DEBUG_PROPERTY)) {
+ if (Options.getStringProperty(DEBUG_PROPERTY, "").equals("create")) {
+ FUNC = new TraceCreateMethodHandleFunctionality();
+ } else {
+ FUNC = new TraceMethodHandleFunctionality();
+ }
+ } else {
+ FUNC = STANDARD;
+ }
+ }
+
+ private static final boolean PRINT_STACKTRACE = Options.getBooleanProperty("nashorn.methodhandles.debug.stacktrace");
+
+
+ /**
+ * Return the method handle functionality used for all method handle operations
+ * @return a method handle functionality implementation
+ */
+ public static MethodHandleFunctionality getFunctionality() {
+ return FUNC;
+ }
+
+ private static final MethodHandle TRACE = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceArgs", MethodType.methodType(void.class, DebugLogger.class, String.class, int.class, Object[].class));
+ private static final MethodHandle TRACE_RETURN = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturn", MethodType.methodType(Object.class, DebugLogger.class, Object.class));
+
+ /**
+ * Tracer that is applied before a value is returned from the traced function. It will output the return
+ * value and its class
+ *
+ * @param value return value for filter
+ * @return return value unmodified
+ */
+ static Object traceReturn(final DebugLogger logger, final Object value) {
+ final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
+ logger.log(str, TRACE_LEVEL);
+ return value;
+ }
+
+ /**
+ * Tracer that is applied before a function is called, printing the arguments
+ *
+ * @param tag tag to start the debug printout string
+ * @param paramStart param index to start outputting from
+ * @param args arguments to the function
+ */
+ static void traceArgs(final DebugLogger logger, final String tag, final int paramStart, final Object... args) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(tag);
+
+ for (int i = paramStart; i < args.length; i++) {
+ if (i == paramStart) {
+ sb.append(" => args: ");
+ }
+
+ sb.append('\'').
+ append(stripName(argString(args[i]))).
+ append('\'').
+ append(' ').
+ append('[').
+ append("type=").
+ append(args[i] == null ? "null" : stripName(args[i].getClass())).
+ append(']');
+
+ if (i + 1 < args.length) {
+ sb.append(", ");
+ }
+ }
+
+ assert logger != null;
+ logger.log(sb.toString(), TRACE_LEVEL);
+ stacktrace(logger);
+ }
+
+ private static void stacktrace(final DebugLogger logger) {
+ if (!PRINT_STACKTRACE) {
+ return;
+ }
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final PrintStream ps = new PrintStream(baos);
+ new Throwable().printStackTrace(ps);
+ logger.log(baos.toString(), TRACE_LEVEL);
+ }
+
+ private static String argString(final Object arg) {
+ if (arg == null) {
+ return "null";
+ }
+
+ if (arg.getClass().isArray()) {
+ final List<Object> list = new ArrayList<>();
+ for (final Object elem : (Object[])arg) {
+ list.add('\'' + argString(elem) + '\'');
+ }
+
+ return list.toString();
+ }
+
+ if (arg instanceof ScriptObject) {
+ return arg.toString() +
+ " (map=" + Debug.id((((ScriptObject)arg).getMap())) +
+ ")";
+ }
+
+ return arg.toString();
+ }
+
+ /**
+ * Add a debug printout to a method handle, tracing parameters and return values
+ *
+ * @param logger a specific logger to which to write the output
+ * @param mh method handle to trace
+ * @param tag start of trace message
+ * @return traced method handle
+ */
+ public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final Object tag) {
+ return addDebugPrintout(logger, mh, 0, true, tag);
+ }
+
+
+ /**
+ * Add a debug printout to a method handle, tracing parameters and return values
+ *
+ * @param logger a specific logger to which to write the output
+ * @param mh method handle to trace
+ * @param paramStart first param to print/trace
+ * @param printReturnValue should we print/trace return value if available?
+ * @param tag start of trace message
+ * @return traced method handle
+ */
+ public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
+ final MethodType type = mh.type();
+
+ if (logger != null && logger.levelAbove(TRACE_LEVEL)) {
+ return mh;
+ }
+
+ assert logger != null;
+ assert TRACE != null;
+
+ MethodHandle trace = MethodHandles.insertArguments(TRACE, 0, logger, tag, paramStart);
+
+ trace = MethodHandles.foldArguments(
+ mh,
+ trace.asCollector(
+ Object[].class,
+ type.parameterCount()).
+ asType(type.changeReturnType(void.class)));
+
+ final Class<?> retType = type.returnType();
+ if (retType != void.class && printReturnValue) {
+ final MethodHandle traceReturn = MethodHandles.insertArguments(TRACE_RETURN, 0, logger);
+ trace = MethodHandles.filterReturnValue(trace,
+ traceReturn.asType(
+ traceReturn.type().changeParameterType(0, retType).changeReturnType(retType)));
+ }
+
+ return trace;
+ }
+
+ /**
+ * The standard class that marshalls all method handle operations to the java.lang.invoke
+ * package. This exists only so that it can be subclassed and method handles created from
+ * Nashorn made possible to instrument.
+ *
+ * All Nashorn classes should use the MethodHandleFactory for their method handle operations
+ */
+ private static class StandardMethodHandleFunctionality implements MethodHandleFunctionality {
+
+ @Override
+ public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
+ return MethodHandles.filterArguments(target, pos, filters);
+ }
+
+ @Override
+ public MethodHandle filterReturnValue(final MethodHandle target, final MethodHandle filter) {
+ return MethodHandles.filterReturnValue(target, filter);
+ }
+
+ @Override
+ public MethodHandle guardWithTest(final MethodHandle test, final MethodHandle target, final MethodHandle fallback) {
+ return MethodHandles.guardWithTest(test, target, fallback);
+ }
+
+ @Override
+ public MethodHandle insertArguments(final MethodHandle target, final int pos, final Object... values) {
+ return MethodHandles.insertArguments(target, pos, values);
+ }
+
+ @Override
+ public MethodHandle dropArguments(final MethodHandle target, final int pos, final Class<?>... valueTypes) {
+ return MethodHandles.dropArguments(target, pos, valueTypes);
+ }
+
+ @Override
+ public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> valueTypes) {
+ return MethodHandles.dropArguments(target, pos, valueTypes);
+ }
+
+ @Override
+ public MethodHandle asType(final MethodHandle handle, final MethodType type) {
+ return handle.asType(type);
+ }
+
+ @Override
+ public MethodHandle bindTo(final MethodHandle handle, final Object x) {
+ return handle.bindTo(x);
+ }
+
+ @Override
+ public MethodHandle foldArguments(final MethodHandle target, final MethodHandle combiner) {
+ return MethodHandles.foldArguments(target, combiner);
+ }
+
+ @Override
+ public MethodHandle explicitCastArguments(final MethodHandle target, final MethodType type) {
+ return MethodHandles.explicitCastArguments(target, type);
+ }
+
+ @Override
+ public MethodHandle arrayElementGetter(final Class<?> type) {
+ return MethodHandles.arrayElementGetter(type);
+ }
+
+ @Override
+ public MethodHandle arrayElementSetter(final Class<?> type) {
+ return MethodHandles.arrayElementSetter(type);
+ }
+
+ @Override
+ public MethodHandle throwException(final Class<?> returnType, final Class<? extends Throwable> exType) {
+ return MethodHandles.throwException(returnType, exType);
+ }
+
+ @Override
+ public MethodHandle constant(final Class<?> type, final Object value) {
+ return MethodHandles.constant(type, value);
+ }
+
+ @Override
+ public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
+ return handle.asCollector(arrayType, arrayLength);
+ }
+
+ @Override
+ public MethodHandle asSpreader(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
+ return handle.asSpreader(arrayType, arrayLength);
+ }
+
+ @Override
+ public MethodHandle getter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ try {
+ return explicitLookup.findGetter(clazz, name, type);
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+ @Override
+ public MethodHandle staticGetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ try {
+ return explicitLookup.findStaticGetter(clazz, name, type);
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+
+ @Override
+ public MethodHandle setter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ try {
+ return explicitLookup.findSetter(clazz, name, type);
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+ @Override
+ public MethodHandle staticSetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ try {
+ return explicitLookup.findStaticSetter(clazz, name, type);
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+ @Override
+ public MethodHandle find(final Method method) {
+ try {
+ return PUBLIC_LOOKUP.unreflect(method);
+ } catch (final IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+ @Override
+ public MethodHandle findStatic(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
+ try {
+ return explicitLookup.findStatic(clazz, name, type);
+ } catch (final NoSuchMethodException | IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+ @Override
+ public MethodHandle findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
+ try {
+ return explicitLookup.findVirtual(clazz, name, type);
+ } catch (final NoSuchMethodException | IllegalAccessException e) {
+ throw new LookupException(e);
+ }
+ }
+
+ @Override
+ public SwitchPoint createSwitchPoint() {
+ return new SwitchPoint();
+ }
+
+ @Override
+ public MethodHandle guardWithTest(final SwitchPoint sp, final MethodHandle before, final MethodHandle after) {
+ return sp.guardWithTest(before, after);
+ }
+
+ @Override
+ public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
+ return MethodType.methodType(returnType, paramTypes);
+ }
+
+ }
+
+ /**
+ * Class used for instrumenting and debugging Nashorn generated method handles
+ */
+ private static class TraceMethodHandleFunctionality extends StandardMethodHandleFunctionality {
+
+ protected static String describe(final Object... data) {
+ final StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < data.length; i++) {
+ final Object d = data[i];
+ if (d == null) {
+ sb.append("<null> ");
+ } else if (d instanceof String || d instanceof ConsString) {
+ sb.append(d.toString());
+ sb.append(' ');
+ } else if (d.getClass().isArray()) {
+ sb.append("[ ");
+ for (final Object da : (Object[])d) {
+ sb.append(describe(new Object[]{ da })).append(' ');
+ }
+ sb.append("] ");
+ } else {
+ sb.append(d)
+ .append('{')
+ .append(Integer.toHexString(System.identityHashCode(d)))
+ .append('}');
+ }
+
+ if (i + 1 < data.length) {
+ sb.append(", ");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
+ return addDebugPrintout(LOG, master, Integer.MAX_VALUE, false, str + ' ' + describe(args));
+ }
+
+ @Override
+ public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
+ final MethodHandle mh = super.filterArguments(target, pos, filters);
+ return debug(mh, "filterArguments", target, pos, filters);
+ }
+
+ @Override
+ public MethodHandle filterReturnValue(final MethodHandle target, final MethodHandle filter) {
+ final MethodHandle mh = super.filterReturnValue(target, filter);
+ return debug(mh, "filterReturnValue", target, filter);
+ }
+
+ @Override
+ public MethodHandle guardWithTest(final MethodHandle test, final MethodHandle target, final MethodHandle fallback) {
+ final MethodHandle mh = super.guardWithTest(test, target, fallback);
+ return debug(mh, "guardWithTest", test, target, fallback);
+ }
+
+ @Override
+ public MethodHandle insertArguments(final MethodHandle target, final int pos, final Object... values) {
+ final MethodHandle mh = super.insertArguments(target, pos, values);
+ return debug(mh, "insertArguments", target, pos, values);
+ }
+
+ @Override
+ public MethodHandle dropArguments(final MethodHandle target, final int pos, final Class<?>... values) {
+ final MethodHandle mh = super.dropArguments(target, pos, values);
+ return debug(mh, "dropArguments", target, pos, values);
+ }
+
+ @Override
+ public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> values) {
+ final MethodHandle mh = super.dropArguments(target, pos, values);
+ return debug(mh, "dropArguments", target, pos, values);
+ }
+
+ @Override
+ public MethodHandle asType(final MethodHandle handle, final MethodType type) {
+ final MethodHandle mh = super.asType(handle, type);
+ return debug(mh, "asType", handle, type);
+ }
+
+ @Override
+ public MethodHandle bindTo(final MethodHandle handle, final Object x) {
+ final MethodHandle mh = super.bindTo(handle, x);
+ return debug(mh, "bindTo", handle, x);
+ }
+
+ @Override
+ public MethodHandle foldArguments(final MethodHandle target, final MethodHandle combiner) {
+ final MethodHandle mh = super.foldArguments(target, combiner);
+ return debug(mh, "foldArguments", target, combiner);
+ }
+
+ @Override
+ public MethodHandle explicitCastArguments(final MethodHandle target, final MethodType type) {
+ final MethodHandle mh = super.explicitCastArguments(target, type);
+ return debug(mh, "explicitCastArguments", target, type);
+ }
+
+ @Override
+ public MethodHandle arrayElementGetter(final Class<?> type) {
+ final MethodHandle mh = super.arrayElementGetter(type);
+ return debug(mh, "arrayElementGetter", type);
+ }
+
+ @Override
+ public MethodHandle arrayElementSetter(final Class<?> type) {
+ final MethodHandle mh = super.arrayElementSetter(type);
+ return debug(mh, "arrayElementSetter", type);
+ }
+
+ @Override
+ public MethodHandle throwException(final Class<?> returnType, final Class<? extends Throwable> exType) {
+ final MethodHandle mh = super.throwException(returnType, exType);
+ return debug(mh, "throwException", returnType, exType);
+ }
+
+ @Override
+ public MethodHandle constant(final Class<?> type, final Object value) {
+ final MethodHandle mh = super.constant(type, value);
+ return debug(mh, "constant", type, value);
+ }
+
+ @Override
+ public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
+ final MethodHandle mh = super.asCollector(handle, arrayType, arrayLength);
+ return debug(mh, "asCollector", handle, arrayType, arrayLength);
+ }
+
+ @Override
+ public MethodHandle asSpreader(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
+ final MethodHandle mh = super.asCollector(handle, arrayType, arrayLength);
+ return debug(mh, "asSpreader", handle, arrayType, arrayLength);
+ }
+
+ @Override
+ public MethodHandle getter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ final MethodHandle mh = super.getter(explicitLookup, clazz, name, type);
+ return debug(mh, "getter", explicitLookup, clazz, name, type);
+ }
+
+ @Override
+ public MethodHandle staticGetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ final MethodHandle mh = super.staticGetter(explicitLookup, clazz, name, type);
+ return debug(mh, "static getter", explicitLookup, clazz, name, type);
+ }
+
+ @Override
+ public MethodHandle setter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ final MethodHandle mh = super.setter(explicitLookup, clazz, name, type);
+ return debug(mh, "setter", explicitLookup, clazz, name, type);
+ }
+
+ @Override
+ public MethodHandle staticSetter(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final Class<?> type) {
+ final MethodHandle mh = super.staticSetter(explicitLookup, clazz, name, type);
+ return debug(mh, "static setter", explicitLookup, clazz, name, type);
+ }
+
+ @Override
+ public MethodHandle find(final Method method) {
+ final MethodHandle mh = super.find(method);
+ return debug(mh, "find", method);
+ }
+
+ @Override
+ public MethodHandle findStatic(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
+ final MethodHandle mh = super.findStatic(explicitLookup, clazz, name, type);
+ return debug(mh, "findStatic", explicitLookup, clazz, name, type);
+ }
+
+ @Override
+ public MethodHandle findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
+ final MethodHandle mh = super.findVirtual(explicitLookup, clazz, name, type);
+ return debug(mh, "findVirtual", explicitLookup, clazz, name, type);
+ }
+
+ @Override
+ public SwitchPoint createSwitchPoint() {
+ final SwitchPoint sp = super.createSwitchPoint();
+ LOG.log("createSwitchPoint " + sp, TRACE_LEVEL);
+ return sp;
+ }
+
+ @Override
+ public MethodHandle guardWithTest(final SwitchPoint sp, final MethodHandle before, final MethodHandle after) {
+ final MethodHandle mh = super.guardWithTest(sp, before, after);
+ return debug(mh, "guardWithTest", sp, before, after);
+ }
+
+ @Override
+ public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
+ final MethodType mt = super.type(returnType, paramTypes);
+ LOG.log("methodType " + returnType + ' ' + Arrays.toString(paramTypes) + ' ' + mt, TRACE_LEVEL);
+ return mt;
+ }
+ }
+
+ /**
+ * Class used for debugging Nashorn generated method handles
+ */
+ private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
+ @Override
+ public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
+ LOG.log(str + ' ' + describe(args), TRACE_LEVEL);
+ stacktrace(LOG);
+ return master;
+ }
+ }
+}
diff --git a/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java b/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
new file mode 100644
index 00000000..c7dd83b2
--- /dev/null
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.lookup;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Wrapper for all method handle related functions used in Nashorn. This interface only exists
+ * so that instrumentation can be added to all method handle operations.
+ */
+
+public interface MethodHandleFunctionality {
+ /**
+ * Wrapper for {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}
+ *
+ * @param target target method handle
+ * @param pos start argument index
+ * @param filters filters
+ *
+ * @return filtered handle
+ */
+ public MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters);
+
+ /**
+ * Wrapper for {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)}
+ *
+ * @param target target method handle
+ * @param filter filter
+ *
+ * @return filtered handle
+ */
+ public MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter);
+
+ /**
+ * Wrapper for {@link MethodHandles#guardWithTest(MethodHandle, MethodHandle, MethodHandle)}
+ *
+ * @param test test method handle
+ * @param target target method handle when test is true
+ * @param fallback fallback method handle when test is false
+ *
+ * @return guarded handles
+ */
+ public MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback);
+
+ /**
+ * Wrapper for {@link MethodHandles#insertArguments(MethodHandle, int, Object...)}
+ *
+ * @param target target method handle
+ * @param pos start argument index
+ * @param values values to insert
+ *
+ * @return handle with bound arguments
+ */
+ public MethodHandle insertArguments(MethodHandle target, int pos, Object... values);
+
+ /**
+ * Wrapper for {@link MethodHandles#dropArguments(MethodHandle, int, Class...)}
+ *
+ * @param target target method handle
+ * @param pos start argument index
+ * @param valueTypes valueTypes of arguments to drop
+ *
+ * @return handle with dropped arguments
+ */
+ public MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes);
+
+ /**
+ * Wrapper for {@link MethodHandles#dropArguments(MethodHandle, int, List)}
+ *
+ * @param target target method handle
+ * @param pos start argument index
+ * @param valueTypes valueTypes of arguments to drop
+ *
+ * @return handle with dropped arguments
+ */
+ public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> valueTypes);
+
+ /**
+ * Wrapper for {@link MethodHandles#foldArguments(MethodHandle, MethodHandle)}
+ *
+ * @param target target method handle
+ * @param combiner combiner to apply for fold
+ *
+ * @return folded method handle
+ */
+ public MethodHandle foldArguments(MethodHandle target, MethodHandle combiner);
+
+ /**
+ * Wrapper for {@link MethodHandles#explicitCastArguments(MethodHandle, MethodType)}
+ *
+ * @param target target method handle
+ * @param type type to cast to
+ *
+ * @return modified method handle
+ */
+ public MethodHandle explicitCastArguments(MethodHandle target, MethodType type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles#arrayElementGetter(Class)}
+ *
+ * @param arrayClass class for array
+ *
+ * @return array element getter
+ */
+ public MethodHandle arrayElementGetter(Class<?> arrayClass);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles#arrayElementSetter(Class)}
+ *
+ * @param arrayClass class for array
+ *
+ * @return array element setter
+ */
+ public MethodHandle arrayElementSetter(Class<?> arrayClass);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles#throwException(Class, Class)}
+ *
+ * @param returnType ignored, but method signature will use it
+ * @param exType exception type that will be thrown
+ *
+ * @return exception thrower method handle
+ */
+ public MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles#constant(Class, Object)}
+ *
+ * @param type type of constant
+ * @param value constant value
+ *
+ * @return method handle that returns said constant
+ */
+ public MethodHandle constant(Class<?> type, Object value);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandle#asType(MethodType)}
+ *
+ * @param handle method handle for type conversion
+ * @param type type to convert to
+ *
+ * @return method handle with given type conversion applied
+ */
+ public MethodHandle asType(MethodHandle handle, MethodType type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandle#asCollector(Class, int)}
+ *
+ * @param handle handle to convert
+ * @param arrayType array type for collector array
+ * @param arrayLength length of collector array
+ *
+ * @return method handle with collector
+ */
+ public MethodHandle asCollector(MethodHandle handle, Class<?> arrayType, int arrayLength);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandle#asSpreader(Class, int)}
+ *
+ * @param handle handle to convert
+ * @param arrayType array type for spread
+ * @param arrayLength length of spreader
+ *
+ * @return method handle as spreader
+ */
+ public MethodHandle asSpreader(MethodHandle handle, Class<?> arrayType, int arrayLength);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandle#bindTo(Object)}
+ *
+ * @param handle a handle to which to bind a receiver
+ * @param x the receiver
+ *
+ * @return the bound handle
+ */
+ public MethodHandle bindTo(MethodHandle handle, Object x);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findGetter(Class, String, Class)}
+ *
+ * @param explicitLookup explicit lookup to be used
+ * @param clazz class to look in
+ * @param name name of field
+ * @param type type of field
+ *
+ * @return getter method handle for virtual field
+ */
+ public MethodHandle getter(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, Class<?> type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findStaticGetter(Class, String, Class)}
+ *
+ * @param explicitLookup explicit lookup to be used
+ * @param clazz class to look in
+ * @param name name of field
+ * @param type type of field
+ *
+ * @return getter method handle for static field
+ */
+ public MethodHandle staticGetter(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, Class<?> type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findSetter(Class, String, Class)}
+ *
+ * @param explicitLookup explicit lookup to be used
+ * @param clazz class to look in
+ * @param name name of field
+ * @param type type of field
+ *
+ * @return setter method handle for virtual field
+ */
+ public MethodHandle setter(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, Class<?> type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findStaticSetter(Class, String, Class)}
+ *
+ * @param explicitLookup explicit lookup to be used
+ * @param clazz class to look in
+ * @param name name of field
+ * @param type type of field
+ *
+ * @return setter method handle for static field
+ */
+ public MethodHandle staticSetter(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, Class<?> type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}
+ *
+ * Unreflect a method as a method handle
+ *
+ * @param method method to unreflect
+ * @return unreflected method as method handle
+ */
+ public MethodHandle find(Method method);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findStatic(Class, String, MethodType)}
+ *
+ * @param explicitLookup explicit lookup to be used
+ * @param clazz class to look in
+ * @param name name of method
+ * @param type method type
+ *
+ * @return method handle for static method
+ */
+ public MethodHandle findStatic(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, MethodType type);
+
+ /**
+ * Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findVirtual(Class, String, MethodType)}
+ *
+ * @param explicitLookup explicit lookup to be used
+ * @param clazz class to look in
+ * @param name name of method
+ * @param type method type
+ *
+ * @return method handle for virtual method
+ */
+ public MethodHandle findVirtual(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, MethodType type);
+
+ /**
+ * Wrapper for SwitchPoint creation. Just like {@code new SwitchPoint()} but potentially
+ * tracked
+ *
+ * @return new switch point
+ */
+ public SwitchPoint createSwitchPoint();
+
+ /**
+ * Wrapper for {@link SwitchPoint#guardWithTest(MethodHandle, MethodHandle)}
+ *
+ * @param sp switch point
+ * @param before method handle when switchpoint is valid
+ * @param after method handle when switchpoint is invalidated
+ *
+ * @return guarded method handle
+ */
+ public MethodHandle guardWithTest(SwitchPoint sp, MethodHandle before, MethodHandle after);
+
+ /**
+ * Wrapper for {@link MethodType#methodType(Class, Class...)}
+ *
+ * @param returnType return type for method type
+ * @param paramTypes parameter types for method type
+ *
+ * @return the method type
+ */
+ public MethodType type(Class<?> returnType, Class<?>... paramTypes);
+}
+