diff options
author | sundar <none@none> | 2013-03-09 21:49:32 +0530 |
---|---|---|
committer | sundar <none@none> | 2013-03-09 21:49:32 +0530 |
commit | ce81c68c84d9215b18587a2b25a347d6ffc9d481 (patch) | |
tree | 1350b87d7ca7ddd7b3d2a4083b20b48abe394aea /src/jdk/nashorn/internal/lookup | |
parent | e5e3ec702d09e19e38c78b6792732ce89e580d0a (diff) | |
download | jdk8u_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.java | 212 | ||||
-rw-r--r-- | src/jdk/nashorn/internal/lookup/MethodHandleFactory.java | 646 | ||||
-rw-r--r-- | src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java | 317 |
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); +} + |