# HG changeset patch # User mullan # Date 1307195521 25200 # Node ID 1f39ca0b95982dc41e235e89d11a79396ca0ffd2 # Parent 49aef5a5416e14d6488e52f81386d2b6c7d48680# Parent 53d759eb580cf0f9ed6e350df16e79e0d741a5de Merge diff -r 49aef5a5416e -r 1f39ca0b9598 .hgtags --- a/.hgtags Sat Jun 04 06:45:17 2011 -0700 +++ b/.hgtags Sat Jun 04 06:52:01 2011 -0700 @@ -118,3 +118,4 @@ 63eeefe118da18c75ba3d36266768cd1ccaaca6b jdk7-b141 312612e89ece62633f4809706dec00bcd5fe7c2d jdk7-b142 efbf75c24b0f31847c9c403f6dc07dc80551908d jdk7-b143 +23bdcede4e3945894574892e80b848bd9f15b5f3 jdk7-b144 diff -r 49aef5a5416e -r 1f39ca0b9598 make/common/Defs-embedded.gmk --- a/make/common/Defs-embedded.gmk Sat Jun 04 06:45:17 2011 -0700 +++ b/make/common/Defs-embedded.gmk Sat Jun 04 06:52:01 2011 -0700 @@ -49,5 +49,16 @@ JRE_REDUCED_HEADLESS_IMAGE_DIR = $(ABS_OUTPUTDIR)/j2re-reduced-headless-image JRE_REDUCED_IMAGE_DIR = $(ABS_OUTPUTDIR)/j2re-reduced-image +# Override on linux to further reduce binary/lib sizes in product build +ifeq ($(PLATFORM), linux) + ifeq ($(VARIANT), OPT) + ifneq ($(NO_STRIP), true) + ifneq ($(DEBUG_BINARIES), true) + POST_STRIP_PROCESS = $(STRIP) --strip-unneeded + endif + endif + endif +endif + endif # JAVASE_EMBEDDED diff -r 49aef5a5416e -r 1f39ca0b9598 make/common/Release-embedded.gmk --- a/make/common/Release-embedded.gmk Sat Jun 04 06:45:17 2011 -0700 +++ b/make/common/Release-embedded.gmk Sat Jun 04 06:52:01 2011 -0700 @@ -53,25 +53,30 @@ $(RM) $(HEADLESS_CLASSLIST) $(RM) $(NOT_HEADLESS_RT_JAR_LIST) $(CP) $(NOT_RT_JAR_LIST) $(NOT_HEADLESS_RT_JAR_LIST) +# List all the packages to be excluded $(ECHO) "sun/awt/motif/" >> $@ $(ECHO) "sun/awt/X11/" >> $@ $(ECHO) "sun/applet/" >> $@ $(ECHO) "sun/java2d/opengl/" >> $@ $(ECHO) "com/sun/java/swing/plaf/" >> $@ - $(ECHO) "sun/awt/motif/MFontConfiguration" >$(HEADLESS_CLASSLIST) - $(ECHO) "sun/applet/AppContextCreator" >>$(HEADLESS_CLASSLIST) - $(ECHO) "sun/applet/AppletAudioClip" >>$(HEADLESS_CLASSLIST) - $(ECHO) "sun/java2d/opengl/GLXSurfaceData" >>$(HEADLESS_CLASSLIST) - $(ECHO) "sun/java2d/opengl/GLXSurfaceData"\$$"GLXOffScreenSurfaceData" >>$(HEADLESS_CLASSLIST) - $(ECHO) "sun/java2d/opengl/GLXVolatileSurfaceManager" >>$(HEADLESS_CLASSLIST) - $(ECHO) "sun/java2d/opengl/OGLSurfaceData" >>$(HEADLESS_CLASSLIST) +# List all the individual classes to be included + $(ECHO) "sun/awt/motif/MFontConfiguration.class" >$(HEADLESS_CLASSLIST) + $(ECHO) "sun/applet/AppContextCreator.class" >>$(HEADLESS_CLASSLIST) + $(ECHO) "sun/applet/AppletAudioClip.class" >>$(HEADLESS_CLASSLIST) + $(ECHO) "sun/java2d/opengl/GLXSurfaceData.class" >>$(HEADLESS_CLASSLIST) + $(ECHO) "sun/java2d/opengl/GLXSurfaceData"\$$"GLXOffScreenSurfaceData.class" >>$(HEADLESS_CLASSLIST) + $(ECHO) "sun/java2d/opengl/GLXVolatileSurfaceManager.class" >>$(HEADLESS_CLASSLIST) + $(ECHO) "sun/java2d/opengl/OGLSurfaceData.class" >>$(HEADLESS_CLASSLIST) + $(TOTAL_HEADLESS_JAR_FILELIST): $(JARREORDER_JARFILE) $(NOT_HEADLESS_RT_JAR_LIST) $(prep-target) $(RM) $@.temp $(CD) $(CLASSBINDIR) ; \ $(BOOT_JAVA_CMD) -jar $(JARREORDER_JARFILE) \ - -o $@.temp $(HEADLESS_CLASSLIST) $(NOT_HEADLESS_RT_JAR_LIST) . + -o $@.temp - $(NOT_HEADLESS_RT_JAR_LIST) . +# Add on the explicitly included class files from the otherwise excluded packages + $(CAT) $(HEADLESS_CLASSLIST) >> $@.temp $(MV) $@.temp $@ @$(CD) $(CLASSBINDIR); $(java-vm-cleanup) @@ -124,13 +129,9 @@ $(LIBARCH)/libjavaplugin_nscp.so \ $(LIBARCH)/libjavaplugin_oji.so - -ifeq ($(PLATFORM), linux) - STRIP_OPTS = --strip-unneeded -else - STRIP_OPTS = -x -endif - +# JRE docs that don't get included in reduced jre image top directory +NOT_REDUCEDJRE_DOC = \ + Welcome.html reduced-image-jre:: @$(ECHO) Starting to Produce Reduced JRE @@ -142,12 +143,6 @@ $(CD) $(JRE_IMAGE_DIR); \ $(TAR) cf - . | ($(CD) $(JRE_REDUCED_IMAGE_DIR); $(TAR) xf - ); - @# strip the main .so files - $(STRIP) $(STRIP_OPTS) $(JRE_REDUCED_IMAGE_DIR)/lib/$(LIBARCH)/client/libjvm.so -ifndef BUILD_CLIENT_ONLY - $(STRIP) $(STRIP_OPTS) $(JRE_REDUCED_IMAGE_DIR)/lib/$(LIBARCH)/server/libjvm.so -endif - @# @# Remove all of the files that are not needed for the @# reduced JRE @@ -158,6 +153,9 @@ for l in $(NOT_REDUCEDJRE_LIB) ; do \ $(RM) $(JRE_REDUCED_IMAGE_DIR)/lib/$$l ; \ done + for l in $(NOT_REDUCEDJRE_DOC) ; do \ + $(RM) $(JRE_REDUCED_IMAGE_DIR)/$$l ; \ + done @# Remove misc. other files $(RM) -r $(JRE_REDUCED_IMAGE_DIR)/man diff -r 49aef5a5416e -r 1f39ca0b9598 src/linux/doc/man/ja/keytool.1 --- a/src/linux/doc/man/ja/keytool.1 Sat Jun 04 06:45:17 2011 -0700 +++ b/src/linux/doc/man/ja/keytool.1 Sat Jun 04 06:52:01 2011 -0700 @@ -1,4 +1,4 @@ -." Copyright (c) 1998-2011 keytool tool, Oracle and/or its affiliates. All rights reserved. +." Copyright (c) 1998, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/linux/doc/man/keytool.1 --- a/src/linux/doc/man/keytool.1 Sat Jun 04 06:45:17 2011 -0700 +++ b/src/linux/doc/man/keytool.1 Sat Jun 04 06:52:01 2011 -0700 @@ -1,4 +1,4 @@ -." Copyright (c) 1998-2011 keytool tool, Oracle and/or its affiliates. All rights reserved. +." Copyright (c) 1998, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk.properties --- a/src/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk.properties Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/com/sun/java/swing/plaf/gtk/resources/gtk.properties Sat Jun 04 06:52:01 2011 -0700 @@ -56,9 +56,9 @@ FileChooser.renameFileButtonText=Rename File FileChooser.renameFileButtonMnemonic=82 FileChooser.cancelButtonText=Cancel -#FileChooser.cancelButtonMnemonic=67 +FileChooser.cancelButtonMnemonic=67 FileChooser.saveButtonText=OK -#FileChooser.saveButtonMnemonic=79 +FileChooser.saveButtonMnemonic=79 FileChooser.openButtonText=OK FileChooser.openButtonMnemonic=79 FileChooser.saveDialogTitleText=Save @@ -79,5 +79,5 @@ FileChooser.renameFileErrorTitle=Error FileChooser.renameFileErrorText=Error renaming file "{0}" to "{1}" -#OptionPane.okButtonMnemonic=79 -#OptionPane.cancelButtonMnemonic=67 +OptionPane.okButtonMnemonic=79 +OptionPane.cancelButtonMnemonic=67 diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/awt/Toolkit.java --- a/src/share/classes/java/awt/Toolkit.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/awt/Toolkit.java Sat Jun 04 06:52:01 2011 -0700 @@ -1857,7 +1857,7 @@ /** * Adds the specified property change listener for the named desktop - * property. When a {@link PropertyChangeListenerProxy} object is added, + * property. When a {@link java.beans.PropertyChangeListenerProxy} object is added, * its property name is ignored, and the wrapped listener is added. * If {@code name} is {@code null} or {@code pcl} is {@code null}, * no exception is thrown and no action is performed. @@ -1874,7 +1874,7 @@ /** * Removes the specified property change listener for the named - * desktop property. When a {@link PropertyChangeListenerProxy} object + * desktop property. When a {@link java.beans.PropertyChangeListenerProxy} object * is removed, its property name is ignored, and * the wrapped listener is removed. * If {@code name} is {@code null} or {@code pcl} is {@code null}, @@ -1893,11 +1893,11 @@ /** * Returns an array of all the property change listeners * registered on this toolkit. The returned array - * contains {@code PropertyChangeListenerProxy} objects + * contains {@link java.beans.PropertyChangeListenerProxy} objects * that associate listeners with the names of desktop properties. * - * @return all of this toolkit's {@ code PropertyChangeListener} - * objects wrapped in {@code PropertyChangeListenerProxy} objects + * @return all of this toolkit's {@link PropertyChangeListener} + * objects wrapped in {@code java.beans.PropertyChangeListenerProxy} objects * or an empty array if no listeners are added * * @see PropertyChangeSupport#getPropertyChangeListeners() diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/io/SerialCallbackContext.java --- a/src/share/classes/java/io/SerialCallbackContext.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/io/SerialCallbackContext.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,7 +1,5 @@ /* - * %W% %E% - * - * Copyright (c) 2006, 2010 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/BootstrapMethodError.java --- a/src/share/classes/java/lang/BootstrapMethodError.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/BootstrapMethodError.java Sat Jun 04 06:52:01 2011 -0700 @@ -39,14 +39,14 @@ private static final long serialVersionUID = 292L; /** - * Constructs an {@code BootstrapMethodError} with no detail message. + * Constructs a {@code BootstrapMethodError} with no detail message. */ public BootstrapMethodError() { super(); } /** - * Constructs an {@code BootstrapMethodError} with the specified + * Constructs a {@code BootstrapMethodError} with the specified * detail message. * * @param s the detail message. diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/ClassValue.java --- a/src/share/classes/java/lang/ClassValue.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/ClassValue.java Sat Jun 04 06:52:01 2011 -0700 @@ -39,6 +39,13 @@ */ public abstract class ClassValue { /** + * Sole constructor. (For invocation by subclass constructors, typically + * implicit.) + */ + protected ClassValue() { + } + + /** * Computes the given class's derived value for this {@code ClassValue}. *

* This method will be invoked within the first thread that accesses @@ -100,7 +107,7 @@ * If this value is subsequently {@linkplain #get read} for the same class, * its value will be reinitialized by invoking its {@link #computeValue computeValue} method. * This may result in an additional invocation of the - * {@code computeValue computeValue} method for the given class. + * {@code computeValue} method for the given class. *

* In order to explain the interaction between {@code get} and {@code remove} calls, * we must model the state transitions of a class value to take into account @@ -193,6 +200,7 @@ = new WeakHashMap, ClassValueMap>(); private static ClassValueMap getMap(Class type) { + type.getClass(); // test for null return ROOT.get(type); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/AdapterMethodHandle.java --- a/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Sat Jun 04 06:52:01 2011 -0700 @@ -29,6 +29,8 @@ import sun.invoke.util.Wrapper; import sun.invoke.util.ValueConversions; import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; @@ -62,7 +64,7 @@ // the target and change its type, instead of adding another layer. /** Can a JVM-level adapter directly implement the proposed - * argument conversions, as if by MethodHandles.convertArguments? + * argument conversions, as if by fixed-arity MethodHandle.asType? */ static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) { // same number of args, of course @@ -92,7 +94,7 @@ } /** Can a JVM-level adapter directly implement the proposed - * argument conversion, as if by MethodHandles.convertArguments? + * argument conversion, as if by fixed-arity MethodHandle.asType? */ static boolean canConvertArgument(Class src, Class dst, int level) { // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes, @@ -141,7 +143,7 @@ while (lastConv >= 0) { Class src = newType.parameterType(lastConv); // source type Class dst = oldType.parameterType(lastConv); // destination type - if (VerifyType.isNullConversion(src, dst)) { + if (isTrivialConversion(src, dst, level)) { --lastConv; } else { break; @@ -150,7 +152,7 @@ Class needReturn = newType.returnType(); Class haveReturn = oldType.returnType(); - boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn); + boolean retConv = !isTrivialConversion(haveReturn, needReturn, level); // Now build a chain of one or more adapters. MethodHandle adapter = target, adapter2; @@ -158,7 +160,7 @@ for (int i = 0; i <= lastConv; i++) { Class src = newType.parameterType(i); // source type Class dst = midType.parameterType(i); // destination type - if (VerifyType.isNullConversion(src, dst)) { + if (isTrivialConversion(src, dst, level)) { // do nothing: difference is trivial continue; } @@ -219,6 +221,22 @@ return adapter; } + private static boolean isTrivialConversion(Class src, Class dst, int level) { + if (src == dst || dst == void.class) return true; + if (!VerifyType.isNullConversion(src, dst)) return false; + if (level > 1) return true; // explicitCastArguments + boolean sp = src.isPrimitive(); + boolean dp = dst.isPrimitive(); + if (sp != dp) return false; + if (sp) { + // in addition to being a null conversion, forbid boolean->int etc. + return Wrapper.forPrimitiveType(dst) + .isConvertibleFrom(Wrapper.forPrimitiveType(src)); + } else { + return dst.isAssignableFrom(src); + } + } + private static MethodHandle makeReturnConversion(MethodHandle target, Class haveReturn, Class needReturn) { MethodHandle adjustReturn; if (haveReturn == void.class) { @@ -530,6 +548,11 @@ } static MethodHandle makeVarargsCollector(MethodHandle target, Class arrayType) { + MethodType type = target.type(); + int last = type.parameterCount() - 1; + if (type.parameterType(last) != arrayType) + target = target.asType(type.changeParameterType(last, arrayType)); + target = target.asFixedArity(); // make sure this attribute is turned off return new AsVarargsCollector(target, arrayType); } @@ -551,6 +574,11 @@ } @Override + public MethodHandle asFixedArity() { + return target; + } + + @Override public MethodHandle asType(MethodType newType) { MethodType type = this.type(); int collectArg = type.parameterCount() - 1; @@ -574,14 +602,6 @@ cache = collector; return collector.asType(newType); } - - @Override - public MethodHandle asVarargsCollector(Class arrayType) { - MethodType type = this.type(); - if (type.parameterType(type.parameterCount()-1) == arrayType) - return this; - return super.asVarargsCollector(arrayType); - } } /** Can a checkcast adapter validly convert the target to newType? @@ -596,7 +616,7 @@ || !VerifyType.isNullConversion(castType, dst)) return false; int diff = diffTypes(newType, targetType, false); - return (diff == arg+1); // arg is sole non-trivial diff + return (diff == arg+1) || (diff == 0); // arg is sole non-trivial diff } /** Can an primitive conversion adapter validly convert src to dst? */ static boolean canCheckCast(Class src, Class dst) { @@ -727,8 +747,31 @@ if (!canUnboxArgument(newType, oldType, arg, convType, level)) return null; MethodType castDone = newType; - if (!VerifyType.isNullConversion(src, boxType)) + if (!VerifyType.isNullConversion(src, boxType)) { + // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int + if (level != 0) { + // must include additional conversions + if (src == Object.class || !Wrapper.isWrapperType(src)) { + // src must be examined at runtime, to detect Byte, Character, etc. + MethodHandle unboxMethod = (level == 1 + ? ValueConversions.unbox(dst) + : ValueConversions.unboxCast(dst)); + long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst)); + return new AdapterMethodHandle(target, newType, conv, unboxMethod); + } + // Example: Byte->int + // Do this by reformulating the problem to Byte->byte. + Class srcPrim = Wrapper.forWrapperType(src).primitiveType(); + MethodType midType = newType.changeParameterType(arg, srcPrim); + MethodHandle fixPrim; // makePairwiseConvert(midType, target, 0); + if (canPrimCast(midType, oldType, arg, dst)) + fixPrim = makePrimCast(midType, target, arg, dst); + else + fixPrim = target; + return makeUnboxArgument(newType, fixPrim, arg, srcPrim, 0); + } castDone = newType.changeParameterType(arg, boxType); + } long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType)); MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType); if (castDone == newType) @@ -897,6 +940,20 @@ if (swapArg1 == swapArg2) return target; if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; } + if (type2size(newType.parameterType(swapArg1)) != + type2size(newType.parameterType(swapArg2))) { + // turn a swap into a pair of rotates: + // [x a b c y] => [a b c y x] => [y a b c x] + int argc = swapArg2 - swapArg1 + 1; + final int ROT = 1; + ArrayList> rot1Params = new ArrayList>(target.type().parameterList()); + Collections.rotate(rot1Params.subList(swapArg1, swapArg1 + argc), -ROT); + MethodType rot1Type = MethodType.methodType(target.type().returnType(), rot1Params); + MethodHandle rot1 = makeRotateArguments(rot1Type, target, swapArg1, argc, +ROT); + if (argc == 2) return rot1; + MethodHandle rot2 = makeRotateArguments(newType, rot1, swapArg1, argc-1, -ROT); + return rot2; + } if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2)) return null; Class swapType = newType.parameterType(swapArg1); @@ -926,7 +983,6 @@ static boolean canRotateArguments(MethodType newType, MethodType targetType, int firstArg, int argCount, int rotateBy) { if (!convOpSupported(OP_ROT_ARGS)) return false; - if (argCount <= 2) return false; // must be a swap, not a rotate rotateBy = positiveRotation(argCount, rotateBy); if (rotateBy == 0) return false; // no rotation if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION) @@ -972,6 +1028,7 @@ // From here on out, it assumes a single-argument shift. assert(MAX_ARG_ROTATION == 1); int srcArg, dstArg; + int dstSlot; byte basicType; if (chunk2Slots <= chunk1Slots) { // Rotate right/down N (rotateBy = +N, N small, c2 small): @@ -979,6 +1036,7 @@ // out arglist: [0: ...keep1 | arg1: c2 | arg1+N: c1... | limit: keep2... ] srcArg = limit-1; dstArg = firstArg; + dstSlot = depth0 - chunk2Slots; basicType = basicType(newType.parameterType(srcArg)); assert(chunk2Slots == type2size(basicType)); } else { @@ -987,10 +1045,10 @@ // out arglist: [0: ...keep1 | arg1: c2 ... | limit-N: c1 | limit: keep2... ] srcArg = firstArg; dstArg = limit-1; + dstSlot = depth2; basicType = basicType(newType.parameterType(srcArg)); assert(chunk1Slots == type2size(basicType)); } - int dstSlot = newType.parameterSlotDepth(dstArg + 1); long conv = makeSwapConv(OP_ROT_ARGS, srcArg, basicType, dstSlot); return new AdapterMethodHandle(target, newType, conv); } @@ -1033,8 +1091,9 @@ Class spreadArgType, int spreadArgPos, int spreadArgCount) { // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType MethodType targetType = target.type(); - if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount)) - return null; + assert(canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount)) + : "[newType, targetType, spreadArgType, spreadArgPos, spreadArgCount] = " + + Arrays.asList(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount); // dest is not significant; remove? int dest = T_VOID; for (int i = 0; i < spreadArgCount; i++) { @@ -1127,7 +1186,7 @@ } @Override - public String toString() { + String debugString() { return getNameString(nonAdapter((MethodHandle)vmtarget), this); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/BoundMethodHandle.java --- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java Sat Jun 04 06:52:01 2011 -0700 @@ -151,11 +151,11 @@ final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) { String atype = (argument == null) ? "null" : argument.getClass().toString(); - return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type()); + return new ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type()); } @Override - public String toString() { + String debugString() { return addTypeString(baseName(), this); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/CallSite.java --- a/src/share/classes/java/lang/invoke/CallSite.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/CallSite.java Sat Jun 04 06:52:01 2011 -0700 @@ -111,7 +111,7 @@ } /** - * Make a blank call site object, possibly equipped with an initial target method handle. + * Make a call site object equipped with an initial target method handle. * @param target the method handle which will be the initial target of the call site * @throws NullPointerException if the proposed target is null */ @@ -122,6 +122,25 @@ } /** + * Make a call site object equipped with an initial target method handle. + * @param targetType the desired type of the call site + * @param createTargetHook a hook which will bind the call site to the target method handle + * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments, + * or if the target returned by the hook is not of the given {@code targetType} + * @throws NullPointerException if the hook returns a null value + * @throws ClassCastException if the hook returns something other than a {@code MethodHandle} + * @throws Throwable anything else thrown by the the hook function + */ + /*package-private*/ + CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable { + this(targetType); + ConstantCallSite selfCCS = (ConstantCallSite) this; + MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS); + checkTargetChange(this.target, boundTarget); + this.target = boundTarget; + } + + /** * Returns the type of this call site's target. * Although targets may change, any call site's type is permanent, and can never change to an unequal type. * The {@code setTarget} method enforces this invariant by refusing any new target that does @@ -129,6 +148,7 @@ * @return the type of the current target, which is also the type of any future target */ public MethodType type() { + // warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException return target.type(); } @@ -294,8 +314,8 @@ } else { throw new ClassCastException("bootstrap method failed to produce a CallSite"); } - assert(site.getTarget() != null); - assert(site.getTarget().type().equals(type)); + if (!site.getTarget().type().equals(type)) + throw new WrongMethodTypeException("wrong type: "+site.getTarget()); } catch (Throwable ex) { BootstrapMethodError bex; if (ex instanceof BootstrapMethodError) diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/ConstantCallSite.java --- a/src/share/classes/java/lang/invoke/ConstantCallSite.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/ConstantCallSite.java Sat Jun 04 06:52:01 2011 -0700 @@ -32,6 +32,8 @@ * @author John Rose, JSR 292 EG */ public class ConstantCallSite extends CallSite { + private final boolean isFrozen; + /** * Creates a call site with a permanent target. * @param target the target to be permanently associated with this call site @@ -39,6 +41,45 @@ */ public ConstantCallSite(MethodHandle target) { super(target); + isFrozen = true; + } + + /** + * Creates a call site with a permanent target, possibly bound to the call site itself. + *

+ * During construction of the call site, the {@code createTargetHook} is invoked to + * produce the actual target, as if by a call of the form + * {@code (MethodHandle) createTargetHook.invoke(this)}. + *

+ * Note that user code cannot perform such an action directly in a subclass constructor, + * since the target must be fixed before the {@code ConstantCallSite} constructor returns. + *

+ * The hook is said to bind the call site to a target method handle, + * and a typical action would be {@code someTarget.bindTo(this)}. + * However, the hook is free to take any action whatever, + * including ignoring the call site and returning a constant target. + *

+ * The result returned by the hook must be a method handle of exactly + * the same type as the call site. + *

+ * While the hook is being called, the new {@code ConstantCallSite} + * object is in a partially constructed state. + * In this state, + * a call to {@code getTarget}, or any other attempt to use the target, + * will result in an {@code IllegalStateException}. + * It is legal at all times to obtain the call site's type using the {@code type} method. + * + * @param targetType the type of the method handle to be permanently associated with this call site + * @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target + * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments, + * or if the target returned by the hook is not of the given {@code targetType} + * @throws NullPointerException if the hook returns a null value + * @throws ClassCastException if the hook returns something other than a {@code MethodHandle} + * @throws Throwable anything else thrown by the the hook function + */ + protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable { + super(targetType, createTargetHook); + isFrozen = true; } /** @@ -48,9 +89,10 @@ * to the constructor call which created this instance. * * @return the immutable linkage state of this call site, a constant method handle - * @throws UnsupportedOperationException because this kind of call site cannot change its target + * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed */ @Override public final MethodHandle getTarget() { + if (!isFrozen) throw new IllegalStateException(); return target; } @@ -61,7 +103,7 @@ * @throws UnsupportedOperationException because this kind of call site cannot change its target */ @Override public final void setTarget(MethodHandle ignore) { - throw new UnsupportedOperationException("ConstantCallSite"); + throw new UnsupportedOperationException(); } /** @@ -69,6 +111,7 @@ * Since that target will never change, this is a correct implementation * of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}. * @return the immutable linkage state of this call site, a constant method handle + * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed */ @Override public final MethodHandle dynamicInvoker() { diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/FilterGeneric.java --- a/src/share/classes/java/lang/invoke/FilterGeneric.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/FilterGeneric.java Sat Jun 04 06:52:01 2011 -0700 @@ -234,7 +234,7 @@ protected final MethodHandle target; // ultimate target @Override - public String toString() { + String debugString() { return addTypeString(target, this); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/FilterOneArgument.java --- a/src/share/classes/java/lang/invoke/FilterOneArgument.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/FilterOneArgument.java Sat Jun 04 06:52:01 2011 -0700 @@ -41,7 +41,7 @@ protected final MethodHandle target; // Object -> Object @Override - public String toString() { + String debugString() { return target.toString(); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/FromGeneric.java --- a/src/share/classes/java/lang/invoke/FromGeneric.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/FromGeneric.java Sat Jun 04 06:52:01 2011 -0700 @@ -260,7 +260,7 @@ protected final MethodHandle target; // (any**N) => R @Override - public String toString() { + String debugString() { return addTypeString(target, this); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/InvokeGeneric.java --- a/src/share/classes/java/lang/invoke/InvokeGeneric.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/InvokeGeneric.java Sat Jun 04 06:52:01 2011 -0700 @@ -129,20 +129,17 @@ if (needType == erasedCallerType.returnType()) return false; // no conversions possible, since must be primitive or Object Class haveType = target.type().returnType(); - if (VerifyType.isNullConversion(haveType, needType)) + if (VerifyType.isNullConversion(haveType, needType) && !needType.isInterface()) return false; return true; } - private MethodHandle addReturnConversion(MethodHandle target, Class type) { - if (true) throw new RuntimeException("NYI"); + private MethodHandle addReturnConversion(MethodHandle finisher, Class type) { // FIXME: This is slow because it creates a closure node on every call that requires a return cast. - MethodType targetType = target.type(); + MethodType finisherType = finisher.type(); MethodHandle caster = ValueConversions.identity(type); - caster = caster.asType(MethodType.methodType(type, targetType.returnType())); - // Drop irrelevant arguments, because we only care about the return value: - caster = MethodHandles.dropArguments(caster, 1, targetType.parameterList()); - MethodHandle result = MethodHandles.foldArguments(caster, target); - return result.asType(target.type()); + caster = caster.asType(caster.type().changeParameterType(0, finisherType.returnType())); + finisher = MethodHandles.filterReturnValue(finisher, caster); + return finisher.asType(finisherType); } public String toString() { diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/Invokers.java --- a/src/share/classes/java/lang/invoke/Invokers.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/Invokers.java Sat Jun 04 06:52:01 2011 -0700 @@ -46,7 +46,10 @@ // general invoker for the outgoing call private /*lazy*/ MethodHandle generalInvoker; - // general invoker for the outgoing call; accepts a single Object[] + // general invoker for the outgoing call, uses varargs + private /*lazy*/ MethodHandle varargsInvoker; + + // general invoker for the outgoing call; accepts a trailing Object[] private final /*lazy*/ MethodHandle[] spreadInvokers; // invoker for an unbound callsite @@ -67,45 +70,56 @@ /*non-public*/ MethodHandle exactInvoker() { MethodHandle invoker = exactInvoker; if (invoker != null) return invoker; - try { - invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType); - } catch (ReflectiveOperationException ex) { - throw new InternalError("JVM cannot find invoker for "+targetType); - } - assert(invokerType(targetType) == invoker.type()); + invoker = lookupInvoker("invokeExact"); exactInvoker = invoker; return invoker; } /*non-public*/ MethodHandle generalInvoker() { - MethodHandle invoker1 = exactInvoker(); MethodHandle invoker = generalInvoker; if (invoker != null) return invoker; - MethodType generalType = targetType.generic(); - invoker = invoker1.asType(invokerType(generalType)); + invoker = lookupInvoker("invoke"); generalInvoker = invoker; return invoker; } + private MethodHandle lookupInvoker(String name) { + MethodHandle invoker; + try { + invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, name, targetType); + } catch (ReflectiveOperationException ex) { + throw new InternalError("JVM cannot find invoker for "+targetType); + } + assert(invokerType(targetType) == invoker.type()); + assert(!invoker.isVarargsCollector()); + return invoker; + } + /*non-public*/ MethodHandle erasedInvoker() { - MethodHandle invoker1 = exactInvoker(); + MethodHandle xinvoker = exactInvoker(); MethodHandle invoker = erasedInvoker; if (invoker != null) return invoker; MethodType erasedType = targetType.erase(); - if (erasedType == targetType.generic()) - invoker = generalInvoker(); - else - invoker = invoker1.asType(invokerType(erasedType)); + invoker = xinvoker.asType(invokerType(erasedType)); erasedInvoker = invoker; return invoker; } - /*non-public*/ MethodHandle spreadInvoker(int objectArgCount) { - MethodHandle vaInvoker = spreadInvokers[objectArgCount]; + /*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) { + MethodHandle vaInvoker = spreadInvokers[leadingArgCount]; if (vaInvoker != null) return vaInvoker; MethodHandle gInvoker = generalInvoker(); - vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount); - spreadInvokers[objectArgCount] = vaInvoker; + int spreadArgCount = targetType.parameterCount() - leadingArgCount; + vaInvoker = gInvoker.asSpreader(Object[].class, spreadArgCount); + spreadInvokers[leadingArgCount] = vaInvoker; + return vaInvoker; + } + + /*non-public*/ MethodHandle varargsInvoker() { + MethodHandle vaInvoker = varargsInvoker; + if (vaInvoker != null) return vaInvoker; + vaInvoker = spreadInvoker(0).asType(invokerType(MethodType.genericMethodType(0, true))); + varargsInvoker = vaInvoker; return vaInvoker; } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MemberName.java --- a/src/share/classes/java/lang/invoke/MemberName.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MemberName.java Sat Jun 04 06:52:01 2011 -0700 @@ -506,8 +506,18 @@ if (from != null) message += ", from " + from; return new IllegalAccessException(message); } - public ReflectiveOperationException makeAccessException(String message) { - message = message + ": "+ toString(); + private String message() { + if (isResolved()) + return "no access"; + else if (isConstructor()) + return "no such constructor"; + else if (isMethod()) + return "no such method"; + else + return "no such field"; + } + public ReflectiveOperationException makeAccessException() { + String message = message() + ": "+ toString(); if (isResolved()) return new IllegalAccessException(message); else if (isConstructor()) @@ -641,7 +651,7 @@ MemberName result = resolveOrNull(m, searchSupers, lookupClass); if (result != null) return result; - ReflectiveOperationException ex = m.makeAccessException("no access"); + ReflectiveOperationException ex = m.makeAccessException(); if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex; throw nsmClass.cast(ex); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodHandle.java --- a/src/share/classes/java/lang/invoke/MethodHandle.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandle.java Sat Jun 04 06:52:01 2011 -0700 @@ -26,6 +26,7 @@ package java.lang.invoke; +import java.util.ArrayList; import sun.invoke.util.ValueConversions; import static java.lang.invoke.MethodHandleStatics.*; @@ -40,12 +41,12 @@ * and {@linkplain java.lang.invoke.MethodHandles#filterArguments substitution}. * *

Method handle contents

- * Method handles are dynamically and strongly typed according to type descriptor. - * They are not distinguished by the name or defining class of their underlying methods. - * A method handle must be invoked using type descriptor which matches - * the method handle's own {@linkplain #type method type}. + * Method handles are dynamically and strongly typed according to their parameter and return types. + * They are not distinguished by the name or the defining class of their underlying methods. + * A method handle must be invoked using a symbolic type descriptor which matches + * the method handle's own {@linkplain #type type descriptor}. *

- * Every method handle reports its type via the {@link #type type} accessor. + * Every method handle reports its type descriptor via the {@link #type type} accessor. * This type descriptor is a {@link java.lang.invoke.MethodType MethodType} object, * whose structure is a series of classes, one of which is * the return type of the method (or {@code void.class} if none). @@ -82,7 +83,7 @@ * From the viewpoint of source code, these methods can take any arguments * and their result can be cast to any return type. * Formally this is accomplished by giving the invoker methods - * {@code Object} return types and variable-arity {@code Object} arguments, + * {@code Object} return types and variable arity {@code Object} arguments, * but they have an additional quality called signature polymorphism * which connects this freedom of invocation directly to the JVM execution stack. *

@@ -92,17 +93,17 @@ * and may not perform method invocation conversions on the arguments. * Instead, it must push them on the stack according to their own unconverted types. * The method handle object itself is pushed on the stack before the arguments. - * The compiler then calls the method handle with a type descriptor which + * The compiler then calls the method handle with a symbolic type descriptor which * describes the argument and return types. *

- * To issue a complete type descriptor, the compiler must also determine + * To issue a complete symbolic type descriptor, the compiler must also determine * the return type. This is based on a cast on the method invocation expression, * if there is one, or else {@code Object} if the invocation is an expression * or else {@code void} if the invocation is a statement. * The cast may be to a primitive type (but not {@code void}). *

* As a corner case, an uncasted {@code null} argument is given - * a type descriptor of {@code java.lang.Void}. + * a symbolic type descriptor of {@code java.lang.Void}. * The ambiguity with the type {@code Void} is harmless, since there are no references of type * {@code Void} except the null reference. * @@ -111,16 +112,16 @@ * it is linked, by symbolically resolving the names in the instruction * and verifying that the method call is statically legal. * This is true of calls to {@code invokeExact} and {@code invoke}. - * In this case, the type descriptor emitted by the compiler is checked for + * In this case, the symbolic type descriptor emitted by the compiler is checked for * correct syntax and names it contains are resolved. * Thus, an {@code invokevirtual} instruction which invokes * a method handle will always link, as long - * as the type descriptor is syntactically well-formed + * as the symbolic type descriptor is syntactically well-formed * and the types exist. *

* When the {@code invokevirtual} is executed after linking, * the receiving method handle's type is first checked by the JVM - * to ensure that it matches the descriptor. + * to ensure that it matches the symbolic type descriptor. * If the type match fails, it means that the method which the * caller is invoking is not present on the individual * method handle being invoked. @@ -137,7 +138,7 @@ * (or other behavior, as the case may be). *

* A call to plain {@code invoke} works the same as a call to - * {@code invokeExact}, if the type descriptor specified by the caller + * {@code invokeExact}, if the symbolic type descriptor specified by the caller * exactly matches the method handle's own type. * If there is a type mismatch, {@code invoke} attempts * to adjust the type of the receiving method handle, @@ -164,9 +165,9 @@ * method type matching takes into account both types names and class loaders. * Thus, even if a method handle {@code M} is created in one * class loader {@code L1} and used in another {@code L2}, - * method handle calls are type-safe, because the caller's type + * method handle calls are type-safe, because the caller's symbolic type * descriptor, as resolved in {@code L2}, - * is matched against the original callee method's type descriptor, + * is matched against the original callee method's symbolic type descriptor, * as resolved in {@code L1}. * The resolution in {@code L1} happens when {@code M} is created * and its type is assigned, while the resolution in {@code L2} happens @@ -242,24 +243,24 @@ mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("daddy",'d','n'); // invokeExact(Ljava/lang/String;CC)Ljava/lang/String; -assert(s.equals("nanny")); +assertEquals(s, "nanny"); // weakly typed invocation (using MHs.invoke) s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); -assert(s.equals("savvy")); +assertEquals(s, "savvy"); // mt is (Object[])List mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); x = mh.invoke("one", "two"); // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; -assert(x.equals(java.util.Arrays.asList("one","two"))); +assertEquals(x, java.util.Arrays.asList("one","two")); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); mh = mh.asType(mt); x = mh.invokeExact((Object)1, (Object)2, (Object)3); // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -assert(x.equals(java.util.Arrays.asList(1,2,3))); -// mt is int() +assertEquals(x, java.util.Arrays.asList(1,2,3)); +// mt is ()int mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); @@ -272,7 +273,10 @@ * * Each of the above calls to {@code invokeExact} or plain {@code invoke} * generates a single invokevirtual instruction with - * the type descriptor indicated in the following comment. + * the symbolic type descriptor indicated in the following comment. + * In these examples, the helper method {@code assertEquals} is assumed to + * be a method which calls {@link Objects.equals java.util.Objects#equals} + * on its arguments, and asserts that the result is true. * *

Exceptions

* The methods {@code invokeExact} and {@code invoke} are declared @@ -283,7 +287,7 @@ * there is no particular effect on bytecode shape from ascribing * checked exceptions to method handle invocations. But in Java source * code, methods which perform method handle calls must either explicitly - * throw {@code java.lang.Throwable Throwable}, or else must catch all + * throw {@code Throwable}, or else must catch all * throwables locally, rethrowing only those which are legal in the context, * and wrapping ones which are illegal. * @@ -291,77 +295,26 @@ * The unusual compilation and linkage behavior of * {@code invokeExact} and plain {@code invoke} * is referenced by the term signature polymorphism. - * A signature polymorphic method is one which can operate with + * As defined in the Java Language Specification, + * a signature polymorphic method is one which can operate with * any of a wide range of call signatures and return types. - * In order to make this work, both the Java compiler and the JVM must - * give special treatment to signature polymorphic methods. *

* In source code, a call to a signature polymorphic method will - * compile, regardless of the requested type descriptor. + * compile, regardless of the requested symbolic type descriptor. * As usual, the Java compiler emits an {@code invokevirtual} - * instruction with the given type descriptor against the named method. - * The unusual part is that the type descriptor is derived from + * instruction with the given symbolic type descriptor against the named method. + * The unusual part is that the symbolic type descriptor is derived from * the actual argument and return types, not from the method declaration. *

* When the JVM processes bytecode containing signature polymorphic calls, - * it will successfully link any such call, regardless of its type descriptor. + * it will successfully link any such call, regardless of its symbolic type descriptor. * (In order to retain type safety, the JVM will guard such calls with suitable * dynamic type checks, as described elsewhere.) *

* Bytecode generators, including the compiler back end, are required to emit - * untransformed type descriptors for these methods. + * untransformed symbolic type descriptors for these methods. * Tools which determine symbolic linkage are required to accept such * untransformed descriptors, without reporting linkage errors. - *

- * For the sake of tools (but not as a programming API), the signature polymorphic - * methods are marked with a private yet standard annotation, - * {@code @java.lang.invoke.MethodHandle.PolymorphicSignature}. - * The annotation's retention is {@code RUNTIME}, so that all tools can see it. - * - *

Formal rules for processing signature polymorphic methods

- *

- * The following methods (and no others) are signature polymorphic: - *

- *

- * A signature polymorphic method will be declared with the following properties: - *

- * Because of these requirements, a signature polymorphic method is able to accept - * any number and type of actual arguments, and can, with a cast, produce a value of any type. - * However, the JVM will treat these declaration features as a documentation convention, - * rather than a description of the actual structure of the methods as executed. - *

- * When a call to a signature polymorphic method is compiled, the associated linkage information for - * its arguments is not array of {@code Object} (as for other similar varargs methods) - * but rather the erasure of the static types of all the arguments. - *

- * In an argument position of a method invocation on a signature polymorphic method, - * a null literal has type {@code java.lang.Void}, unless cast to a reference type. - * (Note: This typing rule allows the null type to have its own encoding in linkage information - * distinct from other types. - *

- * The linkage information for the return type is derived from a context-dependent target typing convention. - * The return type for a signature polymorphic method invocation is determined as follows: - *

- * (Programmers are encouraged to use explicit casts unless it is clear that a signature polymorphic - * call will be used as a plain {@code Object} expression.) - *

- * The linkage information for argument and return types is stored in the descriptor for the - * compiled (bytecode) call site. As for any invocation instruction, the arguments and return value - * will be passed directly on the JVM stack, in accordance with the descriptor, - * and without implicit boxing or unboxing. * *

Interoperation between method handles and the Core Reflection API

* Using factory methods in the {@link java.lang.invoke.MethodHandles.Lookup Lookup} API, @@ -385,14 +338,14 @@ * declared method, including in this case {@code native} and {@code varargs} bits. *

* As with any reflected method, these methods (when reflected) may be - * invoked via {@link java.lang.reflect.Method#invoke Method.invoke}. + * invoked via {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}. * However, such reflective calls do not result in method handle invocations. * Such a call, if passed the required argument * (a single one, of type {@code Object[]}), will ignore the argument and * will throw an {@code UnsupportedOperationException}. *

* Since {@code invokevirtual} instructions can natively - * invoke method handles under any type descriptor, this reflective view conflicts + * invoke method handles under any symbolic type descriptor, this reflective view conflicts * with the normal presentation of these methods via bytecodes. * Thus, these two native methods, when reflectively viewed by * {@code Class.getDeclaredMethod}, may be regarded as placeholders only. @@ -413,7 +366,7 @@ * When a method handle is invoked, the types of its arguments * or the return value cast type may be generic types or type instances. * If this occurs, the compiler will replace those - * types by their erasures when when it constructs the type descriptor + * types by their erasures when it constructs the symbolic type descriptor * for the {@code invokevirtual} instruction. *

* Method handles do not represent @@ -502,17 +455,17 @@ /** * Invokes the method handle, allowing any caller type descriptor, but requiring an exact type match. - * The type descriptor at the call site of {@code invokeExact} must + * The symbolic type descriptor at the call site of {@code invokeExact} must * exactly match this method handle's {@link #type type}. * No conversions are allowed on arguments or return values. *

* When this method is observed via the Core Reflection API, * it will appear as a single native method, taking an object array and returning an object. * If this native method is invoked directly via - * {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI, + * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI, * or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}, * it will throw an {@code UnsupportedOperationException}. - * @throws WrongMethodTypeException if the target's type is not identical with the caller's type descriptor + * @throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call */ public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable; @@ -521,7 +474,7 @@ * Invokes the method handle, allowing any caller type descriptor, * and optionally performing conversions on arguments and return values. *

- * If the call site type descriptor exactly matches this method handle's {@link #type type}, + * If the call site's symbolic type descriptor exactly matches this method handle's {@link #type type}, * the call proceeds as if by {@link #invokeExact invokeExact}. *

* Otherwise, the call proceeds as if this method handle were first @@ -534,7 +487,7 @@ * adaptations directly on the caller's arguments, * and call the target method handle according to its own exact type. *

- * The type descriptor at the call site of {@code invoke} must + * The resolved type descriptor at the call site of {@code invoke} must * be a valid argument to the receivers {@code asType} method. * In particular, the caller must specify the same argument arity * as the callee's type, @@ -543,24 +496,17 @@ * When this method is observed via the Core Reflection API, * it will appear as a single native method, taking an object array and returning an object. * If this native method is invoked directly via - * {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI, + * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI, * or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}, * it will throw an {@code UnsupportedOperationException}. - * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type descriptor + * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's symbolic type descriptor * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call */ public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable; /** - * Temporary alias for {@link #invoke}, for backward compatibility with some versions of JSR 292. - * On some JVMs, support can be excluded by the flags {@code -XX:+UnlockExperimentalVMOptions -XX:-AllowInvokeGeneric}. - * @deprecated Will be removed for JSR 292 Proposed Final Draft. - */ - public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable; - - /** - * Performs a varargs invocation, passing the arguments in the given array + * Performs a variable arity invocation, passing the arguments in the given array * to the method handle, as if via an inexact {@link #invoke invoke} from a call site * which mentions only the type {@code Object}, and whose arity is the length * of the argument array. @@ -612,56 +558,16 @@ public Object invokeWithArguments(Object... arguments) throws Throwable { int argc = arguments == null ? 0 : arguments.length; MethodType type = type(); - if (type.parameterCount() != argc) { + if (type.parameterCount() != argc || isVarargsCollector()) { // simulate invoke return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments); } - if (argc <= 10) { - MethodHandle invoker = type.invokers().generalInvoker(); - switch (argc) { - case 0: return invoker.invokeExact(this); - case 1: return invoker.invokeExact(this, - arguments[0]); - case 2: return invoker.invokeExact(this, - arguments[0], arguments[1]); - case 3: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2]); - case 4: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3]); - case 5: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4]); - case 6: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4], arguments[5]); - case 7: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4], arguments[5], - arguments[6]); - case 8: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4], arguments[5], - arguments[6], arguments[7]); - case 9: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4], arguments[5], - arguments[6], arguments[7], arguments[8]); - case 10: return invoker.invokeExact(this, - arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4], arguments[5], - arguments[6], arguments[7], arguments[8], - arguments[9]); - } - } - - // more than ten arguments get boxed in a varargs list: - MethodHandle invoker = type.invokers().spreadInvoker(0); + MethodHandle invoker = type.invokers().varargsInvoker(); return invoker.invokeExact(this, arguments); } /** - * Performs a varargs invocation, passing the arguments in the given array + * Performs a variable arity invocation, passing the arguments in the given array * to the method handle, as if via an inexact {@link #invoke invoke} from a call site * which mentions only the type {@code Object}, and whose arity is the length * of the argument array. @@ -673,6 +579,7 @@ * * @param arguments the arguments to pass to the target * @return the result returned by the target + * @throws NullPointerException if {@code arguments} is a null reference * @throws ClassCastException if an argument cannot be converted by reference casting * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments * @throws Throwable anything thrown by the target method invocation @@ -689,32 +596,105 @@ *

* If the original type and new type are equal, returns {@code this}. *

+ * The new method handle, when invoked, will perform the following + * steps: + *

+ *

* This method provides the crucial behavioral difference between - * {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}. The two methods - * perform the same steps when the caller's type descriptor is identical - * with the callee's, but when the types differ, plain {@link #invoke invoke} + * {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}. + * The two methods + * perform the same steps when the caller's type descriptor exactly m atches + * the callee's, but when the types differ, plain {@link #invoke invoke} * also calls {@code asType} (or some internal equivalent) in order * to match up the caller's and callee's types. *

- * This method is equivalent to {@link MethodHandles#convertArguments convertArguments}, - * except for variable arity method handles produced by {@link #asVarargsCollector asVarargsCollector}. + * If the current method is a variable arity method handle + * argument list conversion may involve the conversion and collection + * of several arguments into an array, as + * {@linkplain #asVarargsCollector described elsewhere}. + * In every other case, all conversions are applied pairwise, + * which means that each argument or return value is converted to + * exactly one argument or return value (or no return value). + * The applied conversions are defined by consulting the + * the corresponding component types of the old and new + * method handle types. + *

+ * Let T0 and T1 be corresponding new and old parameter types, + * or old and new return types. Specifically, for some valid index {@code i}, let + * T0{@code =newType.parameterType(i)} and T1{@code =this.type().parameterType(i)}. + * Or else, going the other way for return values, let + * T0{@code =this.type().returnType()} and T1{@code =newType.returnType()}. + * If the types are the same, the new method handle makes no change + * to the corresponding argument or return value (if any). + * Otherwise, one of the following conversions is applied + * if possible: + *

+ * (Note: Both T0 and T1 may be regarded as static types, + * because neither corresponds specifically to the dynamic type of any + * actual argument or return value.) + *

+ * The method handle conversion cannot be made if any one of the required + * pairwise conversions cannot be made. + *

+ * At runtime, the conversions applied to reference arguments + * or return values may require additional runtime checks which can fail. + * An unboxing operation may fail because the original reference is null, + * causing a {@link java.lang.NullPointerException NullPointerException}. + * An unboxing operation or a reference cast may also fail on a reference + * to an object of the wrong type, + * causing a {@link java.lang.ClassCastException ClassCastException}. + * Although an unboxing operation may accept several kinds of wrappers, + * if none are available, a {@code ClassCastException} will be thrown. * * @param newType the expected type of the new method handle * @return a method handle which delegates to {@code this} after performing * any necessary argument conversions, and arranges for any * necessary return value conversions + * @throws NullPointerException if {@code newType} is a null reference * @throws WrongMethodTypeException if the conversion cannot be made - * @see MethodHandles#convertArguments + * @see MethodHandles#explicitCastArguments */ public MethodHandle asType(MethodType newType) { if (!type.isConvertibleTo(newType)) { - throw new WrongMethodTypeException("cannot convert "+type+" to "+newType); + throw new WrongMethodTypeException("cannot convert "+this+" to "+newType); } - return MethodHandles.convertArguments(this, newType); + return MethodHandleImpl.convertArguments(this, newType, 1); } /** - * Makes an adapter which accepts a trailing array argument + * Makes an array-spreading method handle, which accepts a trailing array argument * and spreads its elements as positional arguments. * The new method handle adapts, as its target, * the current method handle. The type of the adapter will be @@ -739,26 +719,103 @@ * contains exactly enough elements to provide a correct argument count * to the target method handle. * (The array may also be null when zero elements are required.) + *

+ * Here are some simple examples of array-spreading method handles: + *

+MethodHandle equals = publicLookup()
+  .findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
+assert( (boolean) equals.invokeExact("me", (Object)"me"));
+assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
+// spread both arguments from a 2-array:
+MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
+assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
+assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
+// spread both arguments from a String array:
+MethodHandle eq2s = equals.asSpreader(String[].class, 2);
+assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
+assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
+// spread second arguments from a 1-array:
+MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
+assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
+assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
+// spread no arguments from a 0-array or null:
+MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
+assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
+assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
+// asSpreader and asCollector are approximate inverses:
+for (int n = 0; n <= 2; n++) {
+    for (Class a : new Class[]{Object[].class, String[].class, CharSequence[].class}) {
+        MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
+        assert( (boolean) equals2.invokeWithArguments("me", "me"));
+        assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
+    }
+}
+MethodHandle caToString = publicLookup()
+  .findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
+assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
+MethodHandle caString3 = caToString.asCollector(char[].class, 3);
+assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
+MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
+assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
+     * 
* @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments * @param arrayLength the number of arguments to spread from an incoming array argument * @return a new method handle which spreads its final array argument, * before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference * @throws IllegalArgumentException if {@code arrayType} is not an array type * @throws IllegalArgumentException if target does not have at least - * {@code arrayLength} parameter types + * {@code arrayLength} parameter types, + * or if {@code arrayLength} is negative * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asCollector */ public MethodHandle asSpreader(Class arrayType, int arrayLength) { - Class arrayElement = arrayType.getComponentType(); - if (arrayElement == null) throw newIllegalArgumentException("not an array type"); - int nargs = type().parameterCount(); - if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length"); + asSpreaderChecks(arrayType, arrayLength); return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength); } + private void asSpreaderChecks(Class arrayType, int arrayLength) { + spreadArrayChecks(arrayType, arrayLength); + int nargs = type().parameterCount(); + if (nargs < arrayLength || arrayLength < 0) + throw newIllegalArgumentException("bad spread array length"); + if (arrayType != Object[].class && arrayLength != 0) { + boolean sawProblem = false; + Class arrayElement = arrayType.getComponentType(); + for (int i = nargs - arrayLength; i < nargs; i++) { + if (!MethodType.canConvert(arrayElement, type().parameterType(i))) { + sawProblem = true; + break; + } + } + if (sawProblem) { + ArrayList> ptypes = new ArrayList>(type().parameterList()); + for (int i = nargs - arrayLength; i < nargs; i++) { + ptypes.set(i, arrayElement); + } + // elicit an error: + this.asType(MethodType.methodType(type().returnType(), ptypes)); + } + } + } + + private void spreadArrayChecks(Class arrayType, int arrayLength) { + Class arrayElement = arrayType.getComponentType(); + if (arrayElement == null) + throw newIllegalArgumentException("not an array type", arrayType); + if ((arrayLength & 0x7F) != arrayLength) { + if ((arrayLength & 0xFF) != arrayLength) + throw newIllegalArgumentException("array length is not legal", arrayLength); + assert(arrayLength >= 128); + if (arrayElement == long.class || + arrayElement == double.class) + throw newIllegalArgumentException("array length is not legal for long[] or double[]", arrayLength); + } + } + /** - * Makes an adapter which accepts a given number of trailing + * Makes an array-collecting method handle, which accepts a given number of trailing * positional arguments and collects them into an array argument. * The new method handle adapts, as its target, * the current method handle. The type of the adapter will be @@ -785,10 +842,40 @@ *

* In order to create a collecting adapter which is not restricted to a particular * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead. + *

+ * Here are some examples of array-collecting method handles: + *

+MethodHandle deepToString = publicLookup()
+  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+assertEquals("[won]",   (String) deepToString.invokeExact(new Object[]{"won"}));
+MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
+assertEquals(methodType(String.class, Object.class), ts1.type());
+//assertEquals("[won]", (String) ts1.invokeExact(         new Object[]{"won"})); //FAIL
+assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
+// arrayType can be a subtype of Object[]
+MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
+assertEquals(methodType(String.class, String.class, String.class), ts2.type());
+assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
+MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
+assertEquals("[]", (String) ts0.invokeExact());
+// collectors can be nested, Lisp-style
+MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
+assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
+// arrayType can be any primitive array type
+MethodHandle bytesToString = publicLookup()
+  .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
+  .asCollector(byte[].class, 3);
+assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
+MethodHandle longsToString = publicLookup()
+  .findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
+  .asCollector(long[].class, 1);
+assertEquals("[123]", (String) longsToString.invokeExact((long)123));
+     * 
* @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments * @param arrayLength the number of arguments to collect into a new array argument * @return a new method handle which collects some trailing argument * into an array, before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference * @throws IllegalArgumentException if {@code arrayType} is not an array type * or {@code arrayType} is not assignable to this method handle's trailing parameter type, * or {@code arrayLength} is not a legal array size @@ -802,13 +889,16 @@ return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector); } - private void asCollectorChecks(Class arrayType, int arrayLength) { - Class arrayElement = arrayType.getComponentType(); - if (arrayElement == null) - throw newIllegalArgumentException("not an array type", arrayType); + // private API: return true if last param exactly matches arrayType + private boolean asCollectorChecks(Class arrayType, int arrayLength) { + spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); - if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType)) - throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType); + if (nargs != 0) { + Class lastParam = type().parameterType(nargs-1); + if (lastParam == arrayType) return true; + if (lastParam.isAssignableFrom(arrayType)) return false; + } + throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType); } /** @@ -825,6 +915,10 @@ * {@code arrayType}, even if the target has a different * last parameter type. *

+ * This transformation may return {@code this} if the method handle is + * already of variable arity and its trailing parameter type + * is identical to {@code arrayType}. + *

* When called with {@link #invokeExact invokeExact}, the adapter invokes * the target with no argument changes. * (Note: This behavior is different from a @@ -841,8 +935,8 @@ * trailing parameter type of the caller is a reference type identical to * or assignable to the trailing parameter type of the adapter, * the arguments and return values are converted pairwise, - * as if by {@link MethodHandles#convertArguments convertArguments}. - * (This is also normal behavior for {@code invoke} in such a case.) + * as if by {@link #asType asType} on a fixed arity + * method handle. *

* Otherwise, the arities differ, or the adapter's trailing parameter * type is not assignable from the corresponding caller type. @@ -910,14 +1004,24 @@ *

* Here is an example, of a list-making variable arity method handle: *

+MethodHandle deepToString = publicLookup()
+  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
+assertEquals("[won]",   (String) ts1.invokeExact(    new Object[]{"won"}));
+assertEquals("[won]",   (String) ts1.invoke(         new Object[]{"won"}));
+assertEquals("[won]",   (String) ts1.invoke(                      "won" ));
+assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
+// findStatic of Arrays.asList(...) produces a variable arity method handle:
 MethodHandle asList = publicLookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
-  .asVarargsCollector(Object[].class);
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
+assertEquals(methodType(List.class, Object[].class), asList.type());
+assert(asList.isVarargsCollector());
 assertEquals("[]", asList.invoke().toString());
 assertEquals("[1]", asList.invoke(1).toString());
 assertEquals("[two, too]", asList.invoke("two", "too").toString());
-Object[] argv = { "three", "thee", "tee" };
+String[] argv = { "three", "thee", "tee" };
 assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
+assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
 List ls = (List) asList.invoke((Object)argv);
 assertEquals(1, ls.size());
 assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
@@ -934,39 +1038,25 @@
      * or not a single trailing argument is interpreted as a whole
      * array or a single element of an array to be collected.
      * Note that the dynamic type of the trailing argument has no
-     * effect on this decision, only a comparison between the static
-     * type descriptor of the call site and the type of the method handle.)
-     * 

- * As a result of the previously stated rules, the variable arity behavior - * of a method handle may be suppressed, by binding it to the exact invoker - * of its own type, as follows: - *

-MethodHandle vamh = publicLookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
-  .asVarargsCollector(Object[].class);
-MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
-assert(vamh.type().equals(mh.type()));
-assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
-boolean failed = false;
-try { mh.invoke(1,2,3); }
-catch (WrongMethodTypeException ex) { failed = true; }
-assert(failed);
-     * 
- * This transformation has no behavioral effect if the method handle is - * not of variable arity. + * effect on this decision, only a comparison between the symbolic + * type descriptor of the call site and the type descriptor of the method handle.) * * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments * @return a new method handle which can collect any number of trailing arguments * into an array, before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference * @throws IllegalArgumentException if {@code arrayType} is not an array type * or {@code arrayType} is not assignable to this method handle's trailing parameter type * @see #asCollector * @see #isVarargsCollector + * @see #asFixedArity */ public MethodHandle asVarargsCollector(Class arrayType) { Class arrayElement = arrayType.getComponentType(); - if (arrayElement == null) throw newIllegalArgumentException("not an array type"); - return MethodHandles.asVarargsCollector(this, arrayType); + boolean lastMatch = asCollectorChecks(arrayType, 0); + if (isVarargsCollector() && lastMatch) + return this; + return AdapterMethodHandle.makeVarargsCollector(this, arrayType); } /** @@ -982,12 +1072,61 @@ * * @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls * @see #asVarargsCollector + * @see #asFixedArity */ public boolean isVarargsCollector() { return false; } /** + * Makes a fixed arity method handle which is otherwise + * equivalent to the the current method handle. + *

+ * If the current method handle is not of + * {@linkplain #asVarargsCollector variable arity}, + * the current method handle is returned. + * This is true even if the current method handle + * could not be a valid input to {@code asVarargsCollector}. + *

+ * Otherwise, the resulting fixed-arity method handle has the same + * type and behavior of the current method handle, + * except that {@link #isVarargsCollector isVarargsCollector} + * will be false. + * The fixed-arity method handle may (or may not) be the + * a previous argument to {@code asVarargsCollector}. + *

+ * Here is an example, of a list-making variable arity method handle: + *

+MethodHandle asListVar = publicLookup()
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+  .asVarargsCollector(Object[].class);
+MethodHandle asListFix = asListVar.asFixedArity();
+assertEquals("[1]", asListVar.invoke(1).toString());
+Exception caught = null;
+try { asListFix.invoke((Object)1); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof ClassCastException);
+assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
+try { asListFix.invoke("two", "too"); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof WrongMethodTypeException);
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
+assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
+assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
+assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
+     * 
+ * + * @return a new method handle which accepts only a fixed number of arguments + * @see #asVarargsCollector + * @see #isVarargsCollector + */ + public MethodHandle asFixedArity() { + assert(!isVarargsCollector()); + return this; + } + + /** * Binds a value {@code x} to the first argument of a method handle, without invoking it. * The new method handle adapts, as its target, * the current method handle by binding it to the given argument. @@ -1043,6 +1182,12 @@ */ @Override public String toString() { + if (DEBUG_METHOD_HANDLE_NAMES) return debugString(); + return "MethodHandle"+type; + } + + /*non-public*/ + String debugString() { return getNameString(this); } } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java Sat Jun 04 06:52:01 2011 -0700 @@ -82,20 +82,44 @@ } DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass); if (!mh.isValid()) - throw method.makeAccessException("no access", lookupClass); + throw method.makeAccessException("no direct method handle", lookupClass); assert(mh.type() == mtype); if (!method.isVarargs()) return mh; - else - return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1)); + int argc = mtype.parameterCount(); + if (argc != 0) { + Class arrayType = mtype.parameterType(argc-1); + if (arrayType.isArray()) + return AdapterMethodHandle.makeVarargsCollector(mh, arrayType); + } + throw method.makeAccessException("cannot make variable arity", null); } static MethodHandle makeAllocator(MethodHandle rawConstructor) { MethodType rawConType = rawConstructor.type(); + Class allocateClass = rawConType.parameterType(0); // Wrap the raw (unsafe) constructor with the allocation of a suitable object. + if (AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true)) { + // allocator(arg...) + // [fold]=> cookedConstructor(obj=allocate(C), arg...) + // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...)) + MethodHandle returner = MethodHandles.identity(allocateClass); + MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass); + MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false); + assert(cookedConstructor.type().equals(ctype)); + ctype = ctype.dropParameterTypes(0, 1); + cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true); + MethodHandle allocator = new AllocateObject(allocateClass); + // allocate() => new C(void) + assert(allocator.type().equals(MethodType.methodType(allocateClass))); + ctype = ctype.dropParameterTypes(0, 1); + MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator); + return fold; + } + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodHandle allocator - = AllocateObject.make(rawConType.parameterType(0), rawConstructor); + = AllocateObject.make(allocateClass, rawConstructor); assert(allocator.type() .equals(rawConType.dropParameterTypes(0, 1).changeReturnType(rawConType.parameterType(0)))); return allocator; @@ -112,8 +136,16 @@ super(invoker); this.allocateClass = allocateClass; this.rawConstructor = rawConstructor; + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + } + // for allocation only: + private AllocateObject(Class allocateClass) { + super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class))); + this.allocateClass = allocateClass; + this.rawConstructor = null; } static MethodHandle make(Class allocateClass, MethodHandle rawConstructor) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodType rawConType = rawConstructor.type(); assert(rawConType.parameterType(0) == allocateClass); MethodType newType = rawConType.dropParameterTypes(0, 1).changeReturnType(allocateClass); @@ -129,14 +161,14 @@ } else { MethodHandle invoke = VARARGS_INVOKE; MethodType conType = CON_TYPES[nargs]; - MethodHandle gcon = spreadArguments(rawConstructor, conType, 1); + MethodHandle gcon = spreadArgumentsFromPos(rawConstructor, conType, 1); if (gcon == null) return null; MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon); return collectArguments(galloc, newType, 1, null); } } @Override - public String toString() { + String debugString() { return addTypeString(allocateClass.getSimpleName(), this); } @SuppressWarnings("unchecked") @@ -214,9 +246,11 @@ // For testing use this: //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2); static final MethodHandle VARARGS_INVOKE; + static final MethodHandle ALLOCATE; static { try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true)); + ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0)); } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } @@ -278,7 +312,7 @@ this.base = staticBase(field); } @Override - public String toString() { return addTypeString(name, this); } + String debugString() { return addTypeString(name, this); } int getFieldI(C obj) { return unsafe.getInt(obj, offset); } void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); } @@ -309,8 +343,9 @@ try { // FIXME: Should not have to create 'f' to get this value. f = c.getDeclaredField(field.getName()); + // Note: Previous line might invalidly throw SecurityException (7042829) return unsafe.staticFieldBase(f); - } catch (Exception ee) { + } catch (NoSuchFieldException ee) { throw uncaughtException(ee); } } @@ -455,14 +490,14 @@ */ static MethodHandle bindReceiver(MethodHandle target, Object receiver) { + if (receiver == null) return null; if (target instanceof AdapterMethodHandle && ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY ) { Object info = MethodHandleNatives.getTargetInfo(target); if (info instanceof DirectMethodHandle) { DirectMethodHandle dmh = (DirectMethodHandle) info; - if (receiver == null || - dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) { + if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) { MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0); MethodType newType = target.type().dropParameterTypes(0, 1); return convertArguments(bmh, newType, bmh.type(), 0); @@ -668,7 +703,9 @@ if (target == null) throw newIllegalArgumentException("cannot drop"); oldType = target.type(); } - return convertArguments(target, newType, oldType, 0); + target = convertArguments(target, newType, oldType, 0); + assert(target != null); + return target; } /*non-public*/ static @@ -747,7 +784,7 @@ .insertParameterTypes(keepPosArgs, arrayType); return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); } - static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) { + static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) { int arrayLength = target.type().parameterCount() - spreadArgPos; return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); } @@ -761,9 +798,7 @@ // spread the last argument of newType to oldType assert(arrayLength == oldType.parameterCount() - spreadArgPos); assert(newType.parameterType(spreadArgPos) == arrayType); - MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); - if (res == null) throw new IllegalArgumentException("spread on "+target+" with "+arrayType.getSimpleName()); - return res; + return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); } static MethodHandle collectArguments(MethodHandle target, @@ -771,6 +806,7 @@ MethodHandle collector) { MethodType type = target.type(); Class collectType = collector.type().returnType(); + assert(collectType != void.class); // else use foldArguments if (collectType != type.parameterType(collectArg)) target = target.asType(type.changeParameterType(collectArg, collectType)); MethodType newType = type @@ -879,11 +915,15 @@ this.target = target; this.fallback = fallback; } - // FIXME: Build the control flow out of foldArguments. + static boolean preferRicochetFrame(MethodType type) { + return (type.parameterCount() >= INVOKES.length || type.hasPrimitives()); + } static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) { MethodType type = target.type(); int nargs = type.parameterCount(); if (nargs < INVOKES.length) { + if (preferRicochetFrame(type)) + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodHandle invoke = INVOKES[nargs]; MethodType gtype = type.generic(); assert(invoke.type().dropParameterTypes(0,1) == gtype); @@ -894,19 +934,20 @@ MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback); return convertArguments(gguard, type, gtype, 0); } else { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodHandle invoke = VARARGS_INVOKE; MethodType gtype = MethodType.genericMethodType(1); assert(invoke.type().dropParameterTypes(0,1) == gtype); - MethodHandle gtest = spreadArguments(test, gtype.changeReturnType(boolean.class), 0); - MethodHandle gtarget = spreadArguments(target, gtype, 0); - MethodHandle gfallback = spreadArguments(fallback, gtype, 0); + MethodHandle gtest = spreadArgumentsFromPos(test, gtype.changeReturnType(boolean.class), 0); + MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0); + MethodHandle gfallback = spreadArgumentsFromPos(fallback, gtype, 0); MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback); if (gtest == null || gtarget == null || gfallback == null) return null; return collectArguments(gguard, type, 0, null); } } @Override - public String toString() { + String debugString() { return addTypeString(target, this); } private Object invoke_V(Object... av) throws Throwable { @@ -990,9 +1031,49 @@ } static + MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { + return testResult ? target : fallback; + } + + static MethodHandle SELECT_ALTERNATIVE; + static MethodHandle selectAlternative() { + if (SELECT_ALTERNATIVE != null) return SELECT_ALTERNATIVE; + try { + SELECT_ALTERNATIVE + = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", + MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + return SELECT_ALTERNATIVE; + } + + static MethodHandle makeGuardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) { + // gwt(arg...) + // [fold]=> continueAfterTest(z=test(arg...), arg...) + // [filter]=> (tf=select(z))(arg...) + // where select(z) = select(z, t, f).bindTo(t, f) => z ? t f + // [tailcall]=> tf(arg...) + assert(test.type().returnType() == boolean.class); + MethodType targetType = target.type(); + MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class); + if (AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true) + && GuardWithTest.preferRicochetFrame(targetType)) { + // working backwards, as usual: + assert(target.type().equals(fallback.type())); + MethodHandle tailcall = MethodHandles.exactInvoker(target.type()); + MethodHandle select = selectAlternative(); + select = bindArgument(select, 2, fallback); + select = bindArgument(select, 1, target); + // select(z: boolean) => (z ? target : fallback) + MethodHandle filter = filterArgument(tailcall, 0, select); + assert(filter.type().parameterType(0) == boolean.class); + MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test); + return fold; + } return GuardWithTest.make(test, target, fallback); } @@ -1012,7 +1093,7 @@ this.catcher = catcher; } @Override - public String toString() { + String debugString() { return addTypeString(target, this); } private Object invoke_V(Object... av) throws Throwable { @@ -1144,11 +1225,12 @@ } else { MethodType gtype = MethodType.genericMethodType(0, true); MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); - MethodHandle gtarget = spreadArguments(target, gtype, 0); - MethodHandle gcatcher = spreadArguments(catcher, gcatchType, 1); + MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0); + catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class)); + MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1); MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher); if (gtarget == null || gcatcher == null || gguard == null) return null; - return collectArguments(gguard, type, 0, null); + return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type); } } @@ -1178,8 +1260,4 @@ static MethodHandle getBootstrap(Class callerClass) { return MethodHandleNatives.getBootstrap(callerClass); } - - static MethodHandle asVarargsCollector(MethodHandle target, Class arrayType) { - return AdapterMethodHandle.makeVarargsCollector(target, arrayType); - } } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodHandleNatives.java --- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java Sat Jun 04 06:52:01 2011 -0700 @@ -400,7 +400,7 @@ case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type ); case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type ); } - throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type); + throw new InternalError("bad MethodHandle constant "+name+" : "+type); } catch (ReflectiveOperationException ex) { Error err = new IncompatibleClassChangeError(); err.initCause(ex); diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodHandleProxies.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/lang/invoke/MethodHandleProxies.java Sat Jun 04 06:52:01 2011 -0700 @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2008, 2011, 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 java.lang.invoke; + +import java.lang.reflect.*; +import sun.invoke.WrapperInstance; + +/** + * This class consists exclusively of static methods that help adapt + * method handles to other JVM types, such as interfaces. + */ +public class MethodHandleProxies { + + private MethodHandleProxies() { } // do not instantiate + + /** + * Produces an instance of the given single-method interface which redirects + * its calls to the given method handle. + *

+ * A single-method interface is an interface which declares a uniquely named method. + * When determining the uniquely named method of a single-method interface, + * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) + * are disregarded. For example, {@link java.util.Comparator} is a single-method interface, + * even though it re-declares the {@code Object.equals} method. + *

+ * The interface must be public. No additional access checks are performed. + *

+ * The resulting instance of the required type will respond to + * invocation of the type's uniquely named method by calling + * the given target on the incoming arguments, + * and returning or throwing whatever the target + * returns or throws. The invocation will be as if by + * {@code target.invoke}. + * The target's type will be checked before the + * instance is created, as if by a call to {@code asType}, + * which may result in a {@code WrongMethodTypeException}. + *

+ * The uniquely named method is allowed to be multiply declared, + * with distinct type descriptors. (E.g., it can be overloaded, + * or can possess bridge methods.) All such declarations are + * connected directly to the target method handle. + * Argument and return types are adjusted by {@code asType} + * for each individual declaration. + *

+ * The wrapper instance will implement the requested interface + * and its super-types, but no other single-method interfaces. + * This means that the instance will not unexpectedly + * pass an {@code instanceof} test for any unrequested type. + *

+ * Implementation Note: + * Therefore, each instance must implement a unique single-method interface. + * Implementations may not bundle together + * multiple single-method interfaces onto single implementation classes + * in the style of {@link java.awt.AWTEventMulticaster}. + *

+ * The method handle may throw an undeclared exception, + * which means any checked exception (or other checked throwable) + * not declared by the requested type's single abstract method. + * If this happens, the throwable will be wrapped in an instance of + * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} + * and thrown in that wrapped form. + *

+ * Like {@link java.lang.Integer#valueOf Integer.valueOf}, + * {@code asInterfaceInstance} is a factory method whose results are defined + * by their behavior. + * It is not guaranteed to return a new instance for every call. + *

+ * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods} + * and other corner cases, the interface may also have several abstract methods + * with the same name but having distinct descriptors (types of returns and parameters). + * In this case, all the methods are bound in common to the one given target. + * The type check and effective {@code asType} conversion is applied to each + * method type descriptor, and all abstract methods are bound to the target in common. + * Beyond this type check, no further checks are made to determine that the + * abstract methods are related in any way. + *

+ * Future versions of this API may accept additional types, + * such as abstract classes with single abstract methods. + * Future versions of this API may also equip wrapper instances + * with one or more additional public "marker" interfaces. + * + * @param target the method handle to invoke from the wrapper + * @param intfc the desired type of the wrapper, a single-method interface + * @return a correctly-typed wrapper for the given target + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if the {@code intfc} is not a + * valid argument to this method + * @throws WrongMethodTypeException if the target cannot + * be converted to the type required by the requested interface + */ + // Other notes to implementors: + //

+ // No stable mapping is promised between the single-method interface and + // the implementation class C. Over time, several implementation + // classes might be used for the same type. + //

+ // If the implementation is able + // to prove that a wrapper of the required type + // has already been created for a given + // method handle, or for another method handle with the + // same behavior, the implementation may return that wrapper in place of + // a new wrapper. + //

+ // This method is designed to apply to common use cases + // where a single method handle must interoperate with + // an interface that implements a function-like + // API. Additional variations, such as single-abstract-method classes with + // private constructors, or interfaces with multiple but related + // entry points, must be covered by hand-written or automatically + // generated adapter classes. + // + public static + T asInterfaceInstance(final Class intfc, final MethodHandle target) { + // POC implementation only; violates the above contract several ways + final Method sm = getSingleMethod(intfc); + if (sm == null) + throw new IllegalArgumentException("not a single-method interface: "+intfc.getName()); + MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes()); + MethodHandle checkTarget = target.asType(smMT); // make throw WMT + checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class)); + final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, smMT.parameterCount()); + return intfc.cast(Proxy.newProxyInstance( + intfc.getClassLoader(), + new Class[]{ intfc, WrapperInstance.class }, + new InvocationHandler() { + private Object getArg(String name) { + if ((Object)name == "getWrapperInstanceTarget") return target; + if ((Object)name == "getWrapperInstanceType") return intfc; + throw new AssertionError(); + } + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getDeclaringClass() == WrapperInstance.class) + return getArg(method.getName()); + if (method.equals(sm)) + return vaTarget.invokeExact(args); + if (isObjectMethod(method)) + return callObjectMethod(this, method, args); + throw new InternalError(); + } + })); + } + + /** + * Determines if the given object was produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. + * @param x any reference + * @return true if the reference is not null and points to an object produced by {@code asInterfaceInstance} + */ + public static + boolean isWrapperInstance(Object x) { + return x instanceof WrapperInstance; + } + + private static WrapperInstance asWrapperInstance(Object x) { + try { + if (x != null) + return (WrapperInstance) x; + } catch (ClassCastException ex) { + } + throw new IllegalArgumentException("not a wrapper instance"); + } + + /** + * Produces or recovers a target method handle which is behaviorally + * equivalent to the unique method of this wrapper instance. + * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. + * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. + * @param x any reference + * @return a method handle implementing the unique method + * @throws IllegalArgumentException if the reference x is not to a wrapper instance + */ + public static + MethodHandle wrapperInstanceTarget(Object x) { + return asWrapperInstance(x).getWrapperInstanceTarget(); + } + + /** + * Recovers the unique single-method interface type for which this wrapper instance was created. + * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. + * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. + * @param x any reference + * @return the single-method interface type for which the wrapper was created + * @throws IllegalArgumentException if the reference x is not to a wrapper instance + */ + public static + Class wrapperInstanceType(Object x) { + return asWrapperInstance(x).getWrapperInstanceType(); + } + + private static + boolean isObjectMethod(Method m) { + switch (m.getName()) { + case "toString": + return (m.getReturnType() == String.class + && m.getParameterTypes().length == 0); + case "hashCode": + return (m.getReturnType() == int.class + && m.getParameterTypes().length == 0); + case "equals": + return (m.getReturnType() == boolean.class + && m.getParameterTypes().length == 1 + && m.getParameterTypes()[0] == Object.class); + } + return false; + } + + private static + Object callObjectMethod(Object self, Method m, Object[] args) { + assert(isObjectMethod(m)) : m; + switch (m.getName()) { + case "toString": + return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode()); + case "hashCode": + return System.identityHashCode(self); + case "equals": + return (self == args[0]); + } + return null; + } + + private static + Method getSingleMethod(Class intfc) { + if (!intfc.isInterface()) return null; + Method sm = null; + for (Method m : intfc.getMethods()) { + int mod = m.getModifiers(); + if (Modifier.isAbstract(mod)) { + if (sm != null && !isObjectMethod(sm)) + return null; // too many abstract methods + sm = m; + } + } + return sm; + } +} diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodHandleStatics.java --- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java Sat Jun 04 06:52:01 2011 -0700 @@ -35,6 +35,8 @@ private MethodHandleStatics() { } // do not instantiate + static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES"); + /*non-public*/ static String getNameString(MethodHandle target, MethodType type) { if (type == null) type = target.type(); diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodHandles.java --- a/src/share/classes/java/lang/invoke/MethodHandles.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandles.java Sat Jun 04 06:52:01 2011 -0700 @@ -180,6 +180,10 @@ * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand * for reflective objects corresponding to the given members. *

+ * In cases where the given member is of variable arity (i.e., a method or constructor) + * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}. + * In all other cases, the returned method handle will be of fixed arity. + *

* The equivalence between looked-up method handles and underlying * class members can break down in a few ways: *

    @@ -201,7 +205,7 @@ * Access checks are applied in the factory methods of {@code Lookup}, * when a method handle is created. * This is a key difference from the Core Reflection API, since - * {@link java.lang.reflect.Method#invoke Method.invoke} + * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke} * performs access checking against every caller, on every call. *

    * All access checks start from a {@code Lookup} object, which @@ -267,7 +271,7 @@ * Access checks only apply to named and reflected methods, * constructors, and fields. * Other method handle creation methods, such as - * {@link #convertArguments MethodHandles.convertArguments}, + * {@link MethodHandle#asType MethodHandle.asType}, * do not require any access checks, and are done * with static methods of {@link MethodHandles}, * independently of any {@code Lookup} object. @@ -296,6 +300,12 @@ * {@link SecurityManager#checkMemberAccess * smgr.checkMemberAccess(defc, Member.DECLARED)} is called. * (Note that {@code defc} might be the same as {@code refc}.) + * The default implementation of this security manager method + * inspects the stack to determine the original caller of + * the reflective request (such as {@code findStatic}), + * and performs additional permission checks if the + * class loader of {@code defc} differs from the class + * loader of the class from which the reflective request came. *

  • If the retrieved member is not public, * and if {@code defc} and {@code refc} are in different class loaders, * and if the class loader of the lookup class is not @@ -304,8 +314,6 @@ * smgr.checkPackageAccess(defcPkg)} is called, * where {@code defcPkg} is the package of {@code defc}. *
- * In all cases, the requesting class presented to the security - * manager will be the lookup class from the current {@code Lookup} object. */ public static final class Lookup { @@ -559,7 +567,10 @@ * @param type the type of the method * @return the desired method handle * @throws NoSuchMethodException if the method does not exist - * @throws IllegalAccessException if access checking fails, or if the method is not {@code static} + * @throws IllegalAccessException if access checking fails, + * or if the method is not {@code static}, + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @exception SecurityException if a security manager is present and it * refuses access * @throws NullPointerException if any argument is null @@ -567,6 +578,7 @@ public MethodHandle findStatic(Class refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MemberName method = resolveOrFail(refc, name, type, true); + checkSecurityManager(refc, method); // stack walk magic: do not refactor checkMethod(refc, method, true); return MethodHandleImpl.findMethod(method, false, lookupClassOrNull()); } @@ -601,13 +613,17 @@ * @param type the type of the method, with the receiver argument omitted * @return the desired method handle * @throws NoSuchMethodException if the method does not exist - * @throws IllegalAccessException if access checking fails, or if the method is {@code static} + * @throws IllegalAccessException if access checking fails, + * or if the method is {@code static} + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @exception SecurityException if a security manager is present and it * refuses access * @throws NullPointerException if any argument is null */ public MethodHandle findVirtual(Class refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MemberName method = resolveOrFail(refc, name, type, false); + checkSecurityManager(refc, method); // stack walk magic: do not refactor checkMethod(refc, method, false); MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull()); return restrictProtectedReceiver(method, mh); @@ -633,6 +649,8 @@ * @return the desired method handle * @throws NoSuchMethodException if the constructor does not exist * @throws IllegalAccessException if access checking fails + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @exception SecurityException if a security manager is present and it * refuses access * @throws NullPointerException if any argument is null @@ -641,6 +659,7 @@ String name = ""; MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull()); assert(ctor.isConstructor()); + checkSecurityManager(refc, ctor); // stack walk magic: do not refactor checkAccess(refc, ctor); MethodHandle rawMH = MethodHandleImpl.findMethod(ctor, false, lookupClassOrNull()); MethodHandle allocMH = MethodHandleImpl.makeAllocator(rawMH); @@ -658,7 +677,7 @@ int arity = type.parameterCount(); return mh.asVarargsCollector(type.parameterType(arity-1)); } else { - throw new InternalError("already varargs, but template is not: "+mh); + return mh.asFixedArity(); } } @@ -690,6 +709,8 @@ * @return the desired method handle * @throws NoSuchMethodException if the method does not exist * @throws IllegalAccessException if access checking fails + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @exception SecurityException if a security manager is present and it * refuses access * @throws NullPointerException if any argument is null @@ -698,6 +719,7 @@ Class specialCaller) throws NoSuchMethodException, IllegalAccessException { checkSpecialCaller(specialCaller); MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller); + checkSecurityManager(refc, method); // stack walk magic: do not refactor checkMethod(refc, method, false); MethodHandle mh = MethodHandleImpl.findMethod(method, false, specialCaller); return restrictReceiver(method, mh, specialCaller); @@ -721,7 +743,9 @@ * @throws NullPointerException if any argument is null */ public MethodHandle findGetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { - return makeAccessor(refc, name, type, false, false); + MemberName field = resolveOrFail(refc, name, type, false); + checkSecurityManager(refc, field); // stack walk magic: do not refactor + return makeAccessor(refc, field, false, false, 0); } /** @@ -742,7 +766,9 @@ * @throws NullPointerException if any argument is null */ public MethodHandle findSetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { - return makeAccessor(refc, name, type, false, true); + MemberName field = resolveOrFail(refc, name, type, false); + checkSecurityManager(refc, field); // stack walk magic: do not refactor + return makeAccessor(refc, field, false, true, 0); } /** @@ -762,7 +788,9 @@ * @throws NullPointerException if any argument is null */ public MethodHandle findStaticGetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { - return makeAccessor(refc, name, type, true, false); + MemberName field = resolveOrFail(refc, name, type, true); + checkSecurityManager(refc, field); // stack walk magic: do not refactor + return makeAccessor(refc, field, false, false, 1); } /** @@ -782,7 +810,9 @@ * @throws NullPointerException if any argument is null */ public MethodHandle findStaticSetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { - return makeAccessor(refc, name, type, true, true); + MemberName field = resolveOrFail(refc, name, type, true); + checkSecurityManager(refc, field); // stack walk magic: do not refactor + return makeAccessor(refc, field, false, true, 1); } /** @@ -805,10 +835,13 @@ *

* This is equivalent to the following code: *

-MethodHandle mh0 = {@link #findVirtual findVirtual}(defc, name, type);
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle mh0 = lookup().{@link #findVirtual findVirtual}(defc, name, type);
 MethodHandle mh1 = mh0.{@link MethodHandle#bindTo bindTo}(receiver);
 MethodType mt1 = mh1.type();
-if (mh0.isVarargsCollector() && mt1.parameterCount() > 0) {
+if (mh0.isVarargsCollector())
   mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
 return mh1;
          * 
@@ -822,6 +855,8 @@ * @return the desired method handle * @throws NoSuchMethodException if the method does not exist * @throws IllegalAccessException if access checking fails + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @exception SecurityException if a security manager is present and it * refuses access * @throws NullPointerException if any argument is null @@ -829,13 +864,12 @@ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { Class refc = receiver.getClass(); // may get NPE MemberName method = resolveOrFail(refc, name, type, false); + checkSecurityManager(refc, method); // stack walk magic: do not refactor checkMethod(refc, method, false); MethodHandle dmh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull()); MethodHandle bmh = MethodHandleImpl.bindReceiver(dmh, receiver); if (bmh == null) throw method.makeAccessException("no access", this); - if (dmh.type().parameterCount() == 0) - return dmh; // bound the trailing parameter; no varargs possible return fixVarargs(bmh, dmh); } @@ -856,6 +890,8 @@ * @param m the reflected method * @return a method handle which can invoke the reflected method * @throws IllegalAccessException if access checking fails + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @throws NullPointerException if the argument is null */ public MethodHandle unreflect(Method m) throws IllegalAccessException { @@ -884,6 +920,8 @@ * @param specialCaller the class nominally calling the method * @return a method handle which can invoke the reflected method * @throws IllegalAccessException if access checking fails + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @throws NullPointerException if any argument is null */ public MethodHandle unreflectSpecial(Method m, Class specialCaller) throws IllegalAccessException { @@ -913,6 +951,8 @@ * @param c the reflected constructor * @return a method handle which can invoke the reflected constructor * @throws IllegalAccessException if access checking fails + * or if the method's variable arity modifier bit + * is set and {@code asVarargsCollector} fails * @throws NullPointerException if the argument is null */ public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException { @@ -939,7 +979,7 @@ * @throws NullPointerException if the argument is null */ public MethodHandle unreflectGetter(Field f) throws IllegalAccessException { - return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false); + return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false, -1); } /** @@ -957,7 +997,7 @@ * @throws NullPointerException if the argument is null */ public MethodHandle unreflectSetter(Field f) throws IllegalAccessException { - return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true); + return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true, -1); } /// Helper methods, all package-private. @@ -993,6 +1033,46 @@ throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this); } + /** + * Perform necessary access checks. + * This function performs stack walk magic: do not refactor it. + */ + void checkSecurityManager(Class refc, MemberName m) { + SecurityManager smgr = System.getSecurityManager(); + if (smgr == null) return; + if (allowedModes == TRUSTED) return; + // Step 1: + smgr.checkMemberAccess(refc, Member.PUBLIC); + // Step 2: + if (!VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) + smgr.checkPackageAccess(VerifyAccess.getPackageName(refc)); + // Step 3: + if (m.isPublic()) return; + Class defc = m.getDeclaringClass(); + smgr.checkMemberAccess(defc, Member.DECLARED); // STACK WALK HERE + // Step 4: + if (defc != refc) + smgr.checkPackageAccess(VerifyAccess.getPackageName(defc)); + + // Comment from SM.checkMemberAccess, where which=DECLARED: + /* + * stack depth of 4 should be the caller of one of the + * methods in java.lang.Class that invoke checkMember + * access. The stack should look like: + * + * someCaller [3] + * java.lang.Class.someReflectionAPI [2] + * java.lang.Class.checkMemberAccess [1] + * SecurityManager.checkMemberAccess [0] + * + */ + // For us it is this stack: + // someCaller [3] + // Lookup.findSomeMember [2] + // Lookup.checkSecurityManager [1] + // SecurityManager.checkMemberAccess [0] + } + void checkMethod(Class refc, MemberName m, boolean wantStatic) throws IllegalAccessException { String message; if (m.isConstructor()) @@ -1065,6 +1145,7 @@ if (!method.isProtected() || method.isStatic() || allowedModes == TRUSTED || method.getDeclaringClass() == lookupClass() + || VerifyAccess.isSamePackage(method.getDeclaringClass(), lookupClass()) || (ALLOW_NESTMATE_ACCESS && VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass()))) return mh; @@ -1084,19 +1165,14 @@ return fixVarargs(narrowMH, mh); } - MethodHandle makeAccessor(Class refc, String name, Class type, - boolean isStatic, boolean isSetter) throws NoSuchFieldException, IllegalAccessException { - MemberName field = resolveOrFail(refc, name, type, isStatic); - if (isStatic != field.isStatic()) - throw field.makeAccessException(isStatic + MethodHandle makeAccessor(Class refc, MemberName field, + boolean trusted, boolean isSetter, + int checkStatic) throws IllegalAccessException { + assert(field.isField()); + if (checkStatic >= 0 && (checkStatic != 0) != field.isStatic()) + throw field.makeAccessException((checkStatic != 0) ? "expected a static field" : "expected a non-static field", this); - return makeAccessor(refc, field, false, isSetter); - } - - MethodHandle makeAccessor(Class refc, MemberName field, - boolean trusted, boolean isSetter) throws IllegalAccessException { - assert(field.isField()); if (trusted) return MethodHandleImpl.accessField(field, isSetter, lookupClassOrNull()); checkAccess(refc, field); @@ -1138,50 +1214,51 @@ /** * Produces a method handle which will invoke any method handle of the - * given {@code type} on a standard set of {@code Object} type arguments - * and a single trailing {@code Object[]} array. + * given {@code type}, with a given number of trailing arguments replaced by + * a single trailing {@code Object[]} array. * The resulting invoker will be a method handle with the following * arguments: *
    *
  • a single {@code MethodHandle} target - *
  • zero or more {@code Object} values (counted by {@code objectArgCount}) - *
  • an {@code Object[]} array containing more arguments + *
  • zero or more leading values (counted by {@code leadingArgCount}) + *
  • an {@code Object[]} array containing trailing arguments *
*

- * The invoker will behave like a call to {@link MethodHandle#invoke invoke} with + * The invoker will invoke its target like a call to {@link MethodHandle#invoke invoke} with * the indicated {@code type}. * That is, if the target is exactly of the given {@code type}, it will behave * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType} * is used to convert the target to the required {@code type}. *

* The type of the returned invoker will not be the given {@code type}, but rather - * will have all parameter and return types replaced by {@code Object}, except for - * the last parameter type, which will be the array type {@code Object[]}. + * will have all parameters except the first {@code leadingArgCount} + * replaced by a single array of type {@code Object[]}, which will be + * the final parameter. *

- * Before invoking its target, the invoker will spread the varargs array, apply + * Before invoking its target, the invoker will spread the final array, apply * reference casts as necessary, and unbox and widen primitive arguments. - * The return value of the invoker will be an {@code Object} reference, - * boxing a primitive value if the original type returns a primitive, - * and always null if the original type returns void. *

* This method is equivalent to the following code (though it may be more efficient): *

 MethodHandle invoker = MethodHandles.invoker(type);
-int spreadArgCount = type.parameterCount - objectArgCount;
+int spreadArgCount = type.parameterCount() - leadingArgCount;
 invoker = invoker.asSpreader(Object[].class, spreadArgCount);
 return invoker;
      * 
*

* This method throws no reflective or security exceptions. * @param type the desired target type - * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments + * @param leadingArgCount number of fixed arguments, to be passed unchanged to the target * @return a method handle suitable for invoking any method handle of the given type + * @throws NullPointerException if {@code type} is null + * @throws IllegalArgumentException if {@code leadingArgCount} is not in + * the range from 0 to {@code type.parameterCount()} inclusive */ static public - MethodHandle spreadInvoker(MethodType type, int objectArgCount) { - if (objectArgCount < 0 || objectArgCount > type.parameterCount()) - throw new IllegalArgumentException("bad argument count "+objectArgCount); - return type.invokers().spreadInvoker(objectArgCount); + MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { + if (leadingArgCount < 0 || leadingArgCount > type.parameterCount()) + throw new IllegalArgumentException("bad argument count "+leadingArgCount); + return type.invokers().spreadInvoker(leadingArgCount); } /** @@ -1211,7 +1288,7 @@ * method handle values, as long as they are compatible with the type of {@code X}. *

* (Note: The invoker method is not available via the Core Reflection API. - * An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke} + * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke} * on the declared {@code invokeExact} or {@code invoke} method will raise an * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.) *

@@ -1231,12 +1308,18 @@ * exactly equal to the desired type, except that it will accept * an additional leading argument of type {@code MethodHandle}. *

- * Before invoking its target, the invoker will apply reference casts as + * Before invoking its target, if the target differs from the expected type, + * the invoker will apply reference casts as * necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}. * Similarly, the return value will be converted as necessary. * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle}, * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}. *

+ * A {@linkplain MethodType#genericMethodType general method type}, + * mentions only {@code Object} arguments and return values. + * An invoker for such a type is capable of calling any method handle + * of the same arity as the general type. + *

* This method is equivalent to the following code (though it may be more efficient): *

 publicLookup().findVirtual(MethodHandle.class, "invoke", type)
@@ -1252,18 +1335,9 @@
     }
 
     /**
-     * Temporary alias for {@link #invoker}, for backward compatibility with some versions of JSR 292.
-     * @deprecated Will be removed for JSR 292 Proposed Final Draft.
-     */
-    public static
-    MethodHandle genericInvoker(MethodType type) {
-        return invoker(type);
-    }
-
-    /**
      * Perform value checking, exactly as if for an adapted method handle.
      * It is assumed that the given value is either null, of type T0,
-     * or (if T0 is primitive) of the wrapper type corresponding to T0.
+     * or (if T0 is primitive) of the wrapper class corresponding to T0.
      * The following checks and conversions are made:
      * 
    *
  • If T0 and T1 are references, then a cast to T1 is applied. @@ -1271,11 +1345,11 @@ *
  • If T0 and T1 are primitives, then a widening or narrowing * conversion is applied, if one exists. *
  • If T0 is a primitive and T1 a reference, and - * T0 has a wrapper type TW, a boxing conversion to TW is applied, + * T0 has a wrapper class TW, a boxing conversion to TW is applied, * possibly followed by a reference conversion. * T1 must be TW or a supertype. *
  • If T0 is a reference and T1 a primitive, and - * T1 has a wrapper type TW, an unboxing conversion is applied, + * T1 has a wrapper class TW, an unboxing conversion is applied, * possibly preceded by a reference conversion. * T0 must be TW or a supertype. *
  • If T1 is void, the return value is discarded @@ -1288,6 +1362,7 @@ * @return the value, converted if necessary * @throws java.lang.ClassCastException if a cast fails */ + // FIXME: This is used in just one place. Refactor away. static T1 checkValue(Class t0, Class t1, Object value) throws ClassCastException @@ -1316,6 +1391,8 @@ return w1.convert(value, t1); } + // FIXME: Delete this. It is used only for insertArguments & bindTo. + // Replace by a more standard check. static Object checkValue(Class T1, Object value) throws ClassCastException @@ -1332,134 +1409,53 @@ /** * Produces a method handle which adapts the type of the - * given method handle to a new type by pairwise argument conversion. - * The original type and new type must have the same number of arguments. - * The resulting method handle is guaranteed to report a type - * which is equal to the desired new type. - *

    - * If the original type and new type are equal, returns target. - *

    - * The following conversions are applied as needed both to - * arguments and return types. Let T0 and T1 be the differing - * new and old parameter types (or old and new return types) - * for corresponding values passed by the new and old method types. - * Given those types T0, T1, one of the following conversions is applied - * if possible: - *

      - *
    • If T0 and T1 are references, then a cast to T1 is applied. - * (The types do not need to be related in any particular way.) - *
    • If T0 and T1 are primitives, then a Java method invocation - * conversion (JLS 5.3) is applied, if one exists. - *
    • If T0 is a primitive and T1 a reference, a boxing - * conversion is applied if one exists, possibly followed by - * a reference conversion to a superclass. - * T1 must be a wrapper class or a supertype of one. - *
    • If T0 is a reference and T1 a primitive, an unboxing - * conversion will be applied at runtime, possibly followed - * by a Java method invocation conversion (JLS 5.3) - * on the primitive value. (These are the widening conversions.) - * T0 must be a wrapper class or a supertype of one. - * (In the case where T0 is Object, these are the conversions - * allowed by java.lang.reflect.Method.invoke.) - *
    • If the return type T1 is void, any returned value is discarded - *
    • If the return type T0 is void and T1 a reference, a null value is introduced. - *
    • If the return type T0 is void and T1 a primitive, a zero value is introduced. - *
    - * @param target the method handle to invoke after arguments are retyped - * @param newType the expected type of the new method handle - * @return a method handle which delegates to {@code target} after performing - * any necessary argument conversions, and arranges for any - * necessary return value conversions - * @throws NullPointerException if either argument is null - * @throws WrongMethodTypeException if the conversion cannot be made - * @see MethodHandle#asType - * @see MethodHandles#explicitCastArguments - */ - public static - MethodHandle convertArguments(MethodHandle target, MethodType newType) { - return MethodHandleImpl.convertArguments(target, newType, 1); - } - - /** - * Produces a method handle which adapts the type of the - * given method handle to a new type by pairwise argument conversion. + * given method handle to a new type by pairwise argument and return type conversion. * The original type and new type must have the same number of arguments. * The resulting method handle is guaranteed to report a type * which is equal to the desired new type. *

    * If the original type and new type are equal, returns target. *

    - * The same conversions are allowed as for {@link #convertArguments convertArguments}, + * The same conversions are allowed as for {@link MethodHandle#asType MethodHandle.asType}, * and some additional conversions are also applied if those conversions fail. - * Given types T0, T1, one of the following conversions is applied - * in addition, if the conversions specified for {@code convertArguments} - * would be insufficient: + * Given types T0, T1, one of the following conversions is applied + * if possible, before or instead of any conversions done by {@code asType}: *

      - *
    • If T0 and T1 are references, and T1 is an interface type, - * then the value of type T0 is passed as a T1 without a cast. + *
    • If T0 and T1 are references, and T1 is an interface type, + * then the value of type T0 is passed as a T1 without a cast. * (This treatment of interfaces follows the usage of the bytecode verifier.) - *
    • If T0 and T1 are primitives and one is boolean, - * the boolean is treated as a one-bit unsigned integer. + *
    • If T0 is boolean and T1 is another primitive, + * the boolean is converted to a byte value, 1 for true, 0 for false. * (This treatment follows the usage of the bytecode verifier.) - * A conversion from another primitive type behaves as if - * it first converts to byte, and then masks all but the low bit. - *
    • If a primitive value would be converted by {@code convertArguments} - * using Java method invocation conversion (JLS 5.3), - * Java casting conversion (JLS 5.5) may be used also. - * This allows primitives to be narrowed as well as widened. + *
    • If T1 is boolean and T0 is another primitive, + * T0 is converted to byte via Java casting conversion (JLS 5.5), + * and the low order bit of the result is tested, as if by {@code (x & 1) != 0}. + *
    • If T0 and T1 are primitives other than boolean, + * then a Java casting conversion (JLS 5.5) is applied. + * (Specifically, T0 will convert to T1 by + * widening and/or narrowing.) + *
    • If T0 is a reference and T1 a primitive, an unboxing + * conversion will be applied at runtime, possibly followed + * by a Java casting conversion (JLS 5.5) on the primitive value, + * possibly followed by a conversion from byte to boolean by testing + * the low-order bit. + *
    • If T0 is a reference and T1 a primitive, + * and if the reference is null at runtime, a zero value is introduced. *
    * @param target the method handle to invoke after arguments are retyped * @param newType the expected type of the new method handle - * @return a method handle which delegates to {@code target} after performing + * @return a method handle which delegates to the target after performing * any necessary argument conversions, and arranges for any * necessary return value conversions * @throws NullPointerException if either argument is null * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandle#asType - * @see MethodHandles#convertArguments */ public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { return MethodHandleImpl.convertArguments(target, newType, 2); } - /* - FIXME: Reconcile javadoc with 10/22/2010 EG notes on conversion: - - Both converters arrange for their method handles to convert arguments - and return values. The conversion rules are the same for arguments - and return values, and depend only on source and target types, S and - T. The conversions allowed by castConvertArguments are a strict - superset of those performed by convertArguments. - - In all cases, if S and T are references, a simple checkcast is done. - If neither S nor T is a primitive, no attempt is made to unbox and - box. A failed conversion throws ClassCastException. - - If T is void, the value is dropped. - - For compatibility with reflection, if S is void and T is a reference, - a null value is produced. - - For compatibility with reflection, if S is a reference and T is a - primitive, S is first unboxed and then undergoes primitive conversion. - In the case of 'convertArguments', only assignment conversion is - performed (no narrowing primitive conversion). - - If S is a primitive, S is boxed, and then the above rules are applied. - If S and T are both primitives, the boxing will be undetectable; only - the primitive conversions will be apparent to the user. The key point - is that if S is a primitive type, the implementation may box it and - treat is as Object, without loss of information, or it may use a "fast - path" which does not use boxing. - - Notwithstanding the rules above, for compatibility with the verifier, - if T is an interface, it is treated as if it were Object. [KEEP THIS?] - - Also, for compatibility with the verifier, a boolean may be undergo - widening or narrowing conversion to any other primitive type. [KEEP THIS?] - */ - /** * Produces a method handle which adapts the calling sequence of the * given method handle to a new type, by reordering the arguments. @@ -1478,8 +1474,8 @@ *

    * No argument or return value conversions are applied. * The type of each incoming argument, as determined by {@code newType}, - * must be identical to the type of the corresponding outgoing argument - * or arguments in the target method handle. + * must be identical to the type of the corresponding outgoing parameter + * or parameters in the target method handle. * The return type of {@code newType} must be identical to the return * type of the original target. *

    @@ -1491,25 +1487,33 @@ * incoming arguments which are not mentioned in the reordering array * are may be any type, as determined only by {@code newType}. *

    -MethodType intfn1 = MethodType.methodType(int.class, int.class);
    -MethodType intfn2 = MethodType.methodType(int.class, int.class, int.class);
    +import static java.lang.invoke.MethodHandles.*;
    +import static java.lang.invoke.MethodType.*;
    +...
    +MethodType intfn1 = methodType(int.class, int.class);
    +MethodType intfn2 = methodType(int.class, int.class, int.class);
     MethodHandle sub = ... {int x, int y => x-y} ...;
     assert(sub.type().equals(intfn2));
    -MethodHandle sub1 = MethodHandles.permuteArguments(sub, intfn2, 0, 1);
    -MethodHandle rsub = MethodHandles.permuteArguments(sub, intfn2, 1, 0);
    +MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
    +MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
     assert((int)rsub.invokeExact(1, 100) == 99);
     MethodHandle add = ... {int x, int y => x+y} ...;
     assert(add.type().equals(intfn2));
    -MethodHandle twice = MethodHandles.permuteArguments(add, intfn1, 0, 0);
    +MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
     assert(twice.type().equals(intfn1));
     assert((int)twice.invokeExact(21) == 42);
          * 
    * @param target the method handle to invoke after arguments are reordered * @param newType the expected type of the new method handle - * @param reorder a string which controls the reordering - * @return a method handle which delegates to {@code target} after it + * @param reorder an index array which controls the reordering + * @return a method handle which delegates to the target after it * drops unused arguments and moves and/or duplicates the other arguments * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if the index array length is not equal to + * the arity of the target, or if any index array element + * not a valid index for a parameter of {@code newType}, + * or if two corresponding parameter types in + * {@code target.type()} and {@code newType} are not identical, */ public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { @@ -1544,78 +1548,13 @@ } /** - * Equivalent to the following code: - *

    -     * int spreadPos = newType.parameterCount() - 1;
    -     * Class<?> spreadType = newType.parameterType(spreadPos);
    -     * int spreadCount = target.type().parameterCount() - spreadPos;
    -     * MethodHandle adapter = target.asSpreader(spreadType, spreadCount);
    -     * adapter = adapter.asType(newType);
    -     * return adapter;
    -     * 
    - * @param target the method handle to invoke after argument spreading - * @param newType the expected type of the new method handle - * @return a method handle which spreads its final argument, - * before calling the original method handle - */ - /*non-public*/ static - MethodHandle spreadArguments(MethodHandle target, MethodType newType) { - MethodType oldType = target.type(); - int inargs = newType.parameterCount(); - int outargs = oldType.parameterCount(); - int spreadPos = inargs - 1; - int numSpread = (outargs - spreadPos); - MethodHandle res = null; - if (spreadPos >= 0 && numSpread >= 0) { - res = MethodHandleImpl.spreadArguments(target, newType, spreadPos); - } - if (res == null) { - throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType); - } - return res; - } - - /** - * Equivalent to the following code: - *

    -     * int collectPos = target.type().parameterCount() - 1;
    -     * Class<?> collectType = target.type().parameterType(collectPos);
    -     * if (!collectType.isArray())  collectType = Object[].class;
    -     * int collectCount = newType.parameterCount() - collectPos;
    -     * MethodHandle adapter = target.asCollector(collectType, collectCount);
    -     * adapter = adapter.asType(newType);
    -     * return adapter;
    -     * 
    - * @param target the method handle to invoke after argument collection - * @param newType the expected type of the new method handle - * @return a method handle which collects some trailing argument - * into an array, before calling the original method handle - */ - /*non-public*/ static - MethodHandle collectArguments(MethodHandle target, MethodType newType) { - MethodType oldType = target.type(); - int inargs = newType.parameterCount(); - int outargs = oldType.parameterCount(); - int collectPos = outargs - 1; - int numCollect = (inargs - collectPos); - if (collectPos < 0 || numCollect < 0) - throw newIllegalArgumentException("wrong number of arguments"); - MethodHandle res = MethodHandleImpl.collectArguments(target, newType, collectPos, null); - if (res == null) { - throw newIllegalArgumentException("cannot collect from "+newType+" to " +oldType); - } - return res; - } - - /** * Produces a method handle of the requested return type which returns the given * constant value every time it is invoked. *

    * Before the method handle is returned, the passed-in value is converted to the requested type. * If the requested type is primitive, widening primitive conversions are attempted, * else reference conversions are attempted. - *

    The returned method handle is equivalent to {@code identity(type).bindTo(value)}, - * unless the type is {@code void}, in which case it is {@code identity(type)}. + *

    The returned method handle is equivalent to {@code identity(type).bindTo(value)}. * @param type the return type of the desired method handle * @param value the value to return * @return a method handle of the given return type and no arguments, which always returns the given value @@ -1637,7 +1576,6 @@ /** * Produces a method handle which returns its sole argument when invoked. - *

    The identity function for {@code void} takes no arguments and returns no values. * @param type the type of the sole parameter and return value of the desired method handle * @return a unary method handle which accepts and returns the given type * @throws NullPointerException if the argument is null @@ -1657,11 +1595,15 @@ } /** - * Produces a method handle which calls the original method handle {@code target}, - * after inserting the given argument(s) at the given position. - * The formal parameters to {@code target} which will be supplied by those - * arguments are called bound parameters, because the new method - * will contain bindings for those parameters take from {@code values}. + * Provides a target method handle with one or more bound arguments + * in advance of the method handle's invocation. + * The formal parameters to the target corresponding to the bound + * arguments are called bound parameters. + * Returns a new method handle which saves away the bound arguments. + * When it is invoked, it receives arguments for any non-bound parameters, + * binds the saved arguments to their corresponding parameters, + * and calls the original target. + *

    * The type of the new method handle will drop the types for the bound * parameters from the original target type, since the new method handle * will no longer require those arguments to be supplied by its callers. @@ -1670,15 +1612,16 @@ * If a bound parameter type is a primitive, the argument object * must be a wrapper, and will be unboxed to produce the primitive value. *

    - * The pos may range between zero and N (inclusively), - * where N is the number of argument types in resulting method handle - * (after bound parameter types are dropped). + * The {@code pos} argument selects which parameters are to be bound. + * It may range between zero and N-L (inclusively), + * where N is the arity of the target method handle + * and L is the length of the values array. * @param target the method handle to invoke after the argument is inserted * @param pos where to insert the argument (zero for the first) * @param values the series of arguments to insert * @return a method handle which inserts an additional argument, * before calling the original method handle - * @throws NullPointerException if the {@code target} argument or the {@code values} array is null + * @throws NullPointerException if the target or the {@code values} array is null * @see MethodHandle#bindTo */ public static @@ -1711,15 +1654,17 @@ } /** - * Produces a method handle which calls the original method handle, - * after dropping the given argument(s) at the given position. - * The type of the new method handle will insert the given argument - * type(s), at that position, into the original handle's type. + * Produces a method handle which will discard some dummy arguments + * before calling some other specified target method handle. + * The type of the new method handle will be the same as the target's type, + * except it will also include the dummy argument types, + * at some given position. *

    - * The pos may range between zero and N, - * where N is the number of argument types in target, - * meaning to drop the first or last argument (respectively), - * or an argument somewhere in between. + * The {@code pos} argument may range between zero and N, + * where N is the arity of the target. + * If {@code pos} is zero, the dummy arguments will precede + * the target's real arguments; if {@code pos} is N + * they will come after. *

    * Example: *

    @@ -1744,14 +1689,16 @@
          * @param pos position of first argument to drop (zero for the leftmost)
          * @return a method handle which drops arguments of the given types,
          *         before calling the original method handle
    -     * @throws NullPointerException if the {@code target} argument is null,
    +     * @throws NullPointerException if the target is null,
          *                              or if the {@code valueTypes} list or any of its elements is null
    -     * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class}
    +     * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
    +     *                  or if {@code pos} is negative or greater than the arity of the target,
    +     *                  or if the new method handle's type would have too many parameters
          */
         public static
         MethodHandle dropArguments(MethodHandle target, int pos, List> valueTypes) {
    +        MethodType oldType = target.type();  // get NPE
             if (valueTypes.size() == 0)  return target;
    -        MethodType oldType = target.type();
             int outargs = oldType.parameterCount();
             int inargs  = outargs + valueTypes.size();
             if (pos < 0 || pos >= inargs)
    @@ -1764,15 +1711,17 @@
         }
     
         /**
    -     * Produces a method handle which calls the original method handle,
    -     * after dropping the given argument(s) at the given position.
    -     * The type of the new method handle will insert the given argument
    -     * type(s), at that position, into the original handle's type.
    +     * Produces a method handle which will discard some dummy arguments
    +     * before calling some other specified target method handle.
    +     * The type of the new method handle will be the same as the target's type,
    +     * except it will also include the dummy argument types,
    +     * at some given position.
          * 

    - * The pos may range between zero and N, - * where N is the number of argument types in target, - * meaning to drop the first or last argument (respectively), - * or an argument somewhere in between. + * The {@code pos} argument may range between zero and N, + * where N is the arity of the target. + * If {@code pos} is zero, the dummy arguments will precede + * the target's real arguments; if {@code pos} is N + * they will come after. *

    * Example: *

    @@ -1801,9 +1750,11 @@
          * @param pos position of first argument to drop (zero for the leftmost)
          * @return a method handle which drops arguments of the given types,
          *         before calling the original method handle
    -     * @throws NullPointerException if the {@code target} argument is null,
    +     * @throws NullPointerException if the target is null,
          *                              or if the {@code valueTypes} array or any of its elements is null
    -     * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class}
    +     * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
    +     *                  or if {@code pos} is negative or greater than the arity of the target,
    +     *                  or if the new method handle's type would have too many parameters
          */
         public static
         MethodHandle dropArguments(MethodHandle target, int pos, Class... valueTypes) {
    @@ -1811,19 +1762,23 @@
         }
     
         /**
    -     * Adapts a target method handle {@code target} by pre-processing
    +     * Adapts a target method handle by pre-processing
          * one or more of its arguments, each with its own unary filter function,
          * and then calling the target with each pre-processed argument
          * replaced by the result of its corresponding filter function.
          * 

    * The pre-processing is performed by one or more method handles, * specified in the elements of the {@code filters} array. - * Null arguments in the array are ignored, and the corresponding arguments left unchanged. + * The first element of the filter array corresponds to the {@code pos} + * argument of the target, and so on in sequence. + *

    + * Null arguments in the array are treated as identity functions, + * and the corresponding arguments left unchanged. * (If there are no non-null elements in the array, the original target is returned.) * Each filter is applied to the corresponding argument of the adapter. *

    * If a filter {@code F} applies to the {@code N}th argument of - * the method handle, then {@code F} must be a method handle which + * the target, then {@code F} must be a method handle which * takes exactly one argument. The type of {@code F}'s sole argument * replaces the corresponding argument type of the target * in the resulting adapted method handle. @@ -1831,6 +1786,7 @@ * parameter type of the target. *

    * It is an error if there are elements of {@code filters} + * (null or not) * which do not correspond to argument positions in the target. * Example: *

    @@ -1849,15 +1805,23 @@
     MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
     assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
          * 
    + *

    Here is pseudocode for the resulting adapter: + *

    +     * V target(P... p, A[i]... a[i], B... b);
    +     * A[i] filter[i](V[i]);
    +     * T adapter(P... p, V[i]... v[i], B... b) {
    +     *   return target(p..., f[i](v[i])..., b...);
    +     * }
    +     * 
    * * @param target the method handle to invoke after arguments are filtered * @param pos the position of the first argument to filter * @param filters method handles to call initially on filtered arguments * @return method handle which incorporates the specified argument filtering logic - * @throws NullPointerException if the {@code target} argument is null + * @throws NullPointerException if the target is null * or if the {@code filters} array is null * @throws IllegalArgumentException if a non-null element of {@code filters} - * does not match a corresponding argument type of {@code target} as described above, + * does not match a corresponding argument type of target as described above, * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()} */ public static @@ -1891,15 +1855,18 @@ } /** - * Adapts a target method handle {@code target} by post-processing - * its return value with a unary filter function. + * Adapts a target method handle by post-processing + * its return value (if any) with a filter (another method handle). + * The result of the filter is returned from the adapter. *

    - * If a filter {@code F} applies to the return value of - * the target method handle, then {@code F} must be a method handle which - * takes exactly one argument. The return type of {@code F} + * If the target returns a value, the filter must accept that value as + * its only argument. + * If the target returns void, the filter must accept no arguments. + *

    + * The return type of the filter * replaces the return type of the target * in the resulting adapted method handle. - * The argument type of {@code F} must be identical to the + * The argument type of the filter (if any) must be identical to the * return type of the target. * Example: *

    @@ -1914,12 +1881,35 @@
     MethodHandle f0 = filterReturnValue(cat, length);
     System.out.println((int) f0.invokeExact("x", "y")); // 2
          * 
    + *

    Here is pseudocode for the resulting adapter: + *

    +     * V target(A...);
    +     * T filter(V);
    +     * T adapter(A... a) {
    +     *   V v = target(a...);
    +     *   return filter(v);
    +     * }
    +     * // and if the target has a void return:
    +     * void target2(A...);
    +     * T filter2();
    +     * T adapter2(A... a) {
    +     *   target2(a...);
    +     *   return filter2();
    +     * }
    +     * // and if the filter has a void return:
    +     * V target3(A...);
    +     * void filter3(V);
    +     * void adapter3(A... a) {
    +     *   V v = target3(a...);
    +     *   filter3(v);
    +     * }
    +     * 
    * @param target the method handle to invoke before filtering the return value * @param filter method handle to call on the return value * @return method handle which incorporates the specified return value filtering logic * @throws NullPointerException if either argument is null - * @throws IllegalArgumentException if {@code filter} - * does not match the return type of {@code target} as described above + * @throws IllegalArgumentException if the argument list of {@code filter} + * does not match the return type of target as described above */ public static MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { @@ -1948,55 +1938,87 @@ } /** - * Adapts a target method handle {@code target} by pre-processing + * Adapts a target method handle by pre-processing * some of its arguments, and then calling the target with - * the result of the pre-processing, plus all original arguments. + * the result of the pre-processing, inserted into the original + * sequence of arguments. + *

    + * The pre-processing is performed by {@code combiner}, a second method handle. + * Of the arguments passed to the adapter, the first {@code N} arguments + * are copied to the combiner, which is then called. + * (Here, {@code N} is defined as the parameter count of the combiner.) + * After this, control passes to the target, with any result + * from the combiner inserted before the original {@code N} incoming + * arguments. *

    - * The pre-processing is performed by a second method handle, the {@code combiner}. - * The first {@code N} arguments passed to the adapter, - * are copied to the combiner, which then produces a result. - * (Here, {@code N} is defined as the parameter count of the adapter.) - * After this, control passes to the {@code target}, with both the result - * of the combiner, and all the original incoming arguments. + * If the combiner returns a value, the first parameter type of the target + * must be identical with the return type of the combiner, and the next + * {@code N} parameter types of the target must exactly match the parameters + * of the combiner. *

    - * The first argument type of the target must be identical with the - * return type of the combiner. + * If the combiner has a void return, no result will be inserted, + * and the first {@code N} parameter types of the target + * must exactly match the parameters of the combiner. + *

    * The resulting adapter is the same type as the target, except that the - * initial argument type of the target is dropped. + * first parameter type is dropped, + * if it corresponds to the result of the combiner. *

    * (Note that {@link #dropArguments(MethodHandle,int,List) dropArguments} can be used to remove any arguments - * that either the {@code combiner} or {@code target} does not wish to receive. + * that either the combiner or the target does not wish to receive. * If some of the incoming arguments are destined only for the combiner, * consider using {@link MethodHandle#asCollector asCollector} instead, since those * arguments will not need to be live on the stack on entry to the * target.) - *

    - * The first argument of the target must be identical with the - * return value of the combiner. + * Example: + *

    +import static java.lang.invoke.MethodHandles.*;
    +import static java.lang.invoke.MethodType.*;
    +...
    +MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
    +  "println", methodType(void.class, String.class))
    +    .bindTo(System.out);
    +MethodHandle cat = lookup().findVirtual(String.class,
    +  "concat", methodType(String.class, String.class));
    +assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
    +MethodHandle catTrace = foldArguments(cat, trace);
    +// also prints "boo":
    +assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
    +     * 
    *

    Here is pseudocode for the resulting adapter: *

    -     * // there are N arguments in the A sequence
    +     * // there are N arguments in A...
          * T target(V, A[N]..., B...);
          * V combiner(A...);
          * T adapter(A... a, B... b) {
          *   V v = combiner(a...);
          *   return target(v, a..., b...);
          * }
    +     * // and if the combiner has a void return:
    +     * T target2(A[N]..., B...);
    +     * void combiner2(A...);
    +     * T adapter2(A... a, B... b) {
    +     *   combiner2(a...);
    +     *   return target2(a..., b...);
    +     * }
          * 
    * @param target the method handle to invoke after arguments are combined * @param combiner method handle to call initially on the incoming arguments * @return method handle which incorporates the specified argument folding logic * @throws NullPointerException if either argument is null - * @throws IllegalArgumentException if the first argument type of - * {@code target} is not the same as {@code combiner}'s return type, - * or if the following argument types of {@code target} + * @throws IllegalArgumentException if {@code combiner}'s return type + * is non-void and not the same as the first argument type of + * the target, or if the initial {@code N} argument types + * of the target + * (skipping one matching the {@code combiner}'s return type) * are not identical with the argument types of {@code combiner} */ public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { + int pos = 0; MethodType targetType = target.type(); MethodType combinerType = combiner.type(); - int foldPos = 0; // always at the head, at present + int foldPos = pos; int foldArgs = combinerType.parameterCount(); int foldVals = combinerType.returnType() == void.class ? 0 : 1; int afterInsertPos = foldPos + foldVals; @@ -2045,7 +2067,7 @@ * @throws NullPointerException if any argument is null * @throws IllegalArgumentException if {@code test} does not return boolean, * or if all three method types do not match (with the return - * type of {@code test} changed to match that of {@code target}). + * type of {@code test} changed to match that of the target). */ public static MethodHandle guardWithTest(MethodHandle test, @@ -2135,7 +2157,7 @@ int hpc = hargs.size(), tpc = targs.size(); if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs)) throw misMatchedTypes("target and handler types", ttype, htype); - handler = dropArguments(handler, hpc, hargs.subList(hpc, tpc)); + handler = dropArguments(handler, 1+hpc, targs.subList(hpc, tpc)); htype = handler.type(); } return MethodHandleImpl.makeGuardWithCatch(target, exType, handler); @@ -2155,234 +2177,4 @@ MethodHandle throwException(Class returnType, Class exType) { return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType)); } - - /** - * Produces an instance of the given single-method interface which redirects - * its calls to the given method handle. - *

    - * A single-method interface is an interface which declares a unique method. - * When determining the unique method of a single-method interface, - * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) - * are disregarded. For example, {@link java.util.Comparator} is a single-method interface, - * even though it re-declares the {@code Object.equals} method. - *

    - * The type must be public. No additional access checks are performed. - *

    - * The resulting instance of the required type will respond to - * invocation of the type's single abstract method by calling - * the given {@code target} on the incoming arguments, - * and returning or throwing whatever the {@code target} - * returns or throws. The invocation will be as if by - * {@code target.invoke}. - * The target's type will be checked before the - * instance is created, as if by a call to {@code asType}, - * which may result in a {@code WrongMethodTypeException}. - *

    - * The wrapper instance will implement the requested interface - * and its super-types, but no other single-method interfaces. - * This means that the instance will not unexpectedly - * pass an {@code instanceof} test for any unrequested type. - *

    - * Implementation Note: - * Therefore, each instance must implement a unique single-method interface. - * Implementations may not bundle together - * multiple single-method interfaces onto single implementation classes - * in the style of {@link java.awt.AWTEventMulticaster}. - *

    - * The method handle may throw an undeclared exception, - * which means any checked exception (or other checked throwable) - * not declared by the requested type's single abstract method. - * If this happens, the throwable will be wrapped in an instance of - * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} - * and thrown in that wrapped form. - *

    - * Like {@link java.lang.Integer#valueOf Integer.valueOf}, - * {@code asInstance} is a factory method whose results are defined - * by their behavior. - * It is not guaranteed to return a new instance for every call. - *

    - * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods} - * and other corner cases, the interface may also have several abstract methods - * with the same name but having distinct descriptors (types of returns and parameters). - * In this case, all the methods are bound in common to the one given {@code target}. - * The type check and effective {@code asType} conversion is applied to each - * method type descriptor, and all abstract methods are bound to the {@code target} in common. - * Beyond this type check, no further checks are made to determine that the - * abstract methods are related in any way. - *

    - * Future versions of this API may accept additional types, - * such as abstract classes with single abstract methods. - * Future versions of this API may also equip wrapper instances - * with one or more additional public "marker" interfaces. - * - * @param target the method handle to invoke from the wrapper - * @param smType the desired type of the wrapper, a single-method interface - * @return a correctly-typed wrapper for the given {@code target} - * @throws NullPointerException if either argument is null - * @throws IllegalArgumentException if the {@code smType} is not a - * valid argument to this method - * @throws WrongMethodTypeException if the {@code target} cannot - * be converted to the type required by the requested interface - */ - // Other notes to implementors: - //

    - // No stable mapping is promised between the single-method interface and - // the implementation class C. Over time, several implementation - // classes might be used for the same type. - //

    - // If the implementation is able - // to prove that a wrapper of the required type - // has already been created for a given - // method handle, or for another method handle with the - // same behavior, the implementation may return that wrapper in place of - // a new wrapper. - //

    - // This method is designed to apply to common use cases - // where a single method handle must interoperate with - // an interface that implements a function-like - // API. Additional variations, such as single-abstract-method classes with - // private constructors, or interfaces with multiple but related - // entry points, must be covered by hand-written or automatically - // generated adapter classes. - // - public static - T asInstance(final MethodHandle target, final Class smType) { - // POC implementation only; violates the above contract several ways - final Method sm = getSingleMethod(smType); - if (sm == null) - throw new IllegalArgumentException("not a single-method interface: "+smType.getName()); - MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes()); - MethodHandle checkTarget = target.asType(smMT); // make throw WMT - checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class)); - final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, smMT.parameterCount()); - return smType.cast(Proxy.newProxyInstance( - smType.getClassLoader(), - new Class[]{ smType, WrapperInstance.class }, - new InvocationHandler() { - private Object getArg(String name) { - if ((Object)name == "getWrapperInstanceTarget") return target; - if ((Object)name == "getWrapperInstanceType") return smType; - throw new AssertionError(); - } - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getDeclaringClass() == WrapperInstance.class) - return getArg(method.getName()); - if (method.equals(sm)) - return vaTarget.invokeExact(args); - if (isObjectMethod(method)) - return callObjectMethod(this, method, args); - throw new InternalError(); - } - })); - } - - /** - * Determines if the given object was produced by a call to {@link #asInstance asInstance}. - * @param x any reference - * @return true if the reference is not null and points to an object produced by {@code asInstance} - */ - public static - boolean isWrapperInstance(Object x) { - return x instanceof WrapperInstance; - } - - private static WrapperInstance asWrapperInstance(Object x) { - try { - if (x != null) - return (WrapperInstance) x; - } catch (ClassCastException ex) { - } - throw new IllegalArgumentException("not a wrapper instance"); - } - - /** - * Produces or recovers a target method handle which is behaviorally - * equivalent to the unique method of this wrapper instance. - * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}. - * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. - * @param x any reference - * @return a method handle implementing the unique method - * @throws IllegalArgumentException if the reference x is not to a wrapper instance - */ - public static - MethodHandle wrapperInstanceTarget(Object x) { - return asWrapperInstance(x).getWrapperInstanceTarget(); - } - - /** - * Recovers the unique single-method interface type for which this wrapper instance was created. - * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}. - * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. - * @param x any reference - * @return the single-method interface type for which the wrapper was created - * @throws IllegalArgumentException if the reference x is not to a wrapper instance - */ - public static - Class wrapperInstanceType(Object x) { - return asWrapperInstance(x).getWrapperInstanceType(); - } - - private static - boolean isObjectMethod(Method m) { - switch (m.getName()) { - case "toString": - return (m.getReturnType() == String.class - && m.getParameterTypes().length == 0); - case "hashCode": - return (m.getReturnType() == int.class - && m.getParameterTypes().length == 0); - case "equals": - return (m.getReturnType() == boolean.class - && m.getParameterTypes().length == 1 - && m.getParameterTypes()[0] == Object.class); - } - return false; - } - - private static - Object callObjectMethod(Object self, Method m, Object[] args) { - assert(isObjectMethod(m)) : m; - switch (m.getName()) { - case "toString": - return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode()); - case "hashCode": - return System.identityHashCode(self); - case "equals": - return (self == args[0]); - } - return null; - } - - private static - Method getSingleMethod(Class smType) { - Method sm = null; - for (Method m : smType.getMethods()) { - int mod = m.getModifiers(); - if (Modifier.isAbstract(mod)) { - if (sm != null && !isObjectMethod(sm)) - return null; // too many abstract methods - sm = m; - } - } - if (!smType.isInterface() && getSingleConstructor(smType) == null) - return null; // wrong kind of constructor - return sm; - } - - private static - Constructor getSingleConstructor(Class smType) { - for (Constructor c : smType.getDeclaredConstructors()) { - if (c.getParameterTypes().length == 0) { - int mod = c.getModifiers(); - if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) - return c; - } - } - return null; - } - - /*non-public*/ - static MethodHandle asVarargsCollector(MethodHandle target, Class arrayType) { - return MethodHandleImpl.asVarargsCollector(target, arrayType); - } } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodType.java --- a/src/share/classes/java/lang/invoke/MethodType.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodType.java Sat Jun 04 06:52:01 2011 -0700 @@ -163,7 +163,13 @@ public static MethodType methodType(Class rtype, List> ptypes) { boolean notrust = false; // random List impl. could return evil ptypes array - return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust); + return makeImpl(rtype, listToArray(ptypes), notrust); + } + + private static Class[] listToArray(List> ptypes) { + // sanity check the size before the toArray call, since size might be huge + checkSlotCount(ptypes.size()); + return ptypes.toArray(NO_PTYPES); } /** @@ -228,7 +234,7 @@ */ /*trusted*/ static MethodType makeImpl(Class rtype, Class[] ptypes, boolean trusted) { - if (ptypes == null || ptypes.length == 0) { + if (ptypes.length == 0) { ptypes = NO_PTYPES; trusted = true; } MethodType mt1 = new MethodType(rtype, ptypes); @@ -267,7 +273,7 @@ * @param objectArgCount number of parameters (excluding the final array parameter if any) * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]} * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments - * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray}) + * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray} is true) * @see #genericMethodType(int) */ public static @@ -372,7 +378,7 @@ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null */ public MethodType insertParameterTypes(int num, List> ptypesToInsert) { - return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES)); + return insertParameterTypes(num, listToArray(ptypesToInsert)); } /** @@ -449,7 +455,8 @@ /** * Reports if this type contains a wrapper argument or return value. * Wrappers are types which box primitive values, such as {@link Integer}. - * The reference type {@code java.lang.Void} counts as a wrapper. + * The reference type {@code java.lang.Void} counts as a wrapper, + * if it occurs as a return type. * @return true if any of the types are wrappers */ public boolean hasWrappers() { @@ -641,14 +648,57 @@ } return true; } - private static boolean canConvert(Class src, Class dst) { - if (src == dst || dst == void.class) return true; - if (src.isPrimitive() && dst.isPrimitive()) { - if (!Wrapper.forPrimitiveType(dst) - .isConvertibleFrom(Wrapper.forPrimitiveType(src))) + /*non-public*/ + static boolean canConvert(Class src, Class dst) { + // short-circuit a few cases: + if (src == dst || dst == Object.class) return true; + // the remainder of this logic is documented in MethodHandle.asType + if (src.isPrimitive()) { + // can force void to an explicit null, a la reflect.Method.invoke + // can also force void to a primitive zero, by analogy + if (src == void.class) return true; //or !dst.isPrimitive()? + Wrapper sw = Wrapper.forPrimitiveType(src); + if (dst.isPrimitive()) { + // P->P must widen + return Wrapper.forPrimitiveType(dst).isConvertibleFrom(sw); + } else { + // P->R must box and widen + return dst.isAssignableFrom(sw.wrapperType()); + } + } else if (dst.isPrimitive()) { + // any value can be dropped + if (dst == void.class) return true; + Wrapper dw = Wrapper.forPrimitiveType(dst); + // R->P must be able to unbox (from a dynamically chosen type) and widen + // For example: + // Byte/Number/Comparable/Object -> dw:Byte -> byte. + // Character/Comparable/Object -> dw:Character -> char + // Boolean/Comparable/Object -> dw:Boolean -> boolean + // This means that dw must be cast-compatible with src. + if (src.isAssignableFrom(dw.wrapperType())) { + return true; + } + // The above does not work if the source reference is strongly typed + // to a wrapper whose primitive must be widened. For example: + // Byte -> unbox:byte -> short/int/long/float/double + // Character -> unbox:char -> int/long/float/double + if (Wrapper.isWrapperType(src) && + dw.isConvertibleFrom(Wrapper.forWrapperType(src))) { + // can unbox from src and then widen to dst + return true; + } + // We have already covered cases which arise due to runtime unboxing + // of a reference type which covers several wrapper types: + // Object -> cast:Integer -> unbox:int -> long/float/double + // Serializable -> cast:Byte -> unbox:byte -> byte/short/int/long/float/double + // An marginal case is Number -> dw:Character -> char, which would be OK if there were a + // subclass of Number which wraps a value that can convert to char. + // Since there is none, we don't need an extra check here to cover char or boolean. return false; + } else { + // R->R always works, since null is always valid dynamically + return true; } - return true; } /// Queries which have to do with the bytecode architecture @@ -733,15 +783,21 @@ * @param descriptor a bytecode-level type descriptor string "(T...)T" * @param loader the class loader in which to look up the types * @return a method type matching the bytecode-level type descriptor + * @throws NullPointerException if the string is null * @throws IllegalArgumentException if the string is not well-formed * @throws TypeNotPresentException if a named type cannot be found */ public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader) throws IllegalArgumentException, TypeNotPresentException { + if (!descriptor.startsWith("(") || // also generates NPE if needed + descriptor.indexOf(')') < 0 || + descriptor.indexOf('.') >= 0) + throw new IllegalArgumentException("not a method descriptor: "+descriptor); List> types = BytecodeDescriptor.parseMethod(descriptor, loader); Class rtype = types.remove(types.size() - 1); - Class[] ptypes = types.toArray(NO_PTYPES); + checkSlotCount(types.size()); + Class[] ptypes = listToArray(types); return makeImpl(rtype, ptypes, true); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MethodTypeForm.java --- a/src/share/classes/java/lang/invoke/MethodTypeForm.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java Sat Jun 04 06:52:01 2011 -0700 @@ -448,6 +448,8 @@ Class[] cs = null; for (int imax = ts.length, i = 0; i < imax; i++) { Class c = canonicalize(ts[i], how); + if (c == void.class) + c = null; // a Void parameter was unwrapped to void; ignore if (c != null) { if (cs == null) cs = ts.clone(); diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/MutableCallSite.java --- a/src/share/classes/java/lang/invoke/MutableCallSite.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/MutableCallSite.java Sat Jun 04 06:52:01 2011 -0700 @@ -37,12 +37,13 @@ *

    * Here is an example of a mutable call site which introduces a * state variable into a method handle chain. + * *

     MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
     MethodHandle MH_name = name.dynamicInvoker();
    -MethodType MT_str2 = MethodType.methodType(String.class, String.class);
    +MethodType MT_str1 = MethodType.methodType(String.class);
     MethodHandle MH_upcase = MethodHandles.lookup()
    -    .findVirtual(String.class, "toUpperCase", MT_str2);
    +    .findVirtual(String.class, "toUpperCase", MT_str1);
     MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
     name.setTarget(MethodHandles.constant(String.class, "Rocky"));
     assertEquals("ROCKY", (String) worker1.invokeExact());
    @@ -53,8 +54,10 @@
      * 

    * The same call site may be used in several places at once. *

    -MethodHandle MH_dear = MethodHandles.lookup()
    -    .findVirtual(String.class, "concat", MT_str2).bindTo(", dear?");
    +MethodType MT_str2 = MethodType.methodType(String.class, String.class);
    +MethodHandle MH_cat = lookup().findVirtual(String.class,
    +  "concat", methodType(String.class, String.class));
    +MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
     MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
     assertEquals("Fred, dear?", (String) worker2.invokeExact());
     name.setTarget(MethodHandles.constant(String.class, "Wilma"));
    diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/SpreadGeneric.java
    --- a/src/share/classes/java/lang/invoke/SpreadGeneric.java	Sat Jun 04 06:45:17 2011 -0700
    +++ b/src/share/classes/java/lang/invoke/SpreadGeneric.java	Sat Jun 04 06:52:01 2011 -0700
    @@ -126,7 +126,7 @@
             return spreadGen;
         }
     
    -    public String toString() {
    +    String debugString() {
             return getClass().getSimpleName()+targetType+"["+spreadCount+"]";
         }
     
    @@ -224,7 +224,7 @@
             protected final MethodHandle target;   // (any**N) => R
     
             @Override
    -        public String toString() {
    +        String debugString() {
                 return addTypeString(target, this);
             }
     
    diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/SwitchPoint.java
    --- a/src/share/classes/java/lang/invoke/SwitchPoint.java	Sat Jun 04 06:45:17 2011 -0700
    +++ b/src/share/classes/java/lang/invoke/SwitchPoint.java	Sat Jun 04 06:52:01 2011 -0700
    @@ -56,16 +56,17 @@
      * 

    * Here is an example of a switch point in action: *

    -MethodType MT_str2 = MethodType.methodType(String.class, String.class);
     MethodHandle MH_strcat = MethodHandles.lookup()
    -    .findVirtual(String.class, "concat", MT_str2);
    +    .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
     SwitchPoint spt = new SwitchPoint();
    +assert(spt.isValid());
     // the following steps may be repeated to re-use the same switch point:
    -MethodHandle worker1 = strcat;
    -MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
    +MethodHandle worker1 = MH_strcat;
    +MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
     MethodHandle worker = spt.guardWithTest(worker1, worker2);
     assertEquals("method", (String) worker.invokeExact("met", "hod"));
     SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
    +assert(!spt.isValid());
     assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
      * 
    *

    @@ -125,6 +126,19 @@ } /** + * Determines if this switch point is still valid. + *

    + * Since invalidation is a global and immediate operation, + * this query must be sequenced with any + * other threads that could invalidate this switch point. + * It may therefore be expensive. + * @return true if this switch point has never been invalidated + */ + public boolean isValid() { + return (mcs.getTarget() == K_true); + } + + /** * Returns a method handle which always delegates either to the target or the fallback. * The method handle will delegate to the target exactly as long as the switch point is valid. * After that, it will permanently delegate to the fallback. @@ -136,6 +150,7 @@ * @param fallback the method handle selected by the switch point after it is invalidated * @return a combined method handle which always calls either the target or fallback * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if the two method types do not match * @see MethodHandles#guardWithTest */ public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) { diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/ToGeneric.java --- a/src/share/classes/java/lang/invoke/ToGeneric.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/ToGeneric.java Sat Jun 04 06:52:01 2011 -0700 @@ -258,7 +258,7 @@ return toGen; } - public String toString() { + String debugString() { return "ToGeneric"+entryType +(primsAtEndOrder!=null?"[reorder]":""); } @@ -340,7 +340,7 @@ protected final MethodHandle convert; // Object -> R @Override - public String toString() { + String debugString() { return target == null ? "prototype:"+convert : addTypeString(target, this); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/lang/invoke/package-info.java --- a/src/share/classes/java/lang/invoke/package-info.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/lang/invoke/package-info.java Sat Jun 04 06:52:01 2011 -0700 @@ -28,7 +28,8 @@ * the Java core class libraries and virtual machine. * *

    - * Certain types in this package have special relations to dynamic + * As described in the Java Virtual Machine Specification, + * certain types in this package have special relations to dynamic * language support in the virtual machine: *

      *
    • The class {@link java.lang.invoke.MethodHandle MethodHandle} contains @@ -42,177 +43,16 @@ *
    • *
    * - *

    Corresponding JVM bytecode format changes

    - * The following low-level information is presented here as a preview of - * changes being made to the Java Virtual Machine specification for JSR 292. - * This information will be incorporated in a future version of the JVM specification. + *

    Summary of relevant Java Virtual Machine changes

    + * The following low-level information summarizes relevant parts of the + * Java Virtual Machine specification. For full details, please see the + * current version of that specification. * - *

    {@code invokedynamic} instruction format

    - * In bytecode, an {@code invokedynamic} instruction is formatted as five bytes. - * The first byte is the opcode 186 (hexadecimal {@code BA}). - * The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions). - * The final two bytes are reserved for future use and required to be zero. - * The constant pool reference of an {@code invokedynamic} instruction is to a entry - * with tag {@code CONSTANT_InvokeDynamic} (decimal 18). See below for its format. - * The entry specifies the following information: - *
      - *
    • a bootstrap method (a {@link java.lang.invoke.MethodHandle MethodHandle} constant)
    • - *
    • the dynamic invocation name (a UTF8 string)
    • - *
    • the argument and return types of the call (encoded as a type descriptor in a UTF8 string)
    • - *
    • optionally, a sequence of additional static arguments to the bootstrap method ({@code ldc}-type constants)
    • - *
    - *

    - * Each instance of an {@code invokedynamic} instruction is called a dynamic call site. - * Multiple instances of an {@code invokedynamic} instruction can share a single - * {@code CONSTANT_InvokeDynamic} entry. - * In any case, distinct call sites always have distinct linkage state. - *

    + * Each occurrence of an {@code invokedynamic} instruction is called a dynamic call site. + *

    {@code invokedynamic} instructions

    * A dynamic call site is originally in an unlinked state. In this state, there is * no target method for the call site to invoke. - * A dynamic call site is linked by means of a bootstrap method, - * as described below. - * - *

    constant pool entries for {@code invokedynamic} instructions

    - * If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18), - * it must contain exactly four more bytes after the tag. - * These bytes are interpreted as two 16-bit indexes, in the usual {@code u2} format. - * The first pair of bytes after the tag must be an index into a side table called the - * bootstrap method table, which is stored in the {@code BootstrapMethods} - * attribute as described below. - * The second pair of bytes must be an index to a {@code CONSTANT_NameAndType}. *

    - * The first index specifies a bootstrap method used by the associated dynamic call sites. - * The second index specifies the method name, argument types, and return type of the dynamic call site. - * The structure of such an entry is therefore analogous to a {@code CONSTANT_Methodref}, - * except that the bootstrap method specifier reference replaces - * the {@code CONSTANT_Class} reference of a {@code CONSTANT_Methodref} entry. - * - *

    constant pool entries for {@linkplain java.lang.invoke.MethodType method types}

    - * If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16), - * it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8} - * entry which represents a method type descriptor. - *

    - * The JVM will ensure that on first - * execution of an {@code ldc} instruction for this entry, a {@link java.lang.invoke.MethodType MethodType} - * will be created which represents the type descriptor. - * Any classes mentioned in the {@code MethodType} will be loaded if necessary, - * but not initialized. - * Access checking and error reporting is performed exactly as it is for - * references by {@code ldc} instructions to {@code CONSTANT_Class} constants. - * - *

    constant pool entries for {@linkplain java.lang.invoke.MethodHandle method handles}

    - * If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15), - * it must contain exactly three more bytes. The first byte after the tag is a subtag - * value which must be in the range 1 through 9, and the last two must be an index to a - * {@code CONSTANT_Fieldref}, {@code CONSTANT_Methodref}, or - * {@code CONSTANT_InterfaceMethodref} entry which represents a field or method - * for which a method handle is to be created. - * Furthermore, the subtag value and the type of the constant index value - * must agree according to the table below. - *

    - * The JVM will ensure that on first execution of an {@code ldc} instruction - * for this entry, a {@link java.lang.invoke.MethodHandle MethodHandle} will be created which represents - * the field or method reference, according to the specific mode implied by the subtag. - *

    - * As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants, - * the {@code Class} or {@code MethodType} object which reifies the field or method's - * type is created. Any classes mentioned in this reification will be loaded if necessary, - * but not initialized, and access checking and error reporting performed as usual. - *

    - * Unlike the reflective {@code Lookup} API, there are no security manager calls made - * when these constants are resolved. - *

    - * The method handle itself will have a type and behavior determined by the subtag as follows: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    Nsubtag namememberMH typebytecode behaviorlookup expression
    1REF_getFieldC.f:T(C)Tgetfield C.f:T{@linkplain java.lang.invoke.MethodHandles.Lookup#findGetter findGetter(C.class,"f",T.class)}
    2REF_getStaticC.f:T( )Tgetstatic C.f:T{@linkplain java.lang.invoke.MethodHandles.Lookup#findStaticGetter findStaticGetter(C.class,"f",T.class)}
    3REF_putFieldC.f:T(C,T)voidputfield C.f:T{@linkplain java.lang.invoke.MethodHandles.Lookup#findSetter findSetter(C.class,"f",T.class)}
    4REF_putStaticC.f:T(T)voidputstatic C.f:T{@linkplain java.lang.invoke.MethodHandles.Lookup#findStaticSetter findStaticSetter(C.class,"f",T.class)}
    5REF_invokeVirtualC.m(A*)T(C,A*)Tinvokevirtual C.m(A*)T{@linkplain java.lang.invoke.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}
    6REF_invokeStaticC.m(A*)T(C,A*)Tinvokestatic C.m(A*)T{@linkplain java.lang.invoke.MethodHandles.Lookup#findStatic findStatic(C.class,"m",MT)}
    7REF_invokeSpecialC.m(A*)T(C,A*)Tinvokespecial C.m(A*)T{@linkplain java.lang.invoke.MethodHandles.Lookup#findSpecial findSpecial(C.class,"m",MT,this.class)}
    8REF_newInvokeSpecialC.<init>(A*)void(A*)Cnew C; dup; invokespecial C.<init>(A*)void{@linkplain java.lang.invoke.MethodHandles.Lookup#findConstructor findConstructor(C.class,MT)}
    9REF_invokeInterfaceC.m(A*)T(C,A*)Tinvokeinterface C.m(A*)T{@linkplain java.lang.invoke.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}
    - *
    - * Here, the type {@code C} is taken from the {@code CONSTANT_Class} reference associated - * with the {@code CONSTANT_NameAndType} descriptor. - * The field name {@code f} or method name {@code m} is taken from the {@code CONSTANT_NameAndType} - * as is the result type {@code T} and (in the case of a method or constructor) the argument type sequence - * {@code A*}. - *

    - * Each method handle constant has an equivalent instruction sequence called its bytecode behavior. - * In general, creating a method handle constant can be done in exactly the same circumstances that - * the JVM would successfully resolve the symbolic references in the bytecode behavior. - * Also, the type of a method handle constant is such that a valid {@code invokeExact} call - * on the method handle has exactly the same JVM stack effects as the bytecode behavior. - * Finally, calling a method handle constant on a valid set of arguments has exactly the same effect - * and returns the same result (if any) as the corresponding bytecode behavior. - *

    - * Each method handle constant also has an equivalent reflective lookup expression, - * which is a query to a method in {@link java.lang.invoke.MethodHandles.Lookup}. - * In the example lookup method expression given in the table above, the name {@code MT} - * stands for a {@code MethodType} built from {@code T} and the sequence of argument types {@code A*}. - * (Note that the type {@code C} is not prepended to the query type {@code MT} even if the member is non-static.) - * In the case of {@code findSpecial}, the name {@code this.class} refers to the class containing - * the bytecodes. - *

    - * The special name {@code } is not allowed. - * The special name {@code } is not allowed except for subtag 8 as shown. - *

    - * The JVM verifier and linker apply the same access checks and restrictions for these references as for the hypothetical - * bytecode instructions specified in the last column of the table. - * A method handle constant will successfully resolve to a method handle if the symbolic references - * of the corresponding bytecode instruction(s) would also resolve successfully. - * Otherwise, an attempt to resolve the constant will throw equivalent linkage errors. - * In particular, method handles to - * private and protected members can be created in exactly those classes for which the corresponding - * normal accesses are legal. - *

    - * A constant may refer to a method or constructor with the {@code varargs} - * bit (hexadecimal {@code 0x0080}) set in its modifier bitmask. - * The method handle constant produced for such a method behaves as if - * it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}. - * In other words, the constant method handle will exhibit variable arity, - * when invoked via {@code MethodHandle.invoke}. - * On the other hand, its behavior with respect to {@code invokeExact} will be the same - * as if the {@code varargs} bit were not set. - *

    - * Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types - * resolve class names, they do not force class initialization. - * Method handle constants for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic} - * may force class initialization on their first invocation, just like the corresponding bytecodes. - *

    - * The rules of section 5.4.3 of - * The Java™ Virtual Machine Specification - * apply to the resolution of {@code CONSTANT_MethodType}, {@code CONSTANT_MethodHandle}, - * and {@code CONSTANT_InvokeDynamic} constants, - * by the execution of {@code invokedynamic} and {@code ldc} instructions. - * (Roughly speaking, this means that every use of a constant pool entry - * must lead to the same outcome. - * If the resolution succeeds, the same object reference is produced - * by every subsequent execution of the same instruction. - * If the resolution of the constant causes an error to occur, - * the same error will be re-thrown on every subsequent attempt - * to use this particular constant.) - *

    - * Constants created by the resolution of these constant pool types are not necessarily - * interned. Except for {@code CONSTANT_Class} and {@code CONSTANT_String} entries, - * two distinct constant pool entries might not resolve to the same reference - * even if they contain the same symbolic reference. - * - *

    Bootstrap Methods

    * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction), * the call site must first be linked. * Linking is accomplished by calling a bootstrap method @@ -234,15 +74,14 @@ * call site execution. * Linkage does not trigger class initialization. *

    - * Next, the bootstrap method call is started, with at least four values being stacked: + * The bootstrap method is invoked on at least three values: *

      - *
    • a {@code MethodHandle}, the resolved bootstrap method itself
    • *
    • a {@code MethodHandles.Lookup}, a lookup object on the caller class in which dynamic call site occurs
    • *
    • a {@code String}, the method name mentioned in the call site
    • *
    • a {@code MethodType}, the resolved type descriptor of the call
    • - *
    • optionally, one or more additional static arguments
    • + *
    • optionally, between 1 and 251 additional static arguments taken from the constant pool
    • *
    - * The method handle is then applied to the other values as if by + * Invocation is as if by * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}. * The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass). * The type of the call site's target must be exactly equal to the type @@ -250,38 +89,15 @@ * the bootstrap method. * The call site then becomes permanently linked to the dynamic call site. *

    - * As long as each bootstrap method can be correctly invoked - * by MethodHandle.invoke, its detailed type is arbitrary. - * For example, the first argument could be {@code Object} - * instead of {@code MethodHandles.Lookup}, and the return type - * could also be {@code Object} instead of {@code CallSite}. - * (Note that the types and number of the stacked arguments limit - * the legal kinds of bootstrap methods to appropriately typed - * static methods and constructors of {@code CallSite} subclasses.) - *

    - * After resolution, the linkage process may fail in a variety of ways. - * All failures are reported by a {@link java.lang.BootstrapMethodError BootstrapMethodError}, + * As documented in the JVM specification, all failures arising from + * the linkage of a dynamic call site are reported + * by a {@link java.lang.BootstrapMethodError BootstrapMethodError}, * which is thrown as the abnormal termination of the dynamic call * site execution. - * The following circumstances will cause this: - *

      - *
    • the index to the bootstrap method specifier is out of range
    • - *
    • the bootstrap method cannot be resolved
    • - *
    • the {@code MethodType} to pass to the bootstrap method cannot be resolved
    • - *
    • a static argument to the bootstrap method cannot be resolved - * (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType}, - * or {@code CONSTANT_MethodHandle} argument cannot be linked)
    • - *
    • the bootstrap method has the wrong arity, - * causing {@code MethodHandle.invoke} to throw {@code WrongMethodTypeException}
    • - *
    • the bootstrap method has a wrong argument or return type
    • - *
    • the bootstrap method invocation completes abnormally
    • - *
    • the result from the bootstrap invocation is not a reference to - * an object of type {@link java.lang.invoke.CallSite CallSite}
    • - *
    • the target of the {@code CallSite} does not have a target of - * the expected {@code MethodType}
    • - *
    + * If this happens, the same error will the thrown for all subsequent + * attempts to execute the dynamic call site. * - *

    timing of linkage

    + *

    timing of linkage

    * A dynamic call site is linked just before its first execution. * The bootstrap method call implementing the linkage occurs within * a thread that is attempting a first execution. @@ -306,7 +122,7 @@ * all threads. Any other bootstrap method calls are allowed to complete, but their * results are ignored, and their dynamic call site invocations proceed with the originally * chosen target object. - * + *

    * Discussion: * These rules do not enable the JVM to duplicate dynamic call sites, @@ -315,64 +131,15 @@ * just before its first invocation. * There is no way to undo the effect of a completed bootstrap method call. * - *

    the {@code BootstrapMethods} attribute

    - * Each {@code CONSTANT_InvokeDynamic} entry contains an index which references - * a bootstrap method specifier; all such specifiers are contained in a separate array. - * This array is defined by a class attribute named {@code BootstrapMethods}. - * The body of this attribute consists of a sequence of byte pairs, all interpreted as - * as 16-bit counts or constant pool indexes, in the {@code u2} format. - * The attribute body starts with a count of bootstrap method specifiers, - * which is immediately followed by the sequence of specifiers. - *

    - * Each bootstrap method specifier contains an index to a - * {@code CONSTANT_MethodHandle} constant, which is the bootstrap - * method itself. - * This is followed by a count, and then a sequence (perhaps empty) of - * indexes to additional static arguments - * for the bootstrap method. - *

    - * During class loading, the verifier must check the structure of the - * {@code BootstrapMethods} attribute. In particular, each constant - * pool index must be of the correct type. A bootstrap method index - * must refer to a {@code CONSTANT_MethodHandle} (tag 15). - * Every other index must refer to a valid operand of an - * {@code ldc_w} or {@code ldc2_w} instruction (tag 3..8 or 15..16). - * - *

    static arguments to the bootstrap method

    - * An {@code invokedynamic} instruction specifies at least three arguments - * to pass to its bootstrap method: - * The caller class (expressed as a {@link java.lang.invoke.MethodHandles.Lookup Lookup object}, - * the name (extracted from the {@code CONSTANT_NameAndType} entry), - * and the type (also extracted from the {@code CONSTANT_NameAndType} entry). - * The {@code invokedynamic} instruction may specify additional metadata values - * to pass to its bootstrap method. - * Collectively, these values are called static arguments to the - * {@code invokedynamic} instruction, because they are used once at link - * time to determine the instruction's behavior on subsequent sets of - * dynamic arguments. - *

    - * Static arguments are used to communicate application-specific meta-data - * to the bootstrap method. - * Drawn from the constant pool, they may include references to classes, method handles, - * strings, or numeric data that may be relevant to the task of linking that particular call site. - *

    - * Static arguments are specified constant pool indexes stored in the {@code BootstrapMethods} attribute. - * Before the bootstrap method is invoked, each index is used to compute an {@code Object} - * reference to the indexed value in the constant pool. - * The valid constant pool entries are listed in this table: - * - * - * - * - * - * - * - * - * - * - * - *
    entry typeargument typeargument value
    CONSTANT_Stringjava.lang.Stringthe indexed string literal
    CONSTANT_Classjava.lang.Classthe indexed class, resolved
    CONSTANT_Integerjava.lang.Integerthe indexed int value
    CONSTANT_Longjava.lang.Longthe indexed long value
    CONSTANT_Floatjava.lang.Floatthe indexed float value
    CONSTANT_Doublejava.lang.Doublethe indexed double value
    CONSTANT_MethodHandlejava.lang.invoke.MethodHandlethe indexed method handle constant
    CONSTANT_MethodTypejava.lang.invoke.MethodTypethe indexed method type constant
    - *
    + *

    types of bootstrap methods

    + * As long as each bootstrap method can be correctly invoked + * by {@code MethodHandle.invoke}, its detailed type is arbitrary. + * For example, the first argument could be {@code Object} + * instead of {@code MethodHandles.Lookup}, and the return type + * could also be {@code Object} instead of {@code CallSite}. + * (Note that the types and number of the stacked arguments limit + * the legal kinds of bootstrap methods to appropriately typed + * static methods and constructors of {@code CallSite} subclasses.) *

    * If a given {@code invokedynamic} instruction specifies no static arguments, * the instruction's bootstrap method will be invoked on three arguments, @@ -380,7 +147,8 @@ * If the {@code invokedynamic} instruction specifies one or more static arguments, * those values will be passed as additional arguments to the method handle. * (Note that because there is a limit of 255 arguments to any method, - * at most 252 extra arguments can be supplied.) + * at most 251 extra arguments can be supplied, since the bootstrap method + * handle itself and its first three arguments must also be stacked.) * The bootstrap method will be invoked as if by either {@code MethodHandle.invoke} * or {@code invokeWithArguments}. (There is no way to tell the difference.) *

    @@ -390,12 +158,11 @@ * then some or all of the arguments specified here may be collected into a trailing array parameter. * (This is not a special rule, but rather a useful consequence of the interaction * between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods, - * and the {@code java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.) + * and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.) *

    * Given these rules, here are examples of legal bootstrap method declarations, * given various numbers {@code N} of extra arguments. * The first rows (marked {@code *}) will work for any number of extra arguments. - * * * * @@ -408,7 +175,6 @@ * * *
    Nsample bootstrap method
    *CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
    2CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)
    2CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)
    - *
    * The last example assumes that the extra arguments are of type * {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively. * The second-to-last example assumes that all extra arguments are of type @@ -431,34 +197,6 @@ * since each call site could be given its own unique bootstrap method. * Such a practice is likely to produce large class files and constant pools. * - *

    Structure Summary

    - *
    // summary of constant and attribute structures
    -struct CONSTANT_MethodHandle_info {
    -  u1 tag = 15;
    -  u1 reference_kind;       // 1..8 (one of REF_invokeVirtual, etc.)
    -  u2 reference_index;      // index to CONSTANT_Fieldref or *Methodref
    -}
    -struct CONSTANT_MethodType_info {
    -  u1 tag = 16;
    -  u2 descriptor_index;    // index to CONSTANT_Utf8, as in NameAndType
    -}
    -struct CONSTANT_InvokeDynamic_info {
    -  u1 tag = 18;
    -  u2 bootstrap_method_attr_index;  // index into BootstrapMethods_attr
    -  u2 name_and_type_index;          // index to CONSTANT_NameAndType, as in Methodref
    -}
    -struct BootstrapMethods_attr {
    - u2 name;  // CONSTANT_Utf8 = "BootstrapMethods"
    - u4 size;
    - u2 bootstrap_method_count;
    - struct bootstrap_method_specifier {
    -   u2 bootstrap_method_ref;  // index to CONSTANT_MethodHandle
    -   u2 bootstrap_argument_count;
    -   u2 bootstrap_arguments[bootstrap_argument_count];  // constant pool indexes
    - } bootstrap_methods[bootstrap_method_count];
    -}
    - * 
    - * * @author John Rose, JSR 292 EG * @since 1.7 */ diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/java/nio/file/Path.java --- a/src/share/classes/java/nio/file/Path.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/java/nio/file/Path.java Sat Jun 04 06:52:01 2011 -0700 @@ -460,15 +460,13 @@ /** * Returns a URI to represent this path. * - *

    This method constructs a hierarchical {@link URI} that is absolute - * with a non-empty path component. Its {@link URI#getScheme() scheme} is - * equal to the URI scheme that identifies the provider. The exact form of - * the other URI components is highly provider dependent. In particular, it - * is implementation dependent if its query, fragment, and authority - * components are defined or undefined. + *

    This method constructs an absolute {@link URI} with a {@link + * URI#getScheme() scheme} equal to the URI scheme that identifies the + * provider. The exact form of the scheme specific part is highly provider + * dependent. * - *

    For the default provider the {@link URI#getPath() path} component - * will represent the {@link #toAbsolutePath absolute} path; the query, + *

    In the case of the default provider, the URI is hierarchical with + * a {@link URI#getPath() path} component that is absolute. The query and * fragment components are undefined. Whether the authority component is * defined or not is implementation dependent. There is no guarantee that * the {@code URI} may be used to construct a {@link java.io.File java.io.File}. @@ -497,7 +495,7 @@ * A format for compound URIs is not defined in this release; such a scheme * may be added in a future release. * - * @return an absolute, hierarchical URI with a non-empty path component + * @return the URI representing this path * * @throws java.io.IOError * if an I/O error occurs obtaining the absolute path, or where a diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java --- a/src/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/javax/swing/plaf/nimbus/NimbusLookAndFeel.java Sat Jun 04 06:52:01 2011 -0700 @@ -588,6 +588,10 @@ } private void addDefault(String key, Object value) { + if (compiledDefaults == null) { + return; + } + String prefix = parsePrefix(key); if (prefix != null) { Map keys = compiledDefaults.get(prefix); diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/javax/swing/text/Utilities.java --- a/src/share/classes/javax/swing/text/Utilities.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/javax/swing/text/Utilities.java Sat Jun 04 06:52:01 2011 -0700 @@ -337,8 +337,7 @@ // x before x0, return. return 0; } - int currX = x0; - int nextX = currX; + int nextX = x0; // s may be a shared segment, so it is copied prior to calling // the tab expander char[] txt = s.array; @@ -388,19 +387,45 @@ } else { nextX += metrics.charWidth(txt[i]); } - if ((x >= currX) && (x < nextX)) { + if (x < nextX) { // found the hit position... return the appropriate side - int offset = ((round == false) || ((x - currX) < (nextX - x))) ? - (i - txtOffset) : (i + 1 - txtOffset); + int offset; + // the length of the string measured as a whole may differ from // the sum of individual character lengths, for example if // fractional metrics are enabled; and we must guard from this. - while (offset > 0 && metrics.charsWidth(txt, txtOffset, offset) > (x - x0)) { - offset--; + if (round) { + offset = i + 1 - txtOffset; + + int width = metrics.charsWidth(txt, txtOffset, offset); + int span = x - x0; + + if (span < width) { + while (offset > 0) { + int nextWidth = offset > 1 ? metrics.charsWidth(txt, txtOffset, offset - 1) : 0; + + if (span >= nextWidth) { + if (span - nextWidth < width - span) { + offset--; + } + + break; + } + + width = nextWidth; + offset--; + } + } + } else { + offset = i - txtOffset; + + while (offset > 0 && metrics.charsWidth(txt, txtOffset, offset) > (x - x0)) { + offset--; + } } + return offset; } - currX = nextX; } // didn't find, return end offset diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/font/FileFontStrike.java --- a/src/share/classes/sun/font/FileFontStrike.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/font/FileFontStrike.java Sat Jun 04 06:52:01 2011 -0700 @@ -180,7 +180,6 @@ pScalerContext = NullFontScaler.getNullScalerContext(); } else { pScalerContext = fileFont.getScaler().createScalerContext(matrix, - fileFont instanceof TrueTypeFont, desc.aaHint, desc.fmHint, boldness, italic, disableHinting); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/font/FontScaler.java --- a/src/share/classes/sun/font/FontScaler.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/font/FontScaler.java Sat Jun 04 06:52:01 2011 -0700 @@ -242,7 +242,6 @@ freed when corresponding strike is being released. */ abstract long createScalerContext(double[] matrix, - boolean fontType, int aa, int fm, float boldness, float italic, boolean disableHinting); diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/font/FreetypeFontScaler.java --- a/src/share/classes/sun/font/FreetypeFontScaler.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/font/FreetypeFontScaler.java Sat Jun 04 06:52:01 2011 -0700 @@ -210,12 +210,12 @@ return getUnitsPerEMNative(nativeScaler); } - long createScalerContext(double[] matrix, boolean fontType, + long createScalerContext(double[] matrix, int aa, int fm, float boldness, float italic, boolean disableHinting) { if (nativeScaler != 0L) { return createScalerContextNative(nativeScaler, matrix, - fontType, aa, fm, boldness, italic); + aa, fm, boldness, italic); } return NullFontScaler.getNullScalerContext(); } @@ -254,7 +254,7 @@ private native long getUnitsPerEMNative(long pScaler); native long createScalerContextNative(long pScaler, double[] matrix, - boolean fontType, int aa, int fm, float boldness, float italic); + int aa, int fm, float boldness, float italic); /* Freetype scaler context does not contain any pointers that has to be invalidated if native scaler is bad */ diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/font/NullFontScaler.java --- a/src/share/classes/sun/font/NullFontScaler.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/font/NullFontScaler.java Sat Jun 04 06:52:01 2011 -0700 @@ -66,7 +66,7 @@ long getLayoutTableCache() {return 0L;} - long createScalerContext(double[] matrix, boolean fontType, int aa, + long createScalerContext(double[] matrix, int aa, int fm, float boldness, float italic, boolean disableHinting) { return getNullScalerContext(); } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/invoke/util/ValueConversions.java --- a/src/share/classes/sun/invoke/util/ValueConversions.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/invoke/util/ValueConversions.java Sat Jun 04 06:52:01 2011 -0700 @@ -198,27 +198,30 @@ return unbox(Wrapper.forPrimitiveType(type), true, false); } + static private final Integer ZERO_INT = 0, ONE_INT = 1; + /// Primitive conversions public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) { // Maybe merge this code with Wrapper.convert/cast. Number res = null; if (x == null) { if (!cast) return null; - x = wrap.zero(); + return ZERO_INT; } if (x instanceof Number) { res = (Number) x; } else if (x instanceof Boolean) { - res = ((boolean)x ? 1 : 0); + res = ((boolean)x ? ONE_INT : ZERO_INT); } else if (x instanceof Character) { res = (int)(char)x; } else { // this will fail with the required ClassCastException: res = (Number) x; } - if (!cast && !wrap.isConvertibleFrom(Wrapper.forWrapperType(x.getClass()))) + Wrapper xwrap = Wrapper.findWrapperType(x.getClass()); + if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap)) // this will fail with the required ClassCastException: - res = (Number) wrap.wrapperType().cast(x); + return (Number) wrap.wrapperType().cast(x); return res; } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/invoke/util/VerifyAccess.java --- a/src/share/classes/sun/invoke/util/VerifyAccess.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/invoke/util/VerifyAccess.java Sat Jun 04 06:52:01 2011 -0700 @@ -154,9 +154,10 @@ * @return whether they are in the same package */ public static boolean isSamePackage(Class class1, Class class2) { + assert(!class1.isArray() && !class2.isArray()); if (class1 == class2) return true; - if (!loadersAreRelated(class1.getClassLoader(), class2.getClassLoader())) + if (!loadersAreRelated(class1.getClassLoader(), class2.getClassLoader(), false)) return false; String name1 = class1.getName(), name2 = class2.getName(); int dot = name1.lastIndexOf('.'); @@ -169,6 +170,16 @@ return true; } + /** Return the package name for this class. + */ + public static String getPackageName(Class cls) { + assert(!cls.isArray()); + String name = cls.getName(); + int dot = name.lastIndexOf('.'); + if (dot < 0) return ""; + return name.substring(0, dot); + } + /** * Test if two classes are defined as part of the same package member (top-level class). * If this is true, they can share private access with each other. @@ -193,18 +204,33 @@ return pkgmem; } - private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2) { - if (loader1 == loader2 || loader1 == null || loader2 == null) { + private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2, + boolean loader1MustBeParent) { + if (loader1 == loader2 || loader1 == null + || (loader2 == null && !loader1MustBeParent)) { return true; } - for (ClassLoader scan1 = loader1; - scan1 != null; scan1 = scan1.getParent()) { - if (scan1 == loader2) return true; - } for (ClassLoader scan2 = loader2; scan2 != null; scan2 = scan2.getParent()) { if (scan2 == loader1) return true; } + if (loader1MustBeParent) return false; + // see if loader2 is a parent of loader1: + for (ClassLoader scan1 = loader1; + scan1 != null; scan1 = scan1.getParent()) { + if (scan1 == loader2) return true; + } return false; } + + /** + * Is the class loader of parentClass identical to, or an ancestor of, + * the class loader of childClass? + * @param parentClass + * @param childClass + * @return whether parentClass precedes or equals childClass in class loader order + */ + public static boolean classLoaderIsAncestor(Class parentClass, Class childClass) { + return loadersAreRelated(parentClass.getClassLoader(), childClass.getClassLoader(), true); + } } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/invoke/util/Wrapper.java --- a/src/share/classes/sun/invoke/util/Wrapper.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/invoke/util/Wrapper.java Sat Jun 04 06:52:01 2011 -0700 @@ -135,7 +135,7 @@ *

  • any type converted to {@code void} (i.e., dropping a method call's value) *
  • boxing conversion followed by widening reference conversion to {@code Object} *
- * These are the cases allowed by MethodHandle.asType and convertArguments. + * These are the cases allowed by MethodHandle.asType. */ public boolean isConvertibleFrom(Wrapper source) { if (this == source) return true; @@ -258,7 +258,7 @@ } /** Return the wrapper that wraps values into the given wrapper type. - * If it is {@code Object} or an interface, return {@code OBJECT}. + * If it is {@code Object}, return {@code OBJECT}. * Otherwise, it must be a wrapper type. * The type must not be a primitive type. * @throws IllegalArgumentException for unexpected types @@ -277,8 +277,6 @@ if (w != null && w.wrapperType == type) { return w; } - if (type.isInterface()) - return OBJECT; return null; } diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/io/ByteToCharCp833.java --- a/src/share/classes/sun/io/ByteToCharCp833.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/io/ByteToCharCp833.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/io/CharToByteCp833.java --- a/src/share/classes/sun/io/CharToByteCp833.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/io/CharToByteCp833.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/misc/FpUtils.java --- a/src/share/classes/sun/misc/FpUtils.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/misc/FpUtils.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/classes/sun/security/provider/certpath/URICertStore.java --- a/src/share/classes/sun/security/provider/certpath/URICertStore.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/classes/sun/security/provider/certpath/URICertStore.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2010 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/share/native/sun/font/freetypeScaler.c --- a/src/share/native/sun/font/freetypeScaler.c Sat Jun 04 06:45:17 2011 -0700 +++ b/src/share/native/sun/font/freetypeScaler.c Sat Jun 04 06:52:01 2011 -0700 @@ -367,7 +367,7 @@ JNIEXPORT jlong JNICALL Java_sun_font_FreetypeFontScaler_createScalerContextNative( JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix, - jboolean ttFont, jint aa, jint fm, jfloat boldness, jfloat italic) { + jint aa, jint fm, jfloat boldness, jfloat italic) { double dmat[4], ptsz; FTScalerContext *context = (FTScalerContext*) calloc(1, sizeof(FTScalerContext)); diff -r 49aef5a5416e -r 1f39ca0b9598 src/solaris/classes/sun/awt/X11/GtkFileDialogPeer.java --- a/src/solaris/classes/sun/awt/X11/GtkFileDialogPeer.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/solaris/classes/sun/awt/X11/GtkFileDialogPeer.java Sat Jun 04 06:52:01 2011 -0700 @@ -29,8 +29,6 @@ import java.awt.peer.FileDialogPeer; import java.io.File; import java.io.FilenameFilter; -import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; import sun.awt.AWTAccessor; /** @@ -107,9 +105,7 @@ if (b) { Thread t = new Thread() { public void run() { - GtkFileDialogPeer.this.run(fd.getTitle(), fd.getMode(), - fd.getDirectory(), fd.getFile(), fd.getFilenameFilter(), fd.isMultipleMode(), - fd.getX(), fd.getY()); + showNativeDialog(); fd.setVisible(false); } }; @@ -146,4 +142,30 @@ // We do not implement this method because we // have delegated to FileDialog#setFilenameFilter } + + private void showNativeDialog() { + String dirname = fd.getDirectory(); + // File path has a priority against directory path. + String filename = fd.getFile(); + if (filename != null) { + final File file = new File(filename); + if (fd.getMode() == FileDialog.LOAD + && dirname != null + && file.getParent() == null) { + // File path for gtk_file_chooser_set_filename. + filename = dirname + (dirname.endsWith(File.separator) ? "" : + File.separator) + filename; + } + if (fd.getMode() == FileDialog.SAVE && file.getParent() != null) { + // Filename for gtk_file_chooser_set_current_name. + filename = file.getName(); + // Directory path for gtk_file_chooser_set_current_folder. + dirname = file.getParent(); + } + } + GtkFileDialogPeer.this.run(fd.getTitle(), fd.getMode(), dirname, + filename, fd.getFilenameFilter(), + fd.isMultipleMode(), fd.getX(), fd.getY()); + } + } diff -r 49aef5a5416e -r 1f39ca0b9598 src/solaris/classes/sun/awt/X11GraphicsEnvironment.java --- a/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java Sat Jun 04 06:45:17 2011 -0700 +++ b/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java Sat Jun 04 06:52:01 2011 -0700 @@ -121,7 +121,7 @@ // only attempt to initialize Xrender if it was requested if (xRenderRequested) { - xRenderAvailable = initXRender(); + xRenderAvailable = initXRender(xRenderVerbose); if (xRenderVerbose && !xRenderAvailable) { System.out.println( "Could not enable XRender pipeline"); @@ -159,7 +159,7 @@ private static boolean xRenderVerbose; private static boolean xRenderAvailable; - private static native boolean initXRender(); + private static native boolean initXRender(boolean verbose); public static boolean isXRenderAvailable() { return xRenderAvailable; } diff -r 49aef5a5416e -r 1f39ca0b9598 src/solaris/doc/sun/man/man1/ja/keytool.1 --- a/src/solaris/doc/sun/man/man1/ja/keytool.1 Sat Jun 04 06:45:17 2011 -0700 +++ b/src/solaris/doc/sun/man/man1/ja/keytool.1 Sat Jun 04 06:52:01 2011 -0700 @@ -1,4 +1,4 @@ -." Copyright (c) 1998-2011 keytool tool, Oracle and/or its affiliates. All rights reserved. +." Copyright (c) 1998, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/solaris/doc/sun/man/man1/keytool.1 --- a/src/solaris/doc/sun/man/man1/keytool.1 Sat Jun 04 06:45:17 2011 -0700 +++ b/src/solaris/doc/sun/man/man1/keytool.1 Sat Jun 04 06:52:01 2011 -0700 @@ -1,4 +1,4 @@ -." Copyright (c) 1998-2011 keytool tool, Oracle and/or its affiliates. All rights reserved. +." Copyright (c) 1998, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 src/solaris/native/sun/java2d/x11/XRBackendNative.c --- a/src/solaris/native/sun/java2d/x11/XRBackendNative.c Sat Jun 04 06:45:17 2011 -0700 +++ b/src/solaris/native/sun/java2d/x11/XRBackendNative.c Sat Jun 04 06:52:01 2011 -0700 @@ -66,9 +66,10 @@ } XRadialGradient; #endif +#include + #ifdef __solaris__ /* Solaris 10 will not have these symbols at runtime */ -#include #include typedef Picture (*XRenderCreateLinearGradientFuncType) @@ -104,12 +105,20 @@ TRANSFORM.matrix[2][2] = 1<<16; \ } +/* The xrender pipleine requires libXrender.so version 0.9.3 or later. */ +#define REQUIRED_XRENDER_VER1 0 +#define REQUIRED_XRENDER_VER2 9 +#define REQUIRED_XRENDER_VER3 3 -static jboolean IsXRenderAvailable() { +#define PKGINFO_LINE_LEN_MAX 256 +#define PKGINFO_LINE_CNT_MAX 50 + +static jboolean IsXRenderAvailable(jboolean verbose) { void *xrenderlib; int major_opcode, first_event, first_error; + jboolean available = JNI_TRUE; if (!XQueryExtension(awt_display, "RENDER", &major_opcode, &first_event, &first_error)) { @@ -120,23 +129,113 @@ xrenderlib = dlopen("libXrender.so",RTLD_GLOBAL|RTLD_LAZY); if (xrenderlib != NULL) { - XRenderCreateLinearGradientFunc = - (XRenderCreateLinearGradientFuncType) - dlsym(xrenderlib, "XRenderCreateLinearGradient"); + XRenderCreateLinearGradientFunc = + (XRenderCreateLinearGradientFuncType) + dlsym(xrenderlib, "XRenderCreateLinearGradient"); + + XRenderCreateRadialGradientFunc = + (XRenderCreateRadialGradientFuncType) + dlsym(xrenderlib, "XRenderCreateRadialGradient"); + + if (XRenderCreateLinearGradientFunc == NULL || + XRenderCreateRadialGradientFunc == NULL) + { + available = JNI_FALSE; + } + dlclose(xrenderlib); + } else { + available = JNI_FALSE; + } +#else + Dl_info info; + jboolean versionInfoIsFound = JNI_FALSE; + + memset(&info, 0, sizeof(Dl_info)); + if (dladdr(&XRenderChangePicture, &info) && info.dli_fname != NULL) { + char pkgInfoPath[FILENAME_MAX]; + char *pkgFileName = "/pkgconfig/xrender.pc"; + size_t pkgFileNameLen = strlen(pkgFileName); + size_t pos, len = strlen(info.dli_fname); + + pos = len; + while (pos > 0 && info.dli_fname[pos] != '/') { + pos -= 1; + } + + if (pos > 0 && pos < (FILENAME_MAX - pkgFileNameLen - 1)) { + struct stat stat_info; + + // compose absolute filename to package config + strncpy(pkgInfoPath, info.dli_fname, pos); + + strcpy(pkgInfoPath + pos, pkgFileName); + pkgInfoPath[pos + pkgFileNameLen] = '\0'; - XRenderCreateRadialGradientFunc = - (XRenderCreateRadialGradientFuncType) - dlsym(xrenderlib, "XRenderCreateRadialGradient"); + // check whether the config file exist and is a regular file + if ((stat(pkgInfoPath, &stat_info)== 0) && + S_ISREG(stat_info.st_mode)) + { + FILE *fp = fopen(pkgInfoPath, "r"); + if (fp != NULL) { + char line[PKGINFO_LINE_LEN_MAX]; + int lineCount = PKGINFO_LINE_CNT_MAX; + char *versionPrefix = "Version: "; + size_t versionPrefixLen = strlen(versionPrefix); + + // look for version + while(fgets(line,sizeof(line),fp) != NULL && --lineCount > 0) { + size_t lineLen = strlen(line); + + if (lineLen > versionPrefixLen && + strncmp(versionPrefix, line, versionPrefixLen) == 0) + { + int v1 = 0, v2 = 0, v3 = 0; + int numNeeded = 3,numProcessed; + char* version = line + versionPrefixLen; + numProcessed = sscanf(version, "%d.%d.%d", &v1, &v2, &v3); + + if (numProcessed == numNeeded) { + // we successfuly read the library version + versionInfoIsFound = JNI_TRUE; - if (XRenderCreateLinearGradientFunc == NULL || - XRenderCreateRadialGradientFunc == NULL) - { - dlclose(xrenderlib); - return JNI_FALSE; + if (REQUIRED_XRENDER_VER1 == v1 && + ((REQUIRED_XRENDER_VER2 > v2) || + ((REQUIRED_XRENDER_VER2 == v2) && (REQUIRED_XRENDER_VER3 > v3)))) + { + available = JNI_FALSE; + + if (verbose) { + printf("INFO: the version %d.%d.%d of libXrender.so is " + "not supported.\n\tSee release notes for more details.\n", + v1, v2, v3); + fflush(stdout); + } + } else { + if (verbose) { + printf("INFO: The version of libXrender.so " + "is detected as %d.%d%d\n", v1, v2, v3); + fflush(stdout); + } + } + } + break; + } + } + fclose(fp); + } } + } + } + if (verbose && !versionInfoIsFound) { + printf("WARNING: The version of libXrender.so cannot be detected.\n," + "The pipe line will be enabled, but note that versions less than 0.9.3\n" + "may cause hangs and crashes\n" + "\tSee the release notes for more details.\n"); + fflush(stdout); } #endif - return JNI_TRUE; + + return available; } /* * Class: sun_awt_X11GraphicsEnvironment @@ -145,7 +244,7 @@ */ JNIEXPORT jboolean JNICALL Java_sun_awt_X11GraphicsEnvironment_initXRender - (JNIEnv *env, jclass x11ge) +(JNIEnv *env, jclass x11ge, jboolean verbose) { #ifndef HEADLESS static jboolean xrenderAvailable = JNI_FALSE; @@ -153,7 +252,7 @@ if (firstTime) { AWT_LOCK(); - xrenderAvailable = IsXRenderAvailable(); + xrenderAvailable = IsXRenderAvailable(verbose); AWT_UNLOCK(); firstTime = JNI_FALSE; } diff -r 49aef5a5416e -r 1f39ca0b9598 src/windows/native/sun/windows/awt_Component.cpp --- a/src/windows/native/sun/windows/awt_Component.cpp Sat Jun 04 06:45:17 2011 -0700 +++ b/src/windows/native/sun/windows/awt_Component.cpp Sat Jun 04 06:52:01 2011 -0700 @@ -3715,7 +3715,10 @@ SetCandidateWindow(iCandType, x-rc.left, y-rc.top); } if (m_bitsCandType != 0) { - ::DefWindowProc(GetHWnd(), WM_IME_NOTIFY, IMN_OPENCANDIDATE, m_bitsCandType); + HWND proxy = GetProxyFocusOwner(); + // REMIND: is there any chance GetProxyFocusOwner() returns NULL here? + ::DefWindowProc((proxy != NULL) ? proxy : GetHWnd(), + WM_IME_NOTIFY, IMN_OPENCANDIDATE, m_bitsCandType); } } diff -r 49aef5a5416e -r 1f39ca0b9598 src/windows/native/sun/windows/awt_Object.cpp --- a/src/windows/native/sun/windows/awt_Object.cpp Sat Jun 04 06:45:17 2011 -0700 +++ b/src/windows/native/sun/windows/awt_Object.cpp Sat Jun 04 06:52:01 2011 -0700 @@ -69,9 +69,12 @@ CriticalSection::Lock l(AwtToolkit::GetInstance().GetSyncCS()); + JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); + jobject selfGlobalRef = env->NewGlobalRef(self); + // value 0 of lParam means that we should not attempt to enter the // SyncCall critical section, as it was entered someshere earlier - AwtToolkit::GetInstance().SendMessage(WM_AWT_DISPOSE, (WPARAM)self, (LPARAM)0); + AwtToolkit::GetInstance().SendMessage(WM_AWT_DISPOSE, (WPARAM)selfGlobalRef, (LPARAM)0); CATCH_BAD_ALLOC; } diff -r 49aef5a5416e -r 1f39ca0b9598 src/windows/native/sun/windows/awt_Toolkit.cpp --- a/src/windows/native/sun/windows/awt_Toolkit.cpp Sat Jun 04 06:45:17 2011 -0700 +++ b/src/windows/native/sun/windows/awt_Toolkit.cpp Sat Jun 04 06:52:01 2011 -0700 @@ -741,7 +741,9 @@ } if (canDispose) { if(wParam != NULL) { - AwtObject *o = (AwtObject *) JNI_GET_PDATA((jobject)wParam); + jobject self = (jobject)wParam; + AwtObject *o = (AwtObject *) JNI_GET_PDATA(self); + env->DeleteGlobalRef(self); if(o != NULL && theAwtObjectList.Remove(o)) { o->Dispose(); } diff -r 49aef5a5416e -r 1f39ca0b9598 test/com/sun/net/httpserver/Test10.java --- a/test/com/sun/net/httpserver/Test10.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/com/sun/net/httpserver/Test10.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/List/ScrollOutside/ScrollOut.java --- a/test/java/awt/List/ScrollOutside/ScrollOut.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/List/ScrollOutside/ScrollOut.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion.java --- a/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_1.java --- a/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_1.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_1.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_2.java --- a/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_2.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_2.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_3.java --- a/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_3.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_3.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_4.java --- a/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_4.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_4.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2011, 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 diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/awt/image/IncorrectSampleMaskTest.java --- a/test/java/awt/image/IncorrectSampleMaskTest.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/awt/image/IncorrectSampleMaskTest.java Sat Jun 04 06:52:01 2011 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2009, 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 @@ -16,10 +16,10 @@ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ + * 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. +*/ /* * @test diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/6991596/Test6991596.java --- a/test/java/lang/invoke/6991596/Test6991596.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/lang/invoke/6991596/Test6991596.java Sat Jun 04 06:52:01 2011 -0700 @@ -51,10 +51,10 @@ return MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(ret, arg)); } static MethodHandle getmh2(MethodHandle mh1, Class ret, Class arg) { - return MethodHandles.convertArguments(mh1, MethodType.methodType(ret, arg)); + return MethodHandles.explicitCastArguments(mh1, MethodType.methodType(ret, arg)); } static MethodHandle getmh3(MethodHandle mh1, Class ret, Class arg) { - return MethodHandles.convertArguments(mh1, MethodType.methodType(ret, arg)); + return MethodHandles.explicitCastArguments(mh1, MethodType.methodType(ret, arg)); } // test adapter_opt_i2i diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/6998541/Test6998541.java --- a/test/java/lang/invoke/6998541/Test6998541.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/lang/invoke/6998541/Test6998541.java Sat Jun 04 06:52:01 2011 -0700 @@ -164,6 +164,7 @@ private static boolean canDoAsType(Class src, Class dst) { if (src == dst) return true; if (dst == void.class) return true; + if (src == void.class) return true; // allow void->zero if (!src.isPrimitive() || !dst.isPrimitive()) return true; // primitive conversion works for asType only when it's widening if (src == boolean.class || dst == boolean.class) return false; @@ -451,7 +452,6 @@ private final static MethodHandle mh_dv = mh(double.class ); private static void void2prim(int i) throws Throwable { - if (!DO_CASTS) return; assertEquals( false, (boolean) mh_zv.invokeExact()); // void -> boolean assertEquals((byte) 0, (byte) mh_bv.invokeExact()); // void -> byte assertEquals((char) 0, (char) mh_cv.invokeExact()); // void -> char @@ -463,15 +463,7 @@ } private static void void2prim_invalid(double x) throws Throwable { - if (DO_CASTS) return; - try { assertEquals( false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> boolean - try { assertEquals((byte) 0, (byte) mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> byte - try { assertEquals((char) 0, (char) mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> char - try { assertEquals((short) 0, (short) mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> short - try { assertEquals( 0, (int) mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> int - try { assertEquals( 0L, (long) mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> long - try { assertEquals( 0.0f, (float) mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> float - try { assertEquals( 0.0d, (double) mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> double + // no cases } private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); } diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/InvokeDynamicPrintArgs.java --- a/test/java/lang/invoke/InvokeDynamicPrintArgs.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/lang/invoke/InvokeDynamicPrintArgs.java Sat Jun 04 06:52:01 2011 -0700 @@ -106,8 +106,10 @@ "Done printing argument lists." }; + private static boolean doPrint = true; private static void printArgs(Object bsmInfo, Object... args) { - System.out.println(bsmInfo+Arrays.deepToString(args)); + String message = bsmInfo+Arrays.deepToString(args); + if (doPrint) System.out.println(message); } private static MethodHandle MH_printArgs() throws ReflectiveOperationException { shouldNotCallThis(); @@ -129,11 +131,48 @@ return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm()); } - private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException { + /* Example of a constant call site with user-data. + * In this case, the user data is exactly the BSM data. + * Note that a CCS with user data must use the "hooked" constructor + * to bind the CCS itself into the resulting target. + * A normal constructor would not allow a circular relation + * between the CCS and its target. + */ + public static class PrintingCallSite extends ConstantCallSite { + final Lookup caller; + final String name; + final Object[] staticArgs; + + PrintingCallSite(Lookup caller, String name, MethodType type, Object... staticArgs) throws Throwable { + super(type, MH_createTarget()); + this.caller = caller; + this.name = name; + this.staticArgs = staticArgs; + } + + public MethodHandle createTarget() { + try { + return lookup().bind(this, "runTarget", genericMethodType(0, true)).asType(type()); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + } + + public Object runTarget(Object... dynamicArgs) { + List bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type())); + bsmInfo.addAll(Arrays.asList(staticArgs)); + printArgs(bsmInfo, dynamicArgs); + return null; + } + + private static MethodHandle MH_createTarget() throws ReflectiveOperationException { + shouldNotCallThis(); + return lookup().findVirtual(lookup().lookupClass(), "createTarget", methodType(MethodHandle.class)); + } + } + private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws Throwable { // ignore caller and name, but match the type: - List bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type)); - bsmInfo.addAll(Arrays.asList((Object[])arg)); - return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type)); + return new PrintingCallSite(caller, name, type, arg); } private static MethodType MT_bsm2() { shouldNotCallThis(); @@ -146,33 +185,33 @@ private static MethodHandle INDY_nothing() throws Throwable { shouldNotCallThis(); - return ((CallSite) MH_bsm().invokeGeneric(lookup(), + return ((CallSite) MH_bsm().invoke(lookup(), "nothing", methodType(void.class) )).dynamicInvoker(); } private static MethodHandle INDY_foo() throws Throwable { shouldNotCallThis(); - return ((CallSite) MH_bsm().invokeGeneric(lookup(), + return ((CallSite) MH_bsm().invoke(lookup(), "foo", methodType(void.class, String.class) )).dynamicInvoker(); } private static MethodHandle INDY_bar() throws Throwable { shouldNotCallThis(); - return ((CallSite) MH_bsm2().invokeGeneric(lookup(), + return ((CallSite) MH_bsm2().invoke(lookup(), "bar", methodType(void.class, String.class, int.class) , Void.class, "void type!", 1, 234.5F, 67.5, (long)89 )).dynamicInvoker(); } private static MethodHandle INDY_bar2() throws Throwable { shouldNotCallThis(); - return ((CallSite) MH_bsm2().invokeGeneric(lookup(), + return ((CallSite) MH_bsm2().invoke(lookup(), "bar2", methodType(void.class, String.class, int.class) , Void.class, "void type!", 1, 234.5F, 67.5, (long)89 )).dynamicInvoker(); } private static MethodHandle INDY_baz() throws Throwable { shouldNotCallThis(); - return ((CallSite) MH_bsm2().invokeGeneric(lookup(), + return ((CallSite) MH_bsm2().invoke(lookup(), "baz", methodType(void.class, String.class, int.class, double.class) , 1234.5 )).dynamicInvoker(); diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/InvokeGenericTest.java --- a/test/java/lang/invoke/InvokeGenericTest.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/lang/invoke/InvokeGenericTest.java Sat Jun 04 06:52:01 2011 -0700 @@ -53,9 +53,9 @@ if (vstr != null) verbosity = Integer.parseInt(vstr); } - public static void main(String... av) throws Throwable { - new InvokeGenericTest().testFirst(); - } +// public static void main(String... av) throws Throwable { +// new InvokeGenericTest().testFirst(); +// } @Test public void testFirst() throws Throwable { @@ -314,7 +314,7 @@ ArrayList> argTypes = new ArrayList>(targetType.parameterList()); Collections.fill(argTypes.subList(beg, end), argType); MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes); - return MethodHandles.convertArguments(target, ttype2); + return target.asType(ttype2); } // This lookup is good for all members in and under InvokeGenericTest. @@ -378,7 +378,7 @@ String[] args = { "one", "two" }; MethodHandle mh = callable(Object.class, String.class); Object res; List resl; - res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]); + res = resl = (List) mh.invoke((String)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args), res); } @@ -470,8 +470,6 @@ return allMethodTypes(argc, argc, types); } - interface RandomInterface { } - MethodHandle toString_MH; @Test @@ -480,33 +478,62 @@ toString_MH = LOOKUP. findVirtual(Object.class, "toString", MethodType.methodType(String.class)); Object[] args = { "one", "two" }; - for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) { + for (MethodType type : allMethodTypes(2, Object.class, String.class, CharSequence.class)) { testReferenceConversions(type, args); } } public void testReferenceConversions(MethodType type, Object... args) throws Throwable { countTest(); - if (verbosity > 3) System.out.println("target type: "+type); + int nargs = args.length; + List argList = Arrays.asList(args); + String expectString = argList.toString(); + if (verbosity > 3) System.out.println("target type: "+type+expectString); MethodHandle mh = callable(type.parameterList()); - MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList()); - mh = MethodHandles.foldArguments(tsdrop, mh); + mh = MethodHandles.filterReturnValue(mh, toString_MH); mh = mh.asType(type); - Object res = mh.invoke((String)args[0], (Object)args[1]); + Object res = null; + if (nargs == 2) { + res = mh.invoke((Object)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = mh.invoke((String)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = mh.invoke((Object)args[0], (String)args[1]); + assertEquals(expectString, res); + res = mh.invoke((String)args[0], (String)args[1]); + assertEquals(expectString, res); + res = mh.invoke((String)args[0], (CharSequence)args[1]); + assertEquals(expectString, res); + res = mh.invoke((CharSequence)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = (String) mh.invoke((Object)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = (String) mh.invoke((String)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = (CharSequence) mh.invoke((String)args[0], (Object)args[1]); + assertEquals(expectString, res); + } else { + assert(false); // write this code + } //System.out.println(res); - assertEquals(Arrays.asList(args).toString(), res); } - @Test @Ignore("known failure pending 6939861") + @Test public void testBoxConversions() throws Throwable { startTest("testBoxConversions"); countTest(); Object[] args = { 1, 2 }; MethodHandle mh = callable(Object.class, int.class); - Object res; List resl; + Object res; List resl; int resi; res = resl = (List) mh.invoke((int)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args), res); + mh = MethodHandles.identity(int.class); + mh = MethodHandles.dropArguments(mh, 1, int.class); + res = resi = (int) mh.invoke((Object) args[0], (Object) args[1]); + assertEquals(args[0], res); + res = resi = (int) mh.invoke((int) args[0], (Object) args[1]); + assertEquals(args[0], res); } } diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/JavaDocExamplesTest.java --- a/test/java/lang/invoke/JavaDocExamplesTest.java Sat Jun 04 06:45:17 2011 -0700 +++ b/test/java/lang/invoke/JavaDocExamplesTest.java Sat Jun 04 06:52:01 2011 -0700 @@ -34,7 +34,7 @@ $ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \ $DAVINCI/sources/jdk/test/java/lang/invoke/JavaDocExamplesTest.java $ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \ - -Dtest.java.lang.invoke.JavaDocExamplesTest.verbosity=1 \ + -DJavaDocExamplesTest.verbosity=1 \ test.java.lang.invoke.JavaDocExamplesTest ---- */ @@ -45,12 +45,10 @@ import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; -import java.lang.reflect.*; import java.util.*; import org.junit.*; import static org.junit.Assert.*; -import static org.junit.Assume.*; /** @@ -60,11 +58,29 @@ /** Wrapper for running the JUnit tests in this module. * Put JUnit on the classpath! */ - public static void main(String... ignore) { - org.junit.runner.JUnitCore.runClasses(JavaDocExamplesTest.class); + public static void main(String... ignore) throws Throwable { + System.out.println("can run this as:"); + System.out.println("$ java org.junit.runner.JUnitCore "+JavaDocExamplesTest.class.getName()); + new JavaDocExamplesTest().run(); + } + public void run() throws Throwable { + testFindVirtual(); + testPermuteArguments(); + testDropArguments(); + testFilterArguments(); + testFoldArguments(); + testMethodHandlesSummary(); + testAsSpreader(); + testAsCollector(); + testAsVarargsCollector(); + testAsFixedArity(); + testAsTypeCornerCases(); + testMutableCallSite(); } // How much output? - static int verbosity = Integer.getInteger("test.java.lang.invoke.JavaDocExamplesTest.verbosity", 0); + static final Class THIS_CLASS = JavaDocExamplesTest.class; + static int verbosity = Integer.getInteger(THIS_CLASS.getSimpleName()+".verbosity", 0); + {} static final private Lookup LOOKUP = lookup(); @@ -74,17 +90,23 @@ // "hashCode", methodType(int.class)); // form required if ReflectiveOperationException is intercepted: -static final private MethodHandle CONCAT_2, HASHCODE_2; + static final private MethodHandle CONCAT_2, HASHCODE_2, ADD_2, SUB_2; static { try { + Class THIS_CLASS = LOOKUP.lookupClass(); CONCAT_2 = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class)); HASHCODE_2 = LOOKUP.findVirtual(Object.class, "hashCode", methodType(int.class)); + ADD_2 = LOOKUP.findStatic(THIS_CLASS, "add", methodType(int.class, int.class, int.class)); + SUB_2 = LOOKUP.findStatic(THIS_CLASS, "sub", methodType(int.class, int.class, int.class)); } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } + static int add(int x, int y) { return x + y; } + static int sub(int x, int y) { return x - y; } + {} @Test public void testFindVirtual() throws Throwable { @@ -101,6 +123,39 @@ assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy")); {} } + + @Test public void testPermuteArguments() throws Throwable { + {{ +{} /// JAVADOC +MethodType intfn1 = methodType(int.class, int.class); +MethodType intfn2 = methodType(int.class, int.class, int.class); +MethodHandle sub = SUB_2;// ... {int x, int y => x-y} ...; +assert(sub.type().equals(intfn2)); +MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1); +MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0); +assert((int)rsub.invokeExact(1, 100) == 99); +MethodHandle add = ADD_2;// ... {int x, int y => x+y} ...; +assert(add.type().equals(intfn2)); +MethodHandle twice = permuteArguments(add, intfn1, 0, 0); +assert(twice.type().equals(intfn1)); +assert((int)twice.invokeExact(21) == 42); + }} + {{ +{} /// JAVADOC +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +assertEquals("xy", (String) cat.invokeExact("x", "y")); +MethodHandle d0 = dropArguments(cat, 0, String.class); +assertEquals("yz", (String) d0.invokeExact("x", "y", "z")); +MethodHandle d1 = dropArguments(cat, 1, String.class); +assertEquals("xz", (String) d1.invokeExact("x", "y", "z")); +MethodHandle d2 = dropArguments(cat, 2, String.class); +assertEquals("xy", (String) d2.invokeExact("x", "y", "z")); +MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); +assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); + }} + } + @Test public void testDropArguments() throws Throwable { {{ {} /// JAVADOC @@ -145,6 +200,21 @@ }} } + @Test public void testFoldArguments() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class, + "println", methodType(void.class, String.class)) + .bindTo(System.out); +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); +MethodHandle catTrace = foldArguments(cat, trace); +// also prints "boo": +assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); + }} + } + static void assertEquals(Object exp, Object act) { if (verbosity > 0) System.out.println("result: "+act); @@ -162,24 +232,24 @@ mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("daddy",'d','n'); // invokeExact(Ljava/lang/String;CC)Ljava/lang/String; -assert(s.equals("nanny")); +assertEquals(s, "nanny"); // weakly typed invocation (using MHs.invoke) s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); -assert(s.equals("savvy")); +assertEquals(s, "savvy"); // mt is (Object[])List mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); x = mh.invoke("one", "two"); // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; -assert(x.equals(java.util.Arrays.asList("one","two"))); +assertEquals(x, java.util.Arrays.asList("one","two")); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); mh = mh.asType(mt); x = mh.invokeExact((Object)1, (Object)2, (Object)3); // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -assert(x.equals(java.util.Arrays.asList(1,2,3))); -// mt is { => int} +assertEquals(x, java.util.Arrays.asList(1,2,3)); +// mt is ()int mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); @@ -193,37 +263,239 @@ }} } + @Test public void testAsSpreader() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle equals = publicLookup() + .findVirtual(String.class, "equals", methodType(boolean.class, Object.class)); +assert( (boolean) equals.invokeExact("me", (Object)"me")); +assert(!(boolean) equals.invokeExact("me", (Object)"thee")); +// spread both arguments from a 2-array: +MethodHandle eq2 = equals.asSpreader(Object[].class, 2); +assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" })); +assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" })); +// spread both arguments from a String array: +MethodHandle eq2s = equals.asSpreader(String[].class, 2); +assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" })); +assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" })); +// spread second arguments from a 1-array: +MethodHandle eq1 = equals.asSpreader(Object[].class, 1); +assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" })); +assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" })); +// spread no arguments from a 0-array or null: +MethodHandle eq0 = equals.asSpreader(Object[].class, 0); +assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0])); +assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null)); +// asSpreader and asCollector are approximate inverses: +for (int n = 0; n <= 2; n++) { + for (Class a : new Class[]{Object[].class, String[].class, CharSequence[].class}) { + MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n); + assert( (boolean) equals2.invokeWithArguments("me", "me")); + assert(!(boolean) equals2.invokeWithArguments("me", "thee")); + } +} +MethodHandle caToString = publicLookup() + .findStatic(Arrays.class, "toString", methodType(String.class, char[].class)); +assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray())); +MethodHandle caString3 = caToString.asCollector(char[].class, 3); +assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C')); +MethodHandle caToString2 = caString3.asSpreader(char[].class, 2); +assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray())); + }} + } + + @Test public void testAsCollector() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle deepToString = publicLookup() + .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); +assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"})); +MethodHandle ts1 = deepToString.asCollector(Object[].class, 1); +assertEquals(methodType(String.class, Object.class), ts1.type()); +//assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL +assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"})); +// arrayType can be a subtype of Object[] +MethodHandle ts2 = deepToString.asCollector(String[].class, 2); +assertEquals(methodType(String.class, String.class, String.class), ts2.type()); +assertEquals("[two, too]", (String) ts2.invokeExact("two", "too")); +MethodHandle ts0 = deepToString.asCollector(Object[].class, 0); +assertEquals("[]", (String) ts0.invokeExact()); +// collectors can be nested, Lisp-style +MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2); +assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D"))); +// arrayType can be any primitive array type +MethodHandle bytesToString = publicLookup() + .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class)) + .asCollector(byte[].class, 3); +assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3)); +MethodHandle longsToString = publicLookup() + .findStatic(Arrays.class, "toString", methodType(String.class, long[].class)) + .asCollector(long[].class, 1); +assertEquals("[123]", (String) longsToString.invokeExact((long)123)); + }} + } + @Test public void testAsVarargsCollector() throws Throwable { {{ {} /// JAVADOC +MethodHandle deepToString = publicLookup() + .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); +MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class); +assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); +assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"})); +assertEquals("[won]", (String) ts1.invoke( "won" )); +assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"})); +// findStatic of Arrays.asList(...) produces a variable arity method handle: MethodHandle asList = publicLookup() - .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) - .asVarargsCollector(Object[].class); + .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); +assertEquals(methodType(List.class, Object[].class), asList.type()); +assert(asList.isVarargsCollector()); assertEquals("[]", asList.invoke().toString()); assertEquals("[1]", asList.invoke(1).toString()); assertEquals("[two, too]", asList.invoke("two", "too").toString()); -Object[] argv = { "three", "thee", "tee" }; +String[] argv = { "three", "thee", "tee" }; assertEquals("[three, thee, tee]", asList.invoke(argv).toString()); +assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString()); List ls = (List) asList.invoke((Object)argv); assertEquals(1, ls.size()); assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); }} } - @Test public void testVarargsCollectorSuppression() throws Throwable { + @Test public void testAsFixedArity() throws Throwable { {{ {} /// JAVADOC -MethodHandle vamh = publicLookup() +MethodHandle asListVar = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) .asVarargsCollector(Object[].class); -MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh); -assert(vamh.type().equals(mh.type())); -assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString()); -boolean failed = false; -try { mh.invoke(1,2,3); } -catch (WrongMethodTypeException ex) { failed = true; } -assert(failed); +MethodHandle asListFix = asListVar.asFixedArity(); +assertEquals("[1]", asListVar.invoke(1).toString()); +Exception caught = null; +try { asListFix.invoke((Object)1); } +catch (Exception ex) { caught = ex; } +assert(caught instanceof ClassCastException); +assertEquals("[two, too]", asListVar.invoke("two", "too").toString()); +try { asListFix.invoke("two", "too"); } +catch (Exception ex) { caught = ex; } +assert(caught instanceof WrongMethodTypeException); +Object[] argv = { "three", "thee", "tee" }; +assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString()); +assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString()); +assertEquals(1, ((List) asListVar.invoke((Object)argv)).size()); +assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); + }} + } + + @Test public void testAsTypeCornerCases() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle i2s = publicLookup() + .findVirtual(Integer.class, "toString", methodType(String.class)); +i2s = i2s.asType(i2s.type().unwrap()); +MethodHandle l2s = publicLookup() + .findVirtual(Long.class, "toString", methodType(String.class)); +l2s = l2s.asType(l2s.type().unwrap()); + +Exception caught = null; +try { i2s.asType(methodType(String.class, String.class)); } +catch (Exception ex) { caught = ex; } +assert(caught instanceof WrongMethodTypeException); + +i2s.asType(methodType(String.class, byte.class)); +i2s.asType(methodType(String.class, Byte.class)); +i2s.asType(methodType(String.class, Character.class)); +i2s.asType(methodType(String.class, Integer.class)); +l2s.asType(methodType(String.class, byte.class)); +l2s.asType(methodType(String.class, Byte.class)); +l2s.asType(methodType(String.class, Character.class)); +l2s.asType(methodType(String.class, Integer.class)); +l2s.asType(methodType(String.class, Long.class)); + +caught = null; +try { i2s.asType(methodType(String.class, Long.class)); } +catch (Exception ex) { caught = ex; } +assert(caught instanceof WrongMethodTypeException); + +MethodHandle i2sGen = i2s.asType(methodType(String.class, Object.class)); +MethodHandle l2sGen = l2s.asType(methodType(String.class, Object.class)); + +i2sGen.invoke(42); // int -> Integer -> Object -> Integer -> int +i2sGen.invoke((byte)4); // byte -> Byte -> Object -> Byte -> byte -> int +l2sGen.invoke(42); // int -> Integer -> Object -> Integer -> int +l2sGen.invoke((byte)4); // byte -> Byte -> Object -> Byte -> byte -> int +l2sGen.invoke(0x420000000L); + +caught = null; +try { i2sGen.invoke(0x420000000L); } // long -> Long -> Object -> Integer CCE +catch (Exception ex) { caught = ex; } +assert(caught instanceof ClassCastException); + +caught = null; +try { i2sGen.invoke("asdf"); } // String -> Object -> Integer CCE +catch (Exception ex) { caught = ex; } +assert(caught instanceof ClassCastException); {} }} } + + @Test public void testMutableCallSite() throws Throwable { + {{ +{} /// JAVADOC +MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class)); +MethodHandle MH_name = name.dynamicInvoker(); +MethodType MT_str1 = MethodType.methodType(String.class); +MethodHandle MH_upcase = MethodHandles.lookup() + .findVirtual(String.class, "toUpperCase", MT_str1); +MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase); +name.setTarget(MethodHandles.constant(String.class, "Rocky")); +assertEquals("ROCKY", (String) worker1.invokeExact()); +name.setTarget(MethodHandles.constant(String.class, "Fred")); +assertEquals("FRED", (String) worker1.invokeExact()); +// (mutation can be continued indefinitely) +/* + * + *

+ * The same call site may be used in several places at once. + *

+ */
+MethodType MT_str2 = MethodType.methodType(String.class, String.class);
+MethodHandle MH_cat = lookup().findVirtual(String.class,
+  "concat", methodType(String.class, String.class));
+MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
+MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
+assertEquals("Fred, dear?", (String) worker2.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Wilma"));
+assertEquals("WILMA", (String) worker1.invokeExact());
+assertEquals("Wilma, dear?", (String) worker2.invokeExact());
+{}
+            }}
+    }
+
+    @Test public void testSwitchPoint() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle MH_strcat = MethodHandles.lookup()
+    .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
+SwitchPoint spt = new SwitchPoint();
+assert(spt.isValid());
+// the following steps may be repeated to re-use the same switch point:
+MethodHandle worker1 = MH_strcat;
+MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
+MethodHandle worker = spt.guardWithTest(worker1, worker2);
+assertEquals("method", (String) worker.invokeExact("met", "hod"));
+SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
+assert(!spt.isValid());
+assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
+{}
+            }}
+    }
+
+    /* ---- TEMPLATE ----
+    @Test public void testFoo() throws Throwable {
+        {{
+{} /// JAVADOC
+{}
+            }}
+    }
+    */
 }
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/MethodHandlesTest.java
--- a/test/java/lang/invoke/MethodHandlesTest.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/lang/invoke/MethodHandlesTest.java	Sat Jun 04 06:52:01 2011 -0700
@@ -100,6 +100,31 @@
         // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
         testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
     }
+    @Test @Ignore("permuteArguments has trouble with double slots")
+    public void testFail_7() throws Throwable {
+        testPermuteArguments(new Object[]{10, 200L},
+                             new Class[]{Integer.class, long.class},
+                             new int[]{1,0});
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class[]{Integer.class, long.class, long.class},
+                             new int[]{2,0,1}); //rot
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class[]{Integer.class, long.class, long.class},
+                             new int[]{1,2,0}); //rot
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class[]{Integer.class, long.class, long.class},
+                             new int[]{2,1,0}); //swap
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class[]{Integer.class, long.class, long.class},
+                             new int[]{0,1,2,2}); //dup
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class[]{Integer.class, long.class, long.class},
+                             new int[]{2,0,1,2});
+        testPermuteArguments(new Object[]{10, 200L, 5000L},
+                             new Class[]{Integer.class, long.class, long.class},
+                             new int[]{2,2,0,1});
+        testPermuteArguments(4, Integer.class,  2, long.class,    6);
+    }
     static final int MAX_ARG_INCREASE = 3;
 
     public MethodHandlesTest() {
@@ -356,7 +381,7 @@
         ArrayList> argTypes = new ArrayList>(targetType.parameterList());
         Collections.fill(argTypes.subList(beg, end), argType);
         MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
-        return MethodHandles.convertArguments(target, ttype2);
+        return target.asType(ttype2);
     }
 
     // This lookup is good for all members in and under MethodHandlesTest.
@@ -505,8 +530,15 @@
             System.out.print(':');
     }
 
+    static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+
     // rough check of name string
-    static void assertNameStringContains(Object x, String s) {
+    static void assertNameStringContains(MethodHandle x, String s) {
+        if (!DEBUG_METHOD_HANDLE_NAMES) {
+            // ignore s
+            assertEquals("MethodHandle"+x.type(), x.toString());
+            return;
+        }
         if (x.toString().contains(s))  return;
         assertEquals(s, x);
     }
@@ -998,13 +1030,13 @@
         Class vtype = ftype;
         if (ftype != int.class)  vtype = Object.class;
         if (isGetter) {
-            mh = MethodHandles.convertArguments(mh, mh.type().generic()
-                                                .changeReturnType(vtype));
+            mh = mh.asType(mh.type().generic()
+                           .changeReturnType(vtype));
         } else {
             int last = mh.type().parameterCount() - 1;
-            mh = MethodHandles.convertArguments(mh, mh.type().generic()
-                                                .changeReturnType(void.class)
-                                                .changeParameterType(last, vtype));
+            mh = mh.asType(mh.type().generic()
+                           .changeReturnType(void.class)
+                           .changeParameterType(last, vtype));
         }
         if (f != null && f.getDeclaringClass() == HasFields.class) {
             assertEquals(f.get(fields), value);  // clean to start with
@@ -1132,7 +1164,7 @@
             // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
             MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
             if (testSetter)  gtype = gtype.changeReturnType(void.class);
-            mh = MethodHandles.convertArguments(mh, gtype);
+            mh = mh.asType(gtype);
         }
         Object sawValue, expValue;
         List model = array2list(array);
@@ -1226,11 +1258,10 @@
     }
 
     void testConvert(MethodHandle id, Class rtype, String name, Class... params) throws Throwable {
-        testConvert(true, false, id, rtype, name, params);
-        testConvert(true, true,  id, rtype, name, params);
+        testConvert(true, id, rtype, name, params);
     }
 
-    void testConvert(boolean positive, boolean useAsType,
+    void testConvert(boolean positive,
                      MethodHandle id, Class rtype, String name, Class... params) throws Throwable {
         countTest(positive);
         MethodType idType = id.type();
@@ -1258,10 +1289,7 @@
         MethodHandle target = null;
         RuntimeException error = null;
         try {
-            if (useAsType)
-                target = id.asType(newType);
-            else
-                target = MethodHandles.convertArguments(id, newType);
+            target = id.asType(newType);
         } catch (RuntimeException ex) {
             error = ex;
         }
@@ -1286,11 +1314,11 @@
                                MethodType.methodType(Object.class, String.class, Object[].class));
         vac0 = vac0.bindTo("vac");
         MethodHandle vac = vac0.asVarargsCollector(Object[].class);
-        testConvert(true,  true,  vac.asType(MethodType.genericMethodType(0)), null, "vac");
-        testConvert(true,  true,  vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
         for (Class at : new Class[] { Object.class, String.class, Integer.class }) {
-            testConvert(true,  true,  vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
-            testConvert(true,  true,  vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
+            testConvert(true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
+            testConvert(true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
         }
     }
 
@@ -1299,8 +1327,8 @@
         if (CAN_SKIP_WORKING)  return;
         startTest("permuteArguments");
         testPermuteArguments(4, Integer.class,  2, String.class,  0);
-        //testPermuteArguments(6, Integer.class,  0, null,         30);
-        //testPermuteArguments(4, Integer.class,  1, int.class,     6);
+        testPermuteArguments(6, Integer.class,  0, null,         30);
+        //testPermuteArguments(4, Integer.class,  2, long.class,    6);  // FIXME Fail_7
     }
     public void testPermuteArguments(int max, Class type1, int t2c, Class type2, int dilution) throws Throwable {
         if (verbosity >= 2)
@@ -1414,8 +1442,9 @@
         }
         MethodType inType  = MethodType.methodType(Object.class, types);
         MethodType outType = MethodType.methodType(Object.class, permTypes);
-        MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType);
+        MethodHandle target = varargsList(outargs).asType(outType);
         MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
+        if (verbosity >= 5)  System.out.println("newTarget = "+newTarget);
         Object result = newTarget.invokeWithArguments(args);
         Object expected = Arrays.asList(permArgs);
         if (!expected.equals(result)) {
@@ -1659,7 +1688,7 @@
         countTest();
         MethodHandle target = varargsList(nargs);
         MethodHandle filter = varargsList(1);
-        filter = MethodHandles.convertArguments(filter, filter.type().generic());
+        filter = filter.asType(filter.type().generic());
         Object[] argsToPass = randomArgs(nargs, Object.class);
         if (verbosity >= 3)
             System.out.println("filter "+target+" at "+pos+" with "+filter);
@@ -1800,7 +1829,7 @@
         // generic invoker
         countTest();
         inv = MethodHandles.invoker(type);
-        if (nargs <= 3) {
+        if (nargs <= 3 && type == type.generic()) {
             calledLog.clear();
             switch (nargs) {
             case 0:
@@ -1826,10 +1855,16 @@
         // varargs invoker #0
         calledLog.clear();
         inv = MethodHandles.spreadInvoker(type, 0);
-        result = inv.invokeExact(target, args);
+        if (type.returnType() == Object.class) {
+            result = inv.invokeExact(target, args);
+        } else if (type.returnType() == void.class) {
+            result = null; inv.invokeExact(target, args);
+        } else {
+            result = inv.invokeWithArguments(target, (Object) args);
+        }
         if (testRetCode)  assertEquals(code, result);
         assertCalled("invokee", args);
-        if (nargs >= 1) {
+        if (nargs >= 1 && type == type.generic()) {
             // varargs invoker #1
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, 1);
@@ -1837,7 +1872,7 @@
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
-        if (nargs >= 2) {
+        if (nargs >= 2 && type == type.generic()) {
             // varargs invoker #2
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, 2);
@@ -1845,7 +1880,7 @@
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
         }
-        if (nargs >= 3) {
+        if (nargs >= 3 && type == type.generic()) {
             // varargs invoker #3
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, 3);
@@ -1858,6 +1893,10 @@
             countTest();
             calledLog.clear();
             inv = MethodHandles.spreadInvoker(type, k);
+            MethodType expType = (type.dropParameterTypes(k, nargs)
+                                  .appendParameterTypes(Object[].class)
+                                  .insertParameterTypes(0, MethodHandle.class));
+            assertEquals(expType, inv.type());
             List targetPlusVarArgs = new ArrayList(targetPlusArgs);
             List tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
             Object[] tail = tailList.toArray();
@@ -2038,7 +2077,7 @@
         //System.out.println("throwing with "+target+" : "+thrown);
         MethodType expectedType = MethodType.methodType(returnType, exType);
         assertEquals(expectedType, target.type());
-        target = MethodHandles.convertArguments(target, target.type().generic());
+        target = target.asType(target.type().generic());
         Throwable caught = null;
         try {
             Object res = target.invokeExact((Object) thrown);
@@ -2110,12 +2149,12 @@
         if (mode.endsWith("/return")) {
             if (mode.equals("unbox/return")) {
                 // fail on return to ((Integer)surprise).intValue
-                surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(int.class, Object.class));
-                identity = MethodHandles.convertArguments(identity, MethodType.methodType(int.class, Object.class));
+                surprise = surprise.asType(MethodType.methodType(int.class, Object.class));
+                identity = identity.asType(MethodType.methodType(int.class, Object.class));
             } else if (mode.equals("cast/return")) {
                 // fail on return to (Integer)surprise
-                surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(Integer.class, Object.class));
-                identity = MethodHandles.convertArguments(identity, MethodType.methodType(Integer.class, Object.class));
+                surprise = surprise.asType(MethodType.methodType(Integer.class, Object.class));
+                identity = identity.asType(MethodType.methodType(Integer.class, Object.class));
             }
         } else if (mode.endsWith("/argument")) {
             MethodHandle callee = null;
@@ -2127,14 +2166,14 @@
                 callee = Surprise.BOX_IDENTITY;
             }
             if (callee != null) {
-                callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1));
+                callee = callee.asType(MethodType.genericMethodType(1));
                 surprise = MethodHandles.filterArguments(callee, 0, surprise);
                 identity = MethodHandles.filterArguments(callee, 0, identity);
             }
         }
         assertNotSame(mode, surprise, surprise0);
-        identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1));
-        surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1));
+        identity = identity.asType(MethodType.genericMethodType(1));
+        surprise = surprise.asType(MethodType.genericMethodType(1));
         Object x = 42;
         for (int i = 0; i < okCount; i++) {
             Object y = identity.invokeExact(x);
@@ -2223,14 +2262,14 @@
         {
             MethodType mt = MethodType.methodType(void.class);
             MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
-            Runnable proxy = MethodHandles.asInstance(mh, Runnable.class);
+            Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh);
             proxy.run();
             assertCalled("runForRunnable");
         }
         {
             MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class);
             MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt);
-            Fooable proxy = MethodHandles.asInstance(mh, Fooable.class);
+            Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh);
             Object[] args = randomArgs(mt.parameterArray());
             Object result = proxy.foo((Fooable) args[0], args[1]);
             assertCalled("fooForFooable", args);
@@ -2244,7 +2283,7 @@
                                             }) {
             MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
             mh = MethodHandles.insertArguments(mh, 0, ex);
-            WillThrow proxy = MethodHandles.asInstance(mh, WillThrow.class);
+            WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh);
             try {
                 proxy.willThrow();
                 System.out.println("Failed to throw: "+ex);
@@ -2272,7 +2311,7 @@
                                              CharSequence.class,
                                              Example.class }) {
             try {
-                MethodHandles.asInstance(varargsArray(0), nonSAM);
+                MethodHandleProxies.asInterfaceInstance(nonSAM, varargsArray(0));
                 System.out.println("Failed to throw");
                 assertTrue(false);
             } catch (IllegalArgumentException ex) {
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/MethodTypeTest.java
--- a/test/java/lang/invoke/MethodTypeTest.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/lang/invoke/MethodTypeTest.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,12 +1,10 @@
 /*
- * Copyright 2008, 2011 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright (c) 2008, 2011, 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.  Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
+ * published by the Free Software Foundation.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -18,9 +16,9 @@
  * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
+ * 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.
  */
 
 /* @test
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/lang/invoke/indify/Indify.java
--- a/test/java/lang/invoke/indify/Indify.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/lang/invoke/indify/Indify.java	Sat Jun 04 06:52:01 2011 -0700
@@ -524,6 +524,8 @@
             if (verifySpecifierCount >= 0) {
                 List specs = bootstrapMethodSpecifiers(false);
                 int specsLen = (specs == null ? 0 : specs.size());
+                // Pass by specsLen == 0, to help with associated (inner) classes.
+                if (specsLen == 0)  specsLen = verifySpecifierCount;
                 if (specsLen != verifySpecifierCount) {
                     throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
                 }
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/rmi/server/UnicastRemoteObject/exportObject/GcDuringExport.java
--- a/test/java/rmi/server/UnicastRemoteObject/exportObject/GcDuringExport.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/rmi/server/UnicastRemoteObject/exportObject/GcDuringExport.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/EnumMap/DistinctEntrySetElements.java
--- a/test/java/util/EnumMap/DistinctEntrySetElements.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/EnumMap/DistinctEntrySetElements.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java
--- a/test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/EnumMap/SimpleSerialization.java
--- a/test/java/util/EnumMap/SimpleSerialization.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/EnumMap/SimpleSerialization.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/EnumSet/LargeEnumIteratorRemoveResilience.java
--- a/test/java/util/EnumSet/LargeEnumIteratorRemoveResilience.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/EnumSet/LargeEnumIteratorRemoveResilience.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/EnumSet/SmallEnumIteratorRemoveResilience.java
--- a/test/java/util/EnumSet/SmallEnumIteratorRemoveResilience.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/EnumSet/SmallEnumIteratorRemoveResilience.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/Hashtable/SerializationDeadlock.java
--- a/test/java/util/Hashtable/SerializationDeadlock.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/Hashtable/SerializationDeadlock.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/Hashtable/SimpleSerialization.java
--- a/test/java/util/Hashtable/SimpleSerialization.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/Hashtable/SimpleSerialization.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/IdentityHashMap/DistinctEntrySetElements.java
--- a/test/java/util/IdentityHashMap/DistinctEntrySetElements.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/IdentityHashMap/DistinctEntrySetElements.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java
--- a/test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/Vector/SerializationDeadlock.java
--- a/test/java/util/Vector/SerializationDeadlock.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/Vector/SerializationDeadlock.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/Vector/SimpleSerialization.java
--- a/test/java/util/Vector/SimpleSerialization.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/Vector/SimpleSerialization.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java
--- a/test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/java/util/zip/ZipFile/ClearStaleZipFileInputStreams.java
--- a/test/java/util/zip/ZipFile/ClearStaleZipFileInputStreams.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/java/util/zip/ZipFile/ClearStaleZipFileInputStreams.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/javax/swing/plaf/nimbus/Test7048204.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/plaf/nimbus/Test7048204.java	Sat Jun 04 06:52:01 2011 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ * 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.
+ */
+
+/* @test
+   @bug 7048204
+   @summary NPE from NimbusLookAndFeel.addDefault
+   @author Pavel Porvatov
+*/
+
+import javax.swing.*;
+import javax.swing.plaf.nimbus.NimbusLookAndFeel;
+
+public class Test7048204 {
+    public static void main(String[] args) throws Exception {
+        UIManager.setLookAndFeel(new NimbusLookAndFeel());
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                new JLabel();
+
+                UIDefaults uid = UIManager.getDefaults();
+                uid.putDefaults(new Object[0]);
+                uid.put("what.ever", "else");
+            }
+        });
+    }
+}
diff -r 49aef5a5416e -r 1f39ca0b9598 test/javax/swing/text/Utilities/bug7045593.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/text/Utilities/bug7045593.java	Sat Jun 04 06:52:01 2011 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 7045593
+ * @summary Possible Regression : JTextfield cursor placement behavior algorithm has changed
+ * @author Pavel Porvatov
+ */
+
+import sun.awt.SunToolkit;
+
+import javax.swing.*;
+import javax.swing.text.BadLocationException;
+import java.awt.*;
+
+public class bug7045593 {
+    private static volatile JTextField jtf;
+
+    public static void main(String[] args) throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                jtf = new JTextField("WW");
+
+                JFrame frame = new JFrame();
+                frame.getContentPane().add(jtf);
+                frame.pack();
+                frame.setVisible(true);
+            }
+        });
+
+        ((SunToolkit) SunToolkit.getDefaultToolkit()).realSync();
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                try {
+                    Rectangle r = jtf.modelToView(1);
+
+                    int delta = 2;
+
+                    for (int x = r.x - delta; x < r.x + delta; x++) {
+                        assertEquals(jtf.viewToModel(new Point(x, r.y)), 1);
+                    }
+
+                    System.out.println("Passed.");
+                } catch (BadLocationException e) {
+                    throw new RuntimeException("Test failed", e);
+                }
+            }
+        });
+    }
+
+    private static void assertEquals(int i1, int i2) {
+        if (i1 != i2) {
+            throw new RuntimeException("Test failed, " + i1 + " != " + i2);
+        }
+    }
+}
diff -r 49aef5a5416e -r 1f39ca0b9598 test/sun/net/InetAddress/nameservice/chaining/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
--- a/test/sun/net/InetAddress/nameservice/chaining/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/sun/net/InetAddress/nameservice/chaining/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor	Sat Jun 04 06:52:01 2011 -0700
@@ -17,7 +17,7 @@
 #
 # 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
+# questions.
 
 Simple1NameServiceDescriptor
 Simple2NameServiceDescriptor
diff -r 49aef5a5416e -r 1f39ca0b9598 test/tools/launcher/TestHelper.java
--- a/test/tools/launcher/TestHelper.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/tools/launcher/TestHelper.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/tools/pack200/CommandLineTests.java
--- a/test/tools/pack200/CommandLineTests.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/tools/pack200/CommandLineTests.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2010 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2010, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/tools/pack200/Pack200Test.java
--- a/test/tools/pack200/Pack200Test.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/tools/pack200/Pack200Test.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2010 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2010, 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
diff -r 49aef5a5416e -r 1f39ca0b9598 test/tools/pack200/Utils.java
--- a/test/tools/pack200/Utils.java	Sat Jun 04 06:45:17 2011 -0700
+++ b/test/tools/pack200/Utils.java	Sat Jun 04 06:52:01 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2010 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2010, 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