diff --git a/.hgtags b/.hgtags index ebd78b314598934b2c33eb42e62246f65e619750..78d56961133618b6e9b86d0668ecf543d14a77c6 100644 --- a/.hgtags +++ b/.hgtags @@ -117,3 +117,4 @@ d80954a89b49fda47c0c5cace65a17f5a758b8bd jdk7-b139 9315c733fb17ddfb9fb44be7e0ffea37bf3c727d jdk7-b140 63eeefe118da18c75ba3d36266768cd1ccaaca6b jdk7-b141 312612e89ece62633f4809706dec00bcd5fe7c2d jdk7-b142 +efbf75c24b0f31847c9c403f6dc07dc80551908d jdk7-b143 diff --git a/make/sun/rmi/rmi/Makefile b/make/sun/rmi/rmi/Makefile index 35e4163c5a8c46b95b74aedd6556356037566a34..127777229e42a8338f6cda4836e43432b5625f77 100644 --- a/make/sun/rmi/rmi/Makefile +++ b/make/sun/rmi/rmi/Makefile @@ -85,16 +85,21 @@ REMOTE_impls = \ sun.rmi.registry.RegistryImpl \ sun.rmi.transport.DGCImpl -ifeq ($(PLATFORM), windows) -build: stubs -else # PLATFORM -ifneq ($(ARCH_DATA_MODEL), 32) -build: stubs -else # ARCH_DATA_MODEL -build: stubs bin +# +# The java-rmi.cgi script in bin/ only gets delivered in certain situations +# +BUILD_TARGETS = stubs +ifeq ($(PLATFORM), linux) + BUILD_TARGETS += bin endif +ifeq ($(PLATFORM), solaris) + ifeq ($(ARCH_DATA_MODEL), 32) + BUILD_TARGETS += bin + endif endif +build: $(BUILD_TARGETS) + clean clobber:: bin.clean diff --git a/make/sun/xawt/mapfile-vers b/make/sun/xawt/mapfile-vers index 096bfe553264856292e9587fc67e9bb6b81f6398..8a12b69c31494a3c55a960a83f52e49c5371ab16 100644 --- a/make/sun/xawt/mapfile-vers +++ b/make/sun/xawt/mapfile-vers @@ -158,7 +158,6 @@ SUNWprivate_1.1 { Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl; Java_sun_awt_X11_XRobotPeer_mouseWheelImpl; Java_sun_awt_X11_XRobotPeer_setup; - Java_sun_awt_X11_XRobotPeer__1dispose; Java_sun_awt_X11_XToolkit_getNumberOfButtonsImpl; Java_java_awt_Component_initIDs; Java_java_awt_Container_initIDs; diff --git a/src/share/classes/java/awt/Component.java b/src/share/classes/java/awt/Component.java index f577887abe67c4caf7831dd385c71e25909a3427..2225633c0db5d6783b57663ae335b14fb7ed52ac 100644 --- a/src/share/classes/java/awt/Component.java +++ b/src/share/classes/java/awt/Component.java @@ -2887,11 +2887,12 @@ public abstract class Component implements ImageObserver, MenuContainer, /** * Invalidates this component and its ancestors. *

- * All the ancestors of this component up to the nearest validate root are - * marked invalid also. If there is no a validate root container for this - * component, all of its ancestors up to the root of the hierarchy are - * marked invalid as well. Marking a container invalid indicates - * that the container needs to be laid out. + * By default, all the ancestors of the component up to the top-most + * container of the hierarchy are marked invalid. If the {@code + * java.awt.smartInvalidate} system property is set to {@code true}, + * invalidation stops on the nearest validate root of this component. + * Marking a container invalid indicates that the container needs to + * be laid out. *

* This method is called automatically when any layout-related information * changes (e.g. setting the bounds of the component, or adding the diff --git a/src/share/classes/java/awt/Container.java b/src/share/classes/java/awt/Container.java index 88f0865b187128a72ef555892d04799f5189e423..c59aa90bc5f238f14ee02d9e7267366e5f4831dc 100644 --- a/src/share/classes/java/awt/Container.java +++ b/src/share/classes/java/awt/Container.java @@ -41,6 +41,8 @@ import java.io.ObjectStreamField; import java.io.PrintStream; import java.io.PrintWriter; +import java.security.AccessController; + import java.util.Arrays; import java.util.EventListener; import java.util.HashSet; @@ -60,6 +62,8 @@ import sun.awt.dnd.SunDropTargetEvent; import sun.java2d.pipe.Region; +import sun.security.action.GetBooleanAction; + /** * A generic Abstract Window Toolkit(AWT) container object is a component * that can contain other AWT components. @@ -1506,12 +1510,18 @@ public class Container extends Component { * Layout-related changes, such as bounds of the validate root descendants, * do not affect the layout of the validate root parent. This peculiarity * enables the {@code invalidate()} method to stop invalidating the - * component hierarchy when the method encounters a validate root. + * component hierarchy when the method encounters a validate root. However, + * to preserve backward compatibility this new optimized behavior is + * enabled only when the {@code java.awt.smartInvalidate} system property + * value is set to {@code true}. *

- * If a component hierarchy contains validate roots, the {@code validate()} - * method must be invoked on the validate root of a previously invalidated - * component, rather than on the top-level container (such as a {@code - * Frame} object) to restore the validity of the hierarchy later. + * If a component hierarchy contains validate roots and the new optimized + * {@code invalidate()} behavior is enabled, the {@code validate()} method + * must be invoked on the validate root of a previously invalidated + * component to restore the validity of the hierarchy later. Otherwise, + * calling the {@code validate()} method on the top-level container (such + * as a {@code Frame} object) should be used to restore the validity of the + * component hierarchy. *

* The {@code Window} class and the {@code Applet} class are the validate * roots in AWT. Swing introduces more validate roots. @@ -1527,13 +1537,20 @@ public class Container extends Component { return false; } + private static final boolean isJavaAwtSmartInvalidate; + static { + // Don't lazy-read because every app uses invalidate() + isJavaAwtSmartInvalidate = AccessController.doPrivileged( + new GetBooleanAction("java.awt.smartInvalidate")); + } + /** * Invalidates the parent of the container unless the container * is a validate root. */ @Override void invalidateParent() { - if (!isValidateRoot()) { + if (!isJavaAwtSmartInvalidate || !isValidateRoot()) { super.invalidateParent(); } } @@ -1572,9 +1589,8 @@ public class Container extends Component { * automatically. Note that the ancestors of the container may be * invalidated also (see {@link Component#invalidate} for details.) * Therefore, to restore the validity of the hierarchy, the {@code - * validate()} method should be invoked on a validate root of an - * invalidated component, or on the top-most container if the hierarchy - * does not contain validate roots. + * validate()} method should be invoked on the top-most invalid + * container of the hierarchy. *

* Validating the container may be a quite time-consuming operation. For * performance reasons a developer may postpone the validation of the diff --git a/src/share/classes/java/awt/Toolkit.java b/src/share/classes/java/awt/Toolkit.java index 6e00e6afdbf260aba46d82a34b09e98b0be903f1..3ac2714dcef892da3bb4d97a96f61fa2123715bb 100644 --- a/src/share/classes/java/awt/Toolkit.java +++ b/src/share/classes/java/awt/Toolkit.java @@ -466,10 +466,7 @@ public abstract class Toolkit { */ protected void loadSystemColors(int[] systemColors) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } - + GraphicsEnvironment.checkHeadless(); } /** @@ -504,10 +501,7 @@ public abstract class Toolkit { */ public void setDynamicLayout(boolean dynamic) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } - + GraphicsEnvironment.checkHeadless(); } /** @@ -531,9 +525,8 @@ public abstract class Toolkit { */ protected boolean isDynamicLayoutSet() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().isDynamicLayoutSet(); } else { @@ -569,9 +562,8 @@ public abstract class Toolkit { */ public boolean isDynamicLayoutActive() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().isDynamicLayoutActive(); } else { @@ -615,9 +607,7 @@ public abstract class Toolkit { */ public Insets getScreenInsets(GraphicsConfiguration gc) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().getScreenInsets(gc); } else { @@ -1200,10 +1190,7 @@ public abstract class Toolkit { * security manager's checkPermission method with a * RuntimePermission("queuePrintJob") permission. * - * @param frame the parent of the print dialog. May be null if and only - * if jobAttributes is not null and jobAttributes.getDialog() - * returns JobAttributes.DialogType.NONE or - * JobAttributes.DialogType.COMMON. + * @param frame the parent of the print dialog. May not be null. * @param jobtitle the title of the PrintJob. A null title is equivalent * to "". * @param jobAttributes a set of job attributes which will control the @@ -1359,9 +1346,8 @@ public abstract class Toolkit { * @since 1.4 */ public Clipboard getSystemSelection() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().getSystemSelection(); } else { @@ -1391,9 +1377,7 @@ public abstract class Toolkit { * @since JDK1.1 */ public int getMenuShortcutKeyMask() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); return Event.CTRL_MASK; } @@ -1418,7 +1402,10 @@ public abstract class Toolkit { * @since 1.3 */ public boolean getLockingKeyState(int keyCode) - throws UnsupportedOperationException { + throws UnsupportedOperationException + { + GraphicsEnvironment.checkHeadless(); + if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) { throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState"); @@ -1449,7 +1436,10 @@ public abstract class Toolkit { * @since 1.3 */ public void setLockingKeyState(int keyCode, boolean on) - throws UnsupportedOperationException { + throws UnsupportedOperationException + { + GraphicsEnvironment.checkHeadless(); + if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) { throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState"); @@ -1523,9 +1513,8 @@ public abstract class Toolkit { */ public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + // Override to implement custom cursor support. if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit(). @@ -1553,9 +1542,8 @@ public abstract class Toolkit { * @since 1.2 */ public int getMaximumCursorColors() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + // Override to implement custom cursor support. if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit().getMaximumCursorColors(); @@ -1605,9 +1593,8 @@ public abstract class Toolkit { public boolean isFrameStateSupported(int state) throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + if (this != Toolkit.getDefaultToolkit()) { return Toolkit.getDefaultToolkit(). isFrameStateSupported(state); @@ -2614,9 +2601,8 @@ public abstract class Toolkit { * @since 1.7 */ public boolean areExtraMouseButtonsEnabled() throws HeadlessException { - if (GraphicsEnvironment.isHeadless()){ - throw new HeadlessException(); - } + GraphicsEnvironment.checkHeadless(); + return Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled(); } } diff --git a/src/share/classes/java/lang/Throwable.java b/src/share/classes/java/lang/Throwable.java index a42306f41ae162407e4125aa53afe4d9baf06a6d..2319aafd00fa2fab3f70b889a2f879da451f473b 100644 --- a/src/share/classes/java/lang/Throwable.java +++ b/src/share/classes/java/lang/Throwable.java @@ -777,7 +777,8 @@ public class Throwable implements Serializable { * @see java.lang.Throwable#printStackTrace() */ public synchronized Throwable fillInStackTrace() { - if (stackTrace != null) { + if (stackTrace != null || + backtrace != null /* Out of protocol state */ ) { fillInStackTrace(0); stackTrace = UNASSIGNED_STACK; } @@ -817,7 +818,8 @@ public class Throwable implements Serializable { private synchronized StackTraceElement[] getOurStackTrace() { // Initialize stack trace field with information from // backtrace if this is the first call to this method - if (stackTrace == UNASSIGNED_STACK) { + if (stackTrace == UNASSIGNED_STACK || + (stackTrace == null && backtrace != null) /* Out of protocol state */) { int depth = getStackTraceDepth(); stackTrace = new StackTraceElement[depth]; for (int i=0; i < depth; i++) @@ -865,7 +867,8 @@ public class Throwable implements Serializable { } synchronized (this) { - if (this.stackTrace == null) // Immutable stack + if (this.stackTrace == null && // Immutable stack + backtrace == null) // Test for out of protocol state return; this.stackTrace = defensiveCopy; } diff --git a/src/share/classes/java/lang/invoke/AdapterMethodHandle.java b/src/share/classes/java/lang/invoke/AdapterMethodHandle.java index a47b38f8bb3ec50a791ea014e37896bda8117878..a77c904eb0e94bb3b41967ecaf32171d7beac85a 100644 --- a/src/share/classes/java/lang/invoke/AdapterMethodHandle.java +++ b/src/share/classes/java/lang/invoke/AdapterMethodHandle.java @@ -27,6 +27,7 @@ package java.lang.invoke; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; +import sun.invoke.util.ValueConversions; import java.util.Arrays; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; @@ -55,29 +56,35 @@ class AdapterMethodHandle extends BoundMethodHandle { this(target, newType, conv, null); } + int getConversion() { return conversion; } + // TO DO: When adapting another MH with a null conversion, clone // 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? */ - static boolean canPairwiseConvert(MethodType newType, MethodType oldType) { + static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) { // same number of args, of course int len = newType.parameterCount(); if (len != oldType.parameterCount()) return false; - // Check return type. (Not much can be done with it.) + // Check return type. Class exp = newType.returnType(); Class ret = oldType.returnType(); - if (!VerifyType.isNullConversion(ret, exp)) - return false; + if (!VerifyType.isNullConversion(ret, exp)) { + if (!convOpSupported(OP_COLLECT_ARGS)) + return false; + if (!canConvertArgument(ret, exp, level)) + return false; + } // Check args pairwise. for (int i = 0; i < len; i++) { Class src = newType.parameterType(i); // source type Class dst = oldType.parameterType(i); // destination type - if (!canConvertArgument(src, dst)) + if (!canConvertArgument(src, dst, level)) return false; } @@ -87,11 +94,14 @@ class AdapterMethodHandle extends BoundMethodHandle { /** Can a JVM-level adapter directly implement the proposed * argument conversion, as if by MethodHandles.convertArguments? */ - static boolean canConvertArgument(Class src, Class dst) { + static boolean canConvertArgument(Class src, Class dst, int level) { // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes, // so we don't need to repeat so much decision making. if (VerifyType.isNullConversion(src, dst)) { return true; + } else if (convOpSupported(OP_COLLECT_ARGS)) { + // If we can build filters, we can convert anything to anything. + return true; } else if (src.isPrimitive()) { if (dst.isPrimitive()) return canPrimCast(src, dst); @@ -99,7 +109,7 @@ class AdapterMethodHandle extends BoundMethodHandle { return canBoxArgument(src, dst); } else { if (dst.isPrimitive()) - return canUnboxArgument(src, dst); + return canUnboxArgument(src, dst, level); else return true; // any two refs can be interconverted } @@ -109,21 +119,20 @@ class AdapterMethodHandle extends BoundMethodHandle { * Create a JVM-level adapter method handle to conform the given method * handle to the similar newType, using only pairwise argument conversions. * For each argument, convert incoming argument to the exact type needed. - * Only null conversions are allowed on the return value (until - * the JVM supports ricochet adapters). - * The argument conversions allowed are casting, unboxing, + * The argument conversions allowed are casting, boxing and unboxing, * integral widening or narrowing, and floating point widening or narrowing. * @param newType required call type * @param target original method handle + * @param level which strength of conversion is allowed * @return an adapter to the original handle with the desired new type, * or the original target if the types are already identical * or null if the adaptation cannot be made */ - static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target) { + static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) { MethodType oldType = target.type(); if (newType == oldType) return target; - if (!canPairwiseConvert(newType, oldType)) + if (!canPairwiseConvert(newType, oldType, level)) return null; // (after this point, it is an assertion error to fail to convert) @@ -138,9 +147,14 @@ class AdapterMethodHandle extends BoundMethodHandle { break; } } + + Class needReturn = newType.returnType(); + Class haveReturn = oldType.returnType(); + boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn); + // Now build a chain of one or more adapters. - MethodHandle adapter = target; - MethodType midType = oldType.changeReturnType(newType.returnType()); + MethodHandle adapter = target, adapter2; + MethodType midType = oldType; for (int i = 0; i <= lastConv; i++) { Class src = newType.parameterType(i); // source type Class dst = midType.parameterType(i); // destination type @@ -149,22 +163,23 @@ class AdapterMethodHandle extends BoundMethodHandle { continue; } // Work the current type backward toward the desired caller type: - if (i != lastConv) { - midType = midType.changeParameterType(i, src); - } else { + midType = midType.changeParameterType(i, src); + if (i == lastConv) { // When doing the last (or only) real conversion, // force all remaining null conversions to happen also. - assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src))); - midType = newType; + MethodType lastMidType = newType; + if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn); + assert(VerifyType.isNullConversion(lastMidType, midType)); + midType = lastMidType; } // Tricky case analysis follows. // It parallels canConvertArgument() above. if (src.isPrimitive()) { if (dst.isPrimitive()) { - adapter = makePrimCast(midType, adapter, i, dst); + adapter2 = makePrimCast(midType, adapter, i, dst); } else { - adapter = makeBoxArgument(midType, adapter, i, dst); + adapter2 = makeBoxArgument(midType, adapter, i, src); } } else { if (dst.isPrimitive()) { @@ -174,29 +189,53 @@ class AdapterMethodHandle extends BoundMethodHandle { // conversions supported by reflect.Method.invoke. // Those conversions require a big nest of if/then/else logic, // which we prefer to make a user responsibility. - adapter = makeUnboxArgument(midType, adapter, i, dst); + adapter2 = makeUnboxArgument(midType, adapter, i, dst, level); } else { // Simple reference conversion. // Note: Do not check for a class hierarchy relation // between src and dst. In all cases a 'null' argument // will pass the cast conversion. - adapter = makeCheckCast(midType, adapter, i, dst); + adapter2 = makeCheckCast(midType, adapter, i, dst); } } - assert(adapter != null); - assert(adapter.type() == midType); + assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType); + assert(adapter2.type() == midType); + adapter = adapter2; + } + if (retConv) { + adapter2 = makeReturnConversion(adapter, haveReturn, needReturn); + assert(adapter2 != null); + adapter = adapter2; } if (adapter.type() != newType) { // Only trivial conversions remain. - adapter = makeRetypeOnly(newType, adapter); - assert(adapter != null); + adapter2 = makeRetypeOnly(newType, adapter); + assert(adapter2 != null); + adapter = adapter2; // Actually, that's because there were no non-trivial ones: - assert(lastConv == -1); + assert(lastConv == -1 || retConv); } assert(adapter.type() == newType); return adapter; } + private static MethodHandle makeReturnConversion(MethodHandle target, Class haveReturn, Class needReturn) { + MethodHandle adjustReturn; + if (haveReturn == void.class) { + // synthesize a zero value for the given void + Object zero = Wrapper.forBasicType(needReturn).zero(); + adjustReturn = MethodHandles.constant(needReturn, zero); + } else { + MethodType needConversion = MethodType.methodType(needReturn, haveReturn); + adjustReturn = MethodHandles.identity(needReturn).asType(needConversion); + } + if (!canCollectArguments(adjustReturn.type(), target.type(), 0, false)) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + throw new InternalError("NYI"); + } + return makeCollectArguments(adjustReturn, target, 0, false); + } + /** * Create a JVM-level adapter method handle to permute the arguments * of the given method. @@ -224,7 +263,7 @@ class AdapterMethodHandle extends BoundMethodHandle { if (argumentMap.length != oldType.parameterCount()) throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap)); if (nullPermutation) { - MethodHandle res = makePairwiseConvert(newType, target); + MethodHandle res = makePairwiseConvert(newType, target, 0); // well, that was easy if (res == null) throw newIllegalArgumentException("cannot convert pairwise: "+newType); @@ -310,11 +349,25 @@ class AdapterMethodHandle extends BoundMethodHandle { return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT; } + static int extractStackMove(int convOp) { + int spChange = convOp >> CONV_STACK_MOVE_SHIFT; + return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT; + } + + static int extractStackMove(MethodHandle target) { + if (target instanceof AdapterMethodHandle) { + AdapterMethodHandle amh = (AdapterMethodHandle) target; + return extractStackMove(amh.getConversion()); + } else { + return 0; + } + } + /** Construct an adapter conversion descriptor for a single-argument conversion. */ private static long makeConv(int convOp, int argnum, int src, int dest) { - assert(src == (src & 0xF)); - assert(dest == (dest & 0xF)); - assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF); + assert(src == (src & CONV_TYPE_MASK)); + assert(dest == (dest & CONV_TYPE_MASK)); + assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS); int stackMove = type2size(dest) - type2size(src); return ((long) argnum << 32 | (long) convOp << CONV_OP_SHIFT | @@ -323,11 +376,10 @@ class AdapterMethodHandle extends BoundMethodHandle { insertStackMove(stackMove) ); } - private static long makeConv(int convOp, int argnum, int stackMove) { - assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS); + private static long makeDupConv(int convOp, int argnum, int stackMove) { + // simple argument motion, requiring one slot to specify + assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS); byte src = 0, dest = 0; - if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS) - src = dest = T_OBJECT; return ((long) argnum << 32 | (long) convOp << CONV_OP_SHIFT | (int) src << CONV_SRC_TYPE_SHIFT | @@ -336,7 +388,8 @@ class AdapterMethodHandle extends BoundMethodHandle { ); } private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) { - assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS); + // more complex argument motion, requiring two slots to specify + assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS); return ((long) srcArg << 32 | (long) convOp << CONV_OP_SHIFT | (int) type << CONV_SRC_TYPE_SHIFT | @@ -344,6 +397,18 @@ class AdapterMethodHandle extends BoundMethodHandle { (int) destSlot << CONV_VMINFO_SHIFT ); } + private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) { + // spreading or collecting, at a particular slot location + assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS); + // src = spread ? T_OBJECT (for array) : common type of collected args (else void) + // dest = spread ? element type of array : result type of collector (can be void) + return ((long) argnum << 32 | + (long) convOp << CONV_OP_SHIFT | + (int) src << CONV_SRC_TYPE_SHIFT | + (int) dest << CONV_DEST_TYPE_SHIFT | + insertStackMove(stackMove) + ); + } private static long makeConv(int convOp) { assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW); return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero @@ -570,14 +635,10 @@ class AdapterMethodHandle extends BoundMethodHandle { static boolean canPrimCast(Class src, Class dst) { if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) { return false; - } else if (Wrapper.forPrimitiveType(dst).isFloating()) { - // both must be floating types - return Wrapper.forPrimitiveType(src).isFloating(); } else { - // both are integral, and all combinations work fine - assert(Wrapper.forPrimitiveType(src).isIntegral() && - Wrapper.forPrimitiveType(dst).isIntegral()); - return true; + boolean sflt = Wrapper.forPrimitiveType(src).isFloating(); + boolean dflt = Wrapper.forPrimitiveType(dst).isFloating(); + return !(sflt | dflt); // no float support at present } } @@ -589,6 +650,29 @@ class AdapterMethodHandle extends BoundMethodHandle { */ static MethodHandle makePrimCast(MethodType newType, MethodHandle target, int arg, Class convType) { + Class src = newType.parameterType(arg); + if (canPrimCast(src, convType)) + return makePrimCastOnly(newType, target, arg, convType); + Class dst = convType; + boolean sflt = Wrapper.forPrimitiveType(src).isFloating(); + boolean dflt = Wrapper.forPrimitiveType(dst).isFloating(); + if (sflt | dflt) { + MethodHandle convMethod; + if (sflt) + convMethod = ((src == double.class) + ? ValueConversions.convertFromDouble(dst) + : ValueConversions.convertFromFloat(dst)); + else + convMethod = ((dst == double.class) + ? ValueConversions.convertToDouble(src) + : ValueConversions.convertToFloat(src)); + long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst)); + return new AdapterMethodHandle(target, newType, conv, convMethod); + } + throw new InternalError("makePrimCast"); + } + static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target, + int arg, Class convType) { MethodType oldType = target.type(); if (!canPrimCast(newType, oldType, arg, convType)) return null; @@ -602,7 +686,7 @@ class AdapterMethodHandle extends BoundMethodHandle { * The convType is the unboxed type; it can be either a primitive or wrapper. */ static boolean canUnboxArgument(MethodType newType, MethodType targetType, - int arg, Class convType) { + int arg, Class convType, int level) { if (!convOpSupported(OP_REF_TO_PRIM)) return false; Class src = newType.parameterType(arg); Class dst = targetType.parameterType(arg); @@ -616,21 +700,31 @@ class AdapterMethodHandle extends BoundMethodHandle { return (diff == arg+1); // arg is sole non-trivial diff } /** Can an primitive unboxing adapter validly convert src to dst? */ - static boolean canUnboxArgument(Class src, Class dst) { - return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive()); + static boolean canUnboxArgument(Class src, Class dst, int level) { + assert(dst.isPrimitive()); + // if we have JVM support for boxing, we can also do complex unboxing + if (convOpSupported(OP_PRIM_TO_REF)) return true; + Wrapper dw = Wrapper.forPrimitiveType(dst); + // Level 0 means cast and unbox. This works on any reference. + if (level == 0) return !src.isPrimitive(); + assert(level >= 0 && level <= 2); + // Levels 1 and 2 allow widening and/or narrowing conversions. + // These are not supported directly by the JVM. + // But if the input reference is monomorphic, we can do it. + return dw.wrapperType() == src; } /** Factory method: Unbox the given argument. * Return null if this cannot be done. */ static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target, - int arg, Class convType) { + int arg, Class convType, int level) { MethodType oldType = target.type(); Class src = newType.parameterType(arg); Class dst = oldType.parameterType(arg); Class boxType = Wrapper.asWrapperType(convType); Class primType = Wrapper.asPrimitiveType(convType); - if (!canUnboxArgument(newType, oldType, arg, convType)) + if (!canUnboxArgument(newType, oldType, arg, convType, level)) return null; MethodType castDone = newType; if (!VerifyType.isNullConversion(src, boxType)) @@ -642,19 +736,46 @@ class AdapterMethodHandle extends BoundMethodHandle { return makeCheckCast(newType, adapter, arg, boxType); } + /** Can a boxing conversion validly convert src to dst? */ + static boolean canBoxArgument(MethodType newType, MethodType targetType, + int arg, Class convType) { + if (!convOpSupported(OP_PRIM_TO_REF)) return false; + Class src = newType.parameterType(arg); + Class dst = targetType.parameterType(arg); + Class boxType = Wrapper.asWrapperType(convType); + convType = Wrapper.asPrimitiveType(convType); + if (!canCheckCast(boxType, dst) + || boxType == convType + || !VerifyType.isNullConversion(src, convType)) + return false; + int diff = diffTypes(newType, targetType, false); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive boxing adapter validly convert src to dst? */ static boolean canBoxArgument(Class src, Class dst) { if (!convOpSupported(OP_PRIM_TO_REF)) return false; - throw new UnsupportedOperationException("NYI"); + return (src.isPrimitive() && !dst.isPrimitive()); } - /** Factory method: Unbox the given argument. + /** Factory method: Box the given argument. * Return null if this cannot be done. */ static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target, int arg, Class convType) { - // this is difficult to do in the JVM because it must GC - return null; + MethodType oldType = target.type(); + Class src = newType.parameterType(arg); + Class dst = oldType.parameterType(arg); + Class boxType = Wrapper.asWrapperType(convType); + Class primType = Wrapper.asPrimitiveType(convType); + if (!canBoxArgument(newType, oldType, arg, convType)) { + return null; + } + if (!VerifyType.isNullConversion(boxType, dst)) + target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst); + MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType)); + long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT); + return new AdapterMethodHandle(target, newType, conv, boxerMethod); } /** Can an adapter simply drop arguments to convert the target to newType? */ @@ -699,7 +820,7 @@ class AdapterMethodHandle extends BoundMethodHandle { int slotCount = keep1InSlot - dropSlot; assert(slotCount >= dropArgCount); assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount()); - long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount); + long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount); return new AdapterMethodHandle(target, newType, conv); } @@ -739,7 +860,7 @@ class AdapterMethodHandle extends BoundMethodHandle { int keep1InSlot = newType.parameterSlotDepth(dupArgPos); int slotCount = keep1InSlot - dupSlot; assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount()); - long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount); + long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount); return new AdapterMethodHandle(target, newType, conv); } @@ -900,7 +1021,7 @@ class AdapterMethodHandle extends BoundMethodHandle { for (int i = 0; i < spreadArgCount; i++) { Class src = VerifyType.spreadArgElementType(spreadArgType, i); Class dst = targetType.parameterType(spreadArgPos + i); - if (src == null || !VerifyType.isNullConversion(src, dst)) + if (src == null || !canConvertArgument(src, dst, 1)) return false; } return true; @@ -910,24 +1031,100 @@ class AdapterMethodHandle extends BoundMethodHandle { /** Factory method: Spread selected argument. */ static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target, 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; + // dest is not significant; remove? + int dest = T_VOID; + for (int i = 0; i < spreadArgCount; i++) { + Class arg = VerifyType.spreadArgElementType(spreadArgType, i); + if (arg == null) arg = Object.class; + int dest2 = basicType(arg); + if (dest == T_VOID) dest = dest2; + else if (dest != dest2) dest = T_VOID; + if (dest == T_VOID) break; + targetType = targetType.changeParameterType(spreadArgPos + i, arg); + } + target = target.asType(targetType); + int arrayArgSize = 1; // always a reference // in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ] // out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ] int keep2OutPos = spreadArgPos + spreadArgCount; - int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); - int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); - int slotCount = keep1OutSlot - spreadSlot; - assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1)); + int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...| + int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...| + assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize)); + int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...| assert(slotCount >= spreadArgCount); - long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1); + int stackMove = - arrayArgSize + slotCount; // pop array, push N slots + long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove); MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType); assert(res.type().parameterType(spreadArgPos) == spreadArgType); return res; } - // TO DO: makeCollectArguments, makeFlyby, makeRicochet + /** Can an adapter collect a series of arguments, replacing them by zero or one results? */ + static boolean canCollectArguments(MethodType targetType, + MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) { + if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS)) return false; + int collectArgCount = collectorType.parameterCount(); + Class rtype = collectorType.returnType(); + assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype) + // [(Object)Object[], (Object[])Object[], 0, 1] + : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount) + ; + return true; + } + + /** Factory method: Collect or filter selected argument(s). */ + static MethodHandle makeCollectArguments(MethodHandle target, + MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) { + assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs)); + MethodType targetType = target.type(); + MethodType collectorType = collector.type(); + int collectArgCount = collectorType.parameterCount(); + Class collectValType = collectorType.returnType(); + int collectValCount = (collectValType == void.class ? 0 : 1); + int collectValSlots = collectorType.returnSlotCount(); + MethodType newType = targetType + .dropParameterTypes(collectArgPos, collectArgPos+collectValCount); + if (!retainOriginalArgs) { + newType = newType + .insertParameterTypes(collectArgPos, collectorType.parameterList()); + } else { + // parameter types at the fold point must be the same + assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0) + : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs); + } + // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ] + // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ] + // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ] + int keep2InPos = collectArgPos + collectArgCount; + int keep1InSlot = newType.parameterSlotDepth(collectArgPos); // leading edge of |collect...| + int collectSlot = newType.parameterSlotDepth(keep2InPos); // trailing edge of |collect...| + int slotCount = keep1InSlot - collectSlot; // slots in |collect...| + assert(slotCount >= collectArgCount); + assert(collectSlot == targetType.parameterSlotDepth( + collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) )); + int dest = basicType(collectValType); + int src = T_VOID; + // src is not significant; remove? + for (int i = 0; i < collectArgCount; i++) { + int src2 = basicType(collectorType.parameterType(i)); + if (src == T_VOID) src = src2; + else if (src != src2) src = T_VOID; + if (src == T_VOID) break; + } + int stackMove = collectValSlots; // push 0..2 results + if (!retainOriginalArgs) stackMove -= slotCount; // pop N arguments + int lastCollectArg = keep2InPos-1; + long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS, + lastCollectArg, src, dest, stackMove); + MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector); + assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount) + .equals(collector.type().parameterList())); + return res; + } @Override public String toString() { diff --git a/src/share/classes/java/lang/invoke/CallSite.java b/src/share/classes/java/lang/invoke/CallSite.java index b2da146d2618c926a43a2408cae21f8a14bf5315..0924a438c8062d72db2f1122f9f8759539ccdd2e 100644 --- a/src/share/classes/java/lang/invoke/CallSite.java +++ b/src/share/classes/java/lang/invoke/CallSite.java @@ -273,9 +273,9 @@ public class CallSite { Object binding; info = maybeReBox(info); if (info == null) { - binding = bootstrapMethod.invokeGeneric(caller, name, type); + binding = bootstrapMethod.invoke(caller, name, type); } else if (!info.getClass().isArray()) { - binding = bootstrapMethod.invokeGeneric(caller, name, type, info); + binding = bootstrapMethod.invoke(caller, name, type, info); } else { Object[] argv = (Object[]) info; maybeReBoxElements(argv); @@ -283,10 +283,10 @@ public class CallSite { throw new BootstrapMethodError("too many bootstrap method arguments"); MethodType bsmType = bootstrapMethod.type(); if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class) - binding = bootstrapMethod.invokeGeneric(caller, name, type, argv); + binding = bootstrapMethod.invoke(caller, name, type, argv); else binding = MethodHandles.spreadInvoker(bsmType, 3) - .invokeGeneric(bootstrapMethod, caller, name, type, argv); + .invoke(bootstrapMethod, caller, name, type, argv); } //System.out.println("BSM for "+name+type+" => "+binding); if (binding instanceof CallSite) { diff --git a/src/share/classes/java/lang/invoke/FilterGeneric.java b/src/share/classes/java/lang/invoke/FilterGeneric.java index 6c3395002f5bb37e58d8616667dbe612d5c355c9..769289710d98f63397259ec4d620e1631db7112e 100644 --- a/src/share/classes/java/lang/invoke/FilterGeneric.java +++ b/src/share/classes/java/lang/invoke/FilterGeneric.java @@ -61,6 +61,10 @@ class FilterGeneric { return ad; } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) { Adapter ad = getAdapter(kind, pos); return ad.makeInstance(ad.prototypeEntryPoint(), filter, target); diff --git a/src/share/classes/java/lang/invoke/FilterOneArgument.java b/src/share/classes/java/lang/invoke/FilterOneArgument.java index 64a9f072797c717ff4d804fa7347e564058b2534..f52c8c06f0a87bb4064e8ff1c4a7a88ededf410f 100644 --- a/src/share/classes/java/lang/invoke/FilterOneArgument.java +++ b/src/share/classes/java/lang/invoke/FilterOneArgument.java @@ -67,6 +67,10 @@ class FilterOneArgument extends BoundMethodHandle { this.target = target; } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + public static MethodHandle make(MethodHandle filter, MethodHandle target) { if (filter == null) return target; if (target == null) return filter; diff --git a/src/share/classes/java/lang/invoke/FromGeneric.java b/src/share/classes/java/lang/invoke/FromGeneric.java index 1c0523a4a3668addb6b58944281ba7b245dd5a80..1e035a46fb1dfc15655e42945872c5da795796d5 100644 --- a/src/share/classes/java/lang/invoke/FromGeneric.java +++ b/src/share/classes/java/lang/invoke/FromGeneric.java @@ -98,6 +98,10 @@ class FromGeneric { this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0); } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + /** * The typed target will be called according to targetType. * The adapter code will in fact see the raw result from internalType, @@ -112,10 +116,10 @@ class FromGeneric { assert(iret == Object.class); return ValueConversions.identity(); } else if (wrap.primitiveType() == iret) { - return ValueConversions.box(wrap, false); + return ValueConversions.box(wrap); } else { assert(tret == double.class ? iret == long.class : iret == int.class); - return ValueConversions.boxRaw(wrap, false); + return ValueConversions.boxRaw(wrap); } } @@ -135,7 +139,7 @@ class FromGeneric { MethodType fixArgsType = internalType.changeReturnType(targetType.returnType()); MethodHandle fixArgs = MethodHandleImpl.convertArguments( invoker, Invokers.invokerType(fixArgsType), - invoker.type(), null); + invoker.type(), 0); if (fixArgs == null) throw new InternalError("bad fixArgs"); // reinterpret the calling sequence as raw: @@ -160,7 +164,6 @@ class FromGeneric { /** Build an adapter of the given generic type, which invokes typedTarget * on the incoming arguments, after unboxing as necessary. * The return value is boxed if necessary. - * @param genericType the required type of the result * @param typedTarget the target * @return an adapter method handle */ @@ -231,7 +234,7 @@ class FromGeneric { } static Adapter buildAdapterFromBytecodes(MethodType internalType) { - throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException("NYI "+internalType); } /** diff --git a/src/share/classes/java/lang/invoke/InvokeGeneric.java b/src/share/classes/java/lang/invoke/InvokeGeneric.java index a235e7318da58638412e7e2ffee8782a168f99be..a747090b9a177573fbf244b25f3d5d0131e8bea1 100644 --- a/src/share/classes/java/lang/invoke/InvokeGeneric.java +++ b/src/share/classes/java/lang/invoke/InvokeGeneric.java @@ -29,12 +29,12 @@ import sun.invoke.util.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** - * Adapters which manage MethodHandle.invokeGeneric calls. + * Adapters which manage inexact MethodHandle.invoke calls. * The JVM calls one of these when the exact type match fails. * @author jrose */ class InvokeGeneric { - // erased type for the call, which originates from an invokeGeneric site + // erased type for the call, which originates from an inexact invoke site private final MethodType erasedCallerType; // an invoker of type (MT, MH; A...) -> R private final MethodHandle initialInvoker; @@ -56,7 +56,7 @@ class InvokeGeneric { } /** Return the adapter information for this type's erasure. */ - /*non-public*/ static MethodHandle genericInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { + /*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { InvokeGeneric gen = new InvokeGeneric(erasedCallerType); return gen.initialInvoker; } diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java index 4eeb36f76b767da10d3171c79ba9d949141cd0c5..7bf134cc8581117bf3ed9a2d413fa593bb752778 100644 --- a/src/share/classes/java/lang/invoke/Invokers.java +++ b/src/share/classes/java/lang/invoke/Invokers.java @@ -43,10 +43,10 @@ class Invokers { private /*lazy*/ MethodHandle erasedInvoker; /*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric - // generic (untyped) invoker for the outgoing call - private /*lazy*/ MethodHandle genericInvoker; + // general invoker for the outgoing call + private /*lazy*/ MethodHandle generalInvoker; - // generic (untyped) invoker for the outgoing call; accepts a single Object[] + // general invoker for the outgoing call; accepts a single Object[] private final /*lazy*/ MethodHandle[] spreadInvokers; // invoker for an unbound callsite @@ -77,13 +77,13 @@ class Invokers { return invoker; } - /*non-public*/ MethodHandle genericInvoker() { + /*non-public*/ MethodHandle generalInvoker() { MethodHandle invoker1 = exactInvoker(); - MethodHandle invoker = genericInvoker; + MethodHandle invoker = generalInvoker; if (invoker != null) return invoker; - MethodType genericType = targetType.generic(); - invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType)); - genericInvoker = invoker; + MethodType generalType = targetType.generic(); + invoker = invoker1.asType(invokerType(generalType)); + generalInvoker = invoker; return invoker; } @@ -93,9 +93,9 @@ class Invokers { if (invoker != null) return invoker; MethodType erasedType = targetType.erase(); if (erasedType == targetType.generic()) - invoker = genericInvoker(); + invoker = generalInvoker(); else - invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType)); + invoker = invoker1.asType(invokerType(erasedType)); erasedInvoker = invoker; return invoker; } @@ -103,7 +103,7 @@ class Invokers { /*non-public*/ MethodHandle spreadInvoker(int objectArgCount) { MethodHandle vaInvoker = spreadInvokers[objectArgCount]; if (vaInvoker != null) return vaInvoker; - MethodHandle gInvoker = genericInvoker(); + MethodHandle gInvoker = generalInvoker(); vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount); spreadInvokers[objectArgCount] = vaInvoker; return vaInvoker; diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java index 0300fe758cc301c43f10e5ebe9d008d821ffc0c8..c61336d2af155f686b9abd797e3d52b111e5c87f 100644 --- a/src/share/classes/java/lang/invoke/MemberName.java +++ b/src/share/classes/java/lang/invoke/MemberName.java @@ -525,7 +525,7 @@ import static java.lang.invoke.MethodHandleStatics.*; /** A factory type for resolving member names with the help of the VM. * TBD: Define access-safe public constructors for this factory. */ - public static class Factory { + /*non-public*/ static class Factory { private Factory() { } // singleton pattern static Factory INSTANCE = new Factory(); diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java index 8165915c7bb7ece633bab771c5ad92c5f5d5d56c..e6424f1516f82128ad6ecfa5cfb268073dc7491d 100644 --- a/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/share/classes/java/lang/invoke/MethodHandle.java @@ -26,6 +26,7 @@ package java.lang.invoke; +import sun.invoke.util.ValueConversions; import static java.lang.invoke.MethodHandleStatics.*; /** @@ -53,12 +54,12 @@ import static java.lang.invoke.MethodHandleStatics.*; * and the kinds of transformations that apply to it. *

* A method handle contains a pair of special invoker methods - * called {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}. + * called {@link #invokeExact invokeExact} and {@link #invoke invoke}. * Both invoker methods provide direct access to the method handle's * underlying method, constructor, field, or other operation, * as modified by transformations of arguments and return values. * Both invokers accept calls which exactly match the method handle's own type. - * The {@code invokeGeneric} invoker also accepts a range of other call types. + * The plain, inexact invoker also accepts a range of other call types. *

* Method handles are immutable and have no visible state. * Of course, they can be bound to underlying methods or data which exhibit state. @@ -76,7 +77,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * may change from time to time or across implementations from different vendors. * *

Method handle compilation

- * A Java method call expression naming {@code invokeExact} or {@code invokeGeneric} + * A Java method call expression naming {@code invokeExact} or {@code invoke} * can invoke a method handle from Java source code. * From the viewpoint of source code, these methods can take any arguments * and their result can be cast to any return type. @@ -86,7 +87,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * which connects this freedom of invocation directly to the JVM execution stack. *

* As is usual with virtual methods, source-level calls to {@code invokeExact} - * and {@code invokeGeneric} compile to an {@code invokevirtual} instruction. + * and {@code invoke} compile to an {@code invokevirtual} instruction. * More unusually, the compiler must record the actual argument types, * and may not perform method invocation conversions on the arguments. * Instead, it must push them on the stack according to their own unconverted types. @@ -109,7 +110,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * The first time a {@code invokevirtual} instruction is executed * 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 invokeGeneric}. + * This is true of calls to {@code invokeExact} and {@code invoke}. * In this case, the type descriptor emitted by the compiler is checked for * correct syntax and names it contains are resolved. * Thus, an {@code invokevirtual} instruction which invokes @@ -127,18 +128,18 @@ import static java.lang.invoke.MethodHandleStatics.*; * In the case of {@code invokeExact}, the type descriptor of the invocation * (after resolving symbolic type names) must exactly match the method type * of the receiving method handle. - * In the case of {@code invokeGeneric}, the resolved type descriptor + * In the case of plain, inexact {@code invoke}, the resolved type descriptor * must be a valid argument to the receiver's {@link #asType asType} method. - * Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}. + * Thus, plain {@code invoke} is more permissive than {@code invokeExact}. *

* After type matching, a call to {@code invokeExact} directly * and immediately invoke the method handle's underlying method * (or other behavior, as the case may be). *

- * A call to {@code invokeGeneric} works the same as a call to + * A call to plain {@code invoke} works the same as a call to * {@code invokeExact}, if the type descriptor specified by the caller * exactly matches the method handle's own type. - * If there is a type mismatch, {@code invokeGeneric} attempts + * If there is a type mismatch, {@code invoke} attempts * to adjust the type of the receiving method handle, * as if by a call to {@link #asType asType}, * to obtain an exactly invokable method handle {@code M2}. @@ -152,7 +153,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * In typical programs, method handle type matching will usually succeed. * But if a match fails, the JVM will throw a {@link WrongMethodTypeException}, * either directly (in the case of {@code invokeExact}) or indirectly as if - * by a failed call to {@code asType} (in the case of {@code invokeGeneric}). + * by a failed call to {@code asType} (in the case of {@code invoke}). *

* Thus, a method type mismatch which might show up as a linkage error * in a statically typed program can show up as @@ -249,8 +250,8 @@ assert(s.equals("savvy")); mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); -x = mh.invokeGeneric("one", "two"); -// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; +x = mh.invoke("one", "two"); +// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assert(x.equals(java.util.Arrays.asList("one","two"))); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); @@ -269,12 +270,12 @@ mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V * - * Each of the above calls to {@code invokeExact} or {@code invokeGeneric} + * 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. * *

Exceptions

- * The methods {@code invokeExact} and {@code invokeGeneric} are declared + * The methods {@code invokeExact} and {@code invoke} are declared * to throw {@link java.lang.Throwable Throwable}, * which is to say that there is no static restriction on what a method handle * can throw. Since the JVM does not distinguish between checked @@ -288,7 +289,7 @@ mh.invokeExact(System.out, "Hello, world."); * *

Signature polymorphism

* The unusual compilation and linkage behavior of - * {@code invokeExact} and {@code invokeGeneric} + * {@code invokeExact} and plain {@code invoke} * is referenced by the term signature polymorphism. * A signature polymorphic method is one which can operate with * any of a wide range of call signatures and return types. @@ -322,7 +323,7 @@ mh.invokeExact(System.out, "Hello, world."); * The following methods (and no others) are signature polymorphic: * *

* A signature polymorphic method will be declared with the following properties: @@ -374,24 +375,34 @@ mh.invokeExact(System.out, "Hello, world."); *

* As a special case, * when the Core Reflection API is used to view the signature polymorphic - * methods {@code invokeExact} or {@code invokeGeneric} in this class, - * they appear as single, non-polymorphic native methods. - * Calls to these native methods do not result in method handle invocations. + * methods {@code invokeExact} or plain {@code invoke} in this class, + * they appear as ordinary non-polymorphic methods. + * Their reflective appearance, as viewed by + * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod}, + * is unaffected by their special status in this API. + * For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers} + * will report exactly those modifier bits required for any similarly + * 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}. + * 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 - * with the normal presentation via bytecodes. - * Thus, these two native methods, as viewed by - * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod}, - * are placeholders only. - * If invoked via {@link java.lang.reflect.Method#invoke Method.invoke}, - * they will throw {@code UnsupportedOperationException}. + * 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. *

* In order to obtain an invoker method for a particular type descriptor, * use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker}, - * or {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}. + * or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}. * The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual} * API is also able to return a method handle - * to call {@code invokeExact} or {@code invokeGeneric}, + * to call {@code invokeExact} or plain {@code invoke}, * for any specified type descriptor . * *

Interoperation between method handles and Java generics

@@ -523,7 +534,7 @@ public abstract class MethodHandle { * 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 invokeGeneric} must + * The 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, @@ -539,11 +550,18 @@ public abstract class MethodHandle { * @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 - * to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site + * 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. *

@@ -553,7 +571,7 @@ public abstract class MethodHandle { *

- * @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls + * @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls * @see #asVarargsCollector */ public boolean isVarargsCollector() { diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java index e82442092fe0b42001167fd36cb003eb0be45fa5..b17251e47992417eb85deb209b24ce93da49c055 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -121,11 +121,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (nargs < INVOKES.length) { MethodHandle invoke = INVOKES[nargs]; MethodType conType = CON_TYPES[nargs]; - MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, null); + MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, 0); if (gcon == null) return null; MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon); assert(galloc.type() == newType.generic()); - return convertArguments(galloc, newType, galloc.type(), null); + return convertArguments(galloc, newType, galloc.type(), 0); } else { MethodHandle invoke = VARARGS_INVOKE; MethodType conType = CON_TYPES[nargs]; @@ -256,8 +256,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; FieldAccessor.ahandle(arrayClass, true) }; if (mhs[0].type().parameterType(0) == Class.class) { - mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass); - mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass); + mhs[0] = mhs[0].bindTo(elemClass); + mhs[1] = mhs[1].bindTo(elemClass); } synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier FieldAccessor.ARRAY_CACHE.put(elemClass, mhs); @@ -372,7 +372,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (evclass != vclass || (!isStatic && ecclass != cclass)) { MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); strongType = strongType.insertParameterTypes(0, FieldAccessor.class); - mh = MethodHandles.convertArguments(mh, strongType); + mh = convertArguments(mh, strongType, 0); } return mh; } @@ -439,8 +439,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } if (caclass != null) { MethodType strongType = FieldAccessor.atype(caclass, isSetter); - mh = MethodHandles.insertArguments(mh, 0, caclass); - mh = MethodHandles.convertArguments(mh, strongType); + mh = mh.bindTo(caclass); + mh = convertArguments(mh, strongType, 0); } return mh; } @@ -465,7 +465,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 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(), null); + return convertArguments(bmh, newType, bmh.type(), 0); } } } @@ -486,301 +486,378 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return new BoundMethodHandle(target, receiver, argnum); } - static MethodHandle convertArguments(MethodHandle target, + static MethodHandle permuteArguments(MethodHandle target, MethodType newType, MethodType oldType, int[] permutationOrNull) { assert(oldType.parameterCount() == target.type().parameterCount()); - if (permutationOrNull != null) { - int outargs = oldType.parameterCount(), inargs = newType.parameterCount(); - if (permutationOrNull.length != outargs) - throw newIllegalArgumentException("wrong number of arguments in permutation"); - // Make the individual outgoing argument types match up first. - Class[] callTypeArgs = new Class[outargs]; - for (int i = 0; i < outargs; i++) - callTypeArgs[i] = newType.parameterType(permutationOrNull[i]); - MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs); - target = convertArguments(target, callType, oldType, null); - assert(target != null); - oldType = target.type(); - List goal = new ArrayList(); // i*TOKEN - List state = new ArrayList(); // i*TOKEN - List drops = new ArrayList(); // not tokens - List dups = new ArrayList(); // not tokens - final int TOKEN = 10; // to mark items which are symbolic only - // state represents the argument values coming into target - for (int i = 0; i < outargs; i++) { - state.add(permutationOrNull[i] * TOKEN); + int outargs = oldType.parameterCount(), inargs = newType.parameterCount(); + if (permutationOrNull.length != outargs) + throw newIllegalArgumentException("wrong number of arguments in permutation"); + // Make the individual outgoing argument types match up first. + Class[] callTypeArgs = new Class[outargs]; + for (int i = 0; i < outargs; i++) + callTypeArgs[i] = newType.parameterType(permutationOrNull[i]); + MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs); + target = convertArguments(target, callType, oldType, 0); + assert(target != null); + oldType = target.type(); + List goal = new ArrayList(); // i*TOKEN + List state = new ArrayList(); // i*TOKEN + List drops = new ArrayList(); // not tokens + List dups = new ArrayList(); // not tokens + final int TOKEN = 10; // to mark items which are symbolic only + // state represents the argument values coming into target + for (int i = 0; i < outargs; i++) { + state.add(permutationOrNull[i] * TOKEN); + } + // goal represents the desired state + for (int i = 0; i < inargs; i++) { + if (state.contains(i * TOKEN)) { + goal.add(i * TOKEN); + } else { + // adapter must initially drop all unused arguments + drops.add(i); } - // goal represents the desired state - for (int i = 0; i < inargs; i++) { - if (state.contains(i * TOKEN)) { - goal.add(i * TOKEN); - } else { - // adapter must initially drop all unused arguments - drops.add(i); + } + // detect duplications + while (state.size() > goal.size()) { + for (int i2 = 0; i2 < state.size(); i2++) { + int arg1 = state.get(i2); + int i1 = state.indexOf(arg1); + if (i1 != i2) { + // found duplicate occurrence at i2 + int arg2 = (inargs++) * TOKEN; + state.set(i2, arg2); + dups.add(goal.indexOf(arg1)); + goal.add(arg2); } } - // detect duplications - while (state.size() > goal.size()) { - for (int i2 = 0; i2 < state.size(); i2++) { - int arg1 = state.get(i2); - int i1 = state.indexOf(arg1); - if (i1 != i2) { - // found duplicate occurrence at i2 - int arg2 = (inargs++) * TOKEN; - state.set(i2, arg2); - dups.add(goal.indexOf(arg1)); - goal.add(arg2); + } + assert(state.size() == goal.size()); + int size = goal.size(); + while (!state.equals(goal)) { + // Look for a maximal sequence of adjacent misplaced arguments, + // and try to rotate them into place. + int bestRotArg = -10 * TOKEN, bestRotLen = 0; + int thisRotArg = -10 * TOKEN, thisRotLen = 0; + for (int i = 0; i < size; i++) { + int arg = state.get(i); + // Does this argument match the current run? + if (arg == thisRotArg + TOKEN) { + thisRotArg = arg; + thisRotLen += 1; + if (bestRotLen < thisRotLen) { + bestRotLen = thisRotLen; + bestRotArg = thisRotArg; } - } - } - assert(state.size() == goal.size()); - int size = goal.size(); - while (!state.equals(goal)) { - // Look for a maximal sequence of adjacent misplaced arguments, - // and try to rotate them into place. - int bestRotArg = -10 * TOKEN, bestRotLen = 0; - int thisRotArg = -10 * TOKEN, thisRotLen = 0; - for (int i = 0; i < size; i++) { - int arg = state.get(i); - // Does this argument match the current run? - if (arg == thisRotArg + TOKEN) { + } else { + // The old sequence (if any) stops here. + thisRotLen = 0; + thisRotArg = -10 * TOKEN; + // But maybe a new one starts here also. + int wantArg = goal.get(i); + final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION; + if (arg != wantArg && + arg >= wantArg - TOKEN * MAX_ARG_ROTATION && + arg <= wantArg + TOKEN * MAX_ARG_ROTATION) { thisRotArg = arg; - thisRotLen += 1; - if (bestRotLen < thisRotLen) { - bestRotLen = thisRotLen; - bestRotArg = thisRotArg; - } - } else { - // The old sequence (if any) stops here. - thisRotLen = 0; - thisRotArg = -10 * TOKEN; - // But maybe a new one starts here also. - int wantArg = goal.get(i); - final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION; - if (arg != wantArg && - arg >= wantArg - TOKEN * MAX_ARG_ROTATION && - arg <= wantArg + TOKEN * MAX_ARG_ROTATION) { - thisRotArg = arg; - thisRotLen = 1; - } + thisRotLen = 1; } } - if (bestRotLen >= 2) { - // Do a rotation if it can improve argument positioning - // by at least 2 arguments. This is not always optimal, - // but it seems to catch common cases. - int dstEnd = state.indexOf(bestRotArg); - int srcEnd = goal.indexOf(bestRotArg); - int rotBy = dstEnd - srcEnd; - int dstBeg = dstEnd - (bestRotLen - 1); - int srcBeg = srcEnd - (bestRotLen - 1); - assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs - // Make a span which covers both source and destination. - int rotBeg = Math.min(dstBeg, srcBeg); - int rotEnd = Math.max(dstEnd, srcEnd); - int score = 0; - for (int i = rotBeg; i <= rotEnd; i++) { - if ((int)state.get(i) != (int)goal.get(i)) - score += 1; - } - List rotSpan = state.subList(rotBeg, rotEnd+1); - Collections.rotate(rotSpan, -rotBy); // reverse direction - for (int i = rotBeg; i <= rotEnd; i++) { - if ((int)state.get(i) != (int)goal.get(i)) - score -= 1; - } - if (score >= 2) { - // Improved at least two argument positions. Do it. - List> ptypes = Arrays.asList(oldType.parameterArray()); - Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy); - MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes); - MethodHandle nextTarget - = AdapterMethodHandle.makeRotateArguments(rotType, target, - rotBeg, rotSpan.size(), rotBy); - if (nextTarget != null) { - //System.out.println("Rot: "+rotSpan+" by "+rotBy); - target = nextTarget; - oldType = rotType; - continue; - } - } - // Else de-rotate, and drop through to the swap-fest. - Collections.rotate(rotSpan, rotBy); + } + if (bestRotLen >= 2) { + // Do a rotation if it can improve argument positioning + // by at least 2 arguments. This is not always optimal, + // but it seems to catch common cases. + int dstEnd = state.indexOf(bestRotArg); + int srcEnd = goal.indexOf(bestRotArg); + int rotBy = dstEnd - srcEnd; + int dstBeg = dstEnd - (bestRotLen - 1); + int srcBeg = srcEnd - (bestRotLen - 1); + assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs + // Make a span which covers both source and destination. + int rotBeg = Math.min(dstBeg, srcBeg); + int rotEnd = Math.max(dstEnd, srcEnd); + int score = 0; + for (int i = rotBeg; i <= rotEnd; i++) { + if ((int)state.get(i) != (int)goal.get(i)) + score += 1; } - - // Now swap like the wind! - List> ptypes = Arrays.asList(oldType.parameterArray()); - for (int i = 0; i < size; i++) { - // What argument do I want here? - int arg = goal.get(i); - if (arg != state.get(i)) { - // Where is it now? - int j = state.indexOf(arg); - Collections.swap(ptypes, i, j); - MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes); - target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); - if (target == null) throw newIllegalArgumentException("cannot swap"); - assert(target.type() == swapType); - oldType = swapType; - Collections.swap(state, i, j); + List rotSpan = state.subList(rotBeg, rotEnd+1); + Collections.rotate(rotSpan, -rotBy); // reverse direction + for (int i = rotBeg; i <= rotEnd; i++) { + if ((int)state.get(i) != (int)goal.get(i)) + score -= 1; + } + if (score >= 2) { + // Improved at least two argument positions. Do it. + List> ptypes = Arrays.asList(oldType.parameterArray()); + Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy); + MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes); + MethodHandle nextTarget + = AdapterMethodHandle.makeRotateArguments(rotType, target, + rotBeg, rotSpan.size(), rotBy); + if (nextTarget != null) { + //System.out.println("Rot: "+rotSpan+" by "+rotBy); + target = nextTarget; + oldType = rotType; + continue; } } - // One pass of swapping must finish the job. - assert(state.equals(goal)); + // Else de-rotate, and drop through to the swap-fest. + Collections.rotate(rotSpan, rotBy); } - while (!dups.isEmpty()) { - // Grab a contiguous trailing sequence of dups. - int grab = dups.size() - 1; - int dupArgPos = dups.get(grab), dupArgCount = 1; - while (grab - 1 >= 0) { - int dup0 = dups.get(grab - 1); - if (dup0 != dupArgPos - 1) break; - dupArgPos -= 1; - dupArgCount += 1; - grab -= 1; + + // Now swap like the wind! + List> ptypes = Arrays.asList(oldType.parameterArray()); + for (int i = 0; i < size; i++) { + // What argument do I want here? + int arg = goal.get(i); + if (arg != state.get(i)) { + // Where is it now? + int j = state.indexOf(arg); + Collections.swap(ptypes, i, j); + MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes); + target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); + if (target == null) throw newIllegalArgumentException("cannot swap"); + assert(target.type() == swapType); + oldType = swapType; + Collections.swap(state, i, j); } - //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size())); - dups.subList(grab, dups.size()).clear(); - // In the new target type drop that many args from the tail: - List> ptypes = oldType.parameterList(); - ptypes = ptypes.subList(0, ptypes.size() - dupArgCount); - MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes); - target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); - if (target == null) - throw newIllegalArgumentException("cannot dup"); - oldType = target.type(); } - while (!drops.isEmpty()) { - // Grab a contiguous initial sequence of drops. - int dropArgPos = drops.get(0), dropArgCount = 1; - while (dropArgCount < drops.size()) { - int drop1 = drops.get(dropArgCount); - if (drop1 != dropArgPos + dropArgCount) break; - dropArgCount += 1; - } - //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount)); - drops.subList(0, dropArgCount).clear(); - List> dropTypes = newType.parameterList() - .subList(dropArgPos, dropArgPos + dropArgCount); - MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes); - target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); - if (target == null) throw newIllegalArgumentException("cannot drop"); - oldType = target.type(); + // One pass of swapping must finish the job. + assert(state.equals(goal)); + } + while (!dups.isEmpty()) { + // Grab a contiguous trailing sequence of dups. + int grab = dups.size() - 1; + int dupArgPos = dups.get(grab), dupArgCount = 1; + while (grab - 1 >= 0) { + int dup0 = dups.get(grab - 1); + if (dup0 != dupArgPos - 1) break; + dupArgPos -= 1; + dupArgCount += 1; + grab -= 1; + } + //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size())); + dups.subList(grab, dups.size()).clear(); + // In the new target type drop that many args from the tail: + List> ptypes = oldType.parameterList(); + ptypes = ptypes.subList(0, ptypes.size() - dupArgCount); + MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes); + target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); + if (target == null) + throw newIllegalArgumentException("cannot dup"); + oldType = target.type(); + } + while (!drops.isEmpty()) { + // Grab a contiguous initial sequence of drops. + int dropArgPos = drops.get(0), dropArgCount = 1; + while (dropArgCount < drops.size()) { + int drop1 = drops.get(dropArgCount); + if (drop1 != dropArgPos + dropArgCount) break; + dropArgCount += 1; } + //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount)); + drops.subList(0, dropArgCount).clear(); + List> dropTypes = newType.parameterList() + .subList(dropArgPos, dropArgPos + dropArgCount); + MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes); + target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); + if (target == null) throw newIllegalArgumentException("cannot drop"); + oldType = target.type(); + } + return convertArguments(target, newType, oldType, 0); + } + + /*non-public*/ static + MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) { + MethodType oldType = target.type(); + if (oldType.equals(newType)) + return target; + assert(level > 1 || oldType.isConvertibleTo(newType)); + MethodHandle retFilter = null; + Class oldRT = oldType.returnType(); + Class newRT = newType.returnType(); + if (!VerifyType.isNullConversion(oldRT, newRT)) { + if (oldRT == void.class) { + Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT; + retFilter = ValueConversions.zeroConstantFunction(wrap); + } else { + retFilter = MethodHandles.identity(newRT); + retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level); + } + newType = newType.changeReturnType(oldRT); + } + MethodHandle res = null; + Exception ex = null; + try { + res = convertArguments(target, newType, oldType, level); + } catch (IllegalArgumentException ex1) { + ex = ex1; } + if (res == null) { + WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target); + wmt.initCause(ex); + throw wmt; + } + if (retFilter != null) + res = MethodHandles.filterReturnValue(res, retFilter); + return res; + } + + static MethodHandle convertArguments(MethodHandle target, + MethodType newType, + MethodType oldType, + int level) { + assert(oldType.parameterCount() == target.type().parameterCount()); if (newType == oldType) return target; if (oldType.parameterCount() != newType.parameterCount()) - throw newIllegalArgumentException("mismatched parameter count"); - MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target); + throw newIllegalArgumentException("mismatched parameter count", oldType, newType); + MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target, level); if (res != null) return res; + // We can come here in the case of target(int)void => (Object)void, + // because the unboxing logic for Object => int is complex. int argc = oldType.parameterCount(); + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated // The JVM can't do it directly, so fill in the gap with a Java adapter. // TO DO: figure out what to put here from case-by-case experience // Use a heavier method: Convert all the arguments to Object, // then back to the desired types. We might have to use Java-based // method handles to do this. MethodType objType = MethodType.genericMethodType(argc); - MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target); + MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target, level); if (objTarget == null) objTarget = FromGeneric.make(target); - res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget); + res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget, level); if (res != null) return res; return ToGeneric.make(newType, objTarget); } + static MethodHandle spreadArguments(MethodHandle target, Class arrayType, int arrayLength) { + MethodType oldType = target.type(); + int nargs = oldType.parameterCount(); + int keepPosArgs = nargs - arrayLength; + MethodType newType = oldType + .dropParameterTypes(keepPosArgs, nargs) + .insertParameterTypes(keepPosArgs, arrayType); + return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); + } + static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) { + int arrayLength = target.type().parameterCount() - spreadArgPos; + return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); + } static MethodHandle spreadArguments(MethodHandle target, MethodType newType, - int spreadArg) { + int spreadArgPos, + Class arrayType, + int arrayLength) { // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[] MethodType oldType = target.type(); // spread the last argument of newType to oldType - int spreadCount = oldType.parameterCount() - spreadArg; - Class spreadArgType = Object[].class; - MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, spreadArgType, spreadArg, spreadCount); - if (res != null) - return res; - // try an intermediate adapter - Class spreadType = null; - if (spreadArg < 0 || spreadArg >= newType.parameterCount() - || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg))) - throw newIllegalArgumentException("no restarg in "+newType); - Class[] ptypes = oldType.parameterArray(); - for (int i = 0; i < spreadCount; i++) - ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i); - MethodType midType = MethodType.methodType(newType.returnType(), ptypes); - // after spreading, some arguments may need further conversion - MethodHandle target2 = convertArguments(target, midType, oldType, null); - if (target2 == null) - throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType); - res = AdapterMethodHandle.makeSpreadArguments(newType, target2, spreadArgType, spreadArg, spreadCount); - if (res != null) - return res; - res = SpreadGeneric.make(target2, spreadCount); - if (res != null) - res = convertArguments(res, newType, res.type(), null); + 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; } + static MethodHandle collectArguments(MethodHandle target, + int collectArg, + MethodHandle collector) { + MethodType type = target.type(); + Class collectType = collector.type().returnType(); + if (collectType != type.parameterType(collectArg)) + target = target.asType(type.changeParameterType(collectArg, collectType)); + MethodType newType = type + .dropParameterTypes(collectArg, collectArg+1) + .insertParameterTypes(collectArg, collector.type().parameterArray()); + return collectArguments(target, newType, collectArg, collector); + } static MethodHandle collectArguments(MethodHandle target, MethodType newType, int collectArg, MethodHandle collector) { MethodType oldType = target.type(); // (a...,c)=>r - if (collector == null) { - int numCollect = newType.parameterCount() - oldType.parameterCount() + 1; - collector = ValueConversions.varargsArray(numCollect); - } // newType // (a..., b...)=>r MethodType colType = collector.type(); // (b...)=>c // oldType // (a..., b...)=>r assert(newType.parameterCount() == collectArg + colType.parameterCount()); assert(oldType.parameterCount() == collectArg + 1); - MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null); - MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, null); - if (gtarget == null || gcollector == null) return null; - MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget); - MethodHandle result = convertArguments(gresult, newType, gresult.type(), null); + MethodHandle result = null; + if (AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)) { + result = AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false); + } + if (result == null) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0); + MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, 0); + if (gtarget == null || gcollector == null) return null; + MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget); + result = convertArguments(gresult, newType, gresult.type(), 0); + } return result; } static MethodHandle filterArgument(MethodHandle target, - int pos, - MethodHandle filter) { - MethodType ttype = target.type(), gttype = ttype.generic(); + int pos, + MethodHandle filter) { + MethodType ttype = target.type(); + MethodType ftype = filter.type(); + assert(ftype.parameterCount() == 1); + MethodType rtype = ttype.changeParameterType(pos, ftype.parameterType(0)); + MethodType gttype = ttype.generic(); if (ttype != gttype) { - target = convertArguments(target, gttype, ttype, null); + target = convertArguments(target, gttype, ttype, 0); ttype = gttype; } - MethodType ftype = filter.type(), gftype = ftype.generic(); - if (ftype.parameterCount() != 1) - throw new InternalError(); + MethodType gftype = ftype.generic(); if (ftype != gftype) { - filter = convertArguments(filter, gftype, ftype, null); + filter = convertArguments(filter, gftype, ftype, 0); ftype = gftype; } - if (ftype == ttype) { + MethodHandle result = null; + if (AdapterMethodHandle.canCollectArguments(ttype, ftype, pos, false)) { + result = AdapterMethodHandle.makeCollectArguments(target, filter, pos, false); + } + if (result == null) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + if (ftype == ttype) { // simple unary case - return FilterOneArgument.make(filter, target); + result = FilterOneArgument.make(filter, target); + } else { + result = FilterGeneric.makeArgumentFilter(pos, filter, target); + } } - return FilterGeneric.makeArgumentFilter(pos, filter, target); + if (result.type() != rtype) + result = result.asType(rtype); + return result; } static MethodHandle foldArguments(MethodHandle target, - MethodType newType, - MethodHandle combiner) { + MethodType newType, + int foldPos, + MethodHandle combiner) { MethodType oldType = target.type(); MethodType ctype = combiner.type(); - MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null); - MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, null); + if (AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)) { + MethodHandle res = AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true); + if (res != null) return res; + } + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + if (foldPos != 0) return null; + MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0); + MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, 0); + if (ctype.returnType() == void.class) { + gtarget = dropArguments(gtarget, oldType.generic().insertParameterTypes(foldPos, Object.class), foldPos); + } if (gtarget == null || gcombiner == null) return null; MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget); - MethodHandle result = convertArguments(gresult, newType, gresult.type(), null); - return result; + return convertArguments(gresult, newType, gresult.type(), 0); } static @@ -802,6 +879,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; this.target = target; this.fallback = fallback; } + // FIXME: Build the control flow out of foldArguments. static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) { MethodType type = target.type(); int nargs = type.parameterCount(); @@ -809,12 +887,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; MethodHandle invoke = INVOKES[nargs]; MethodType gtype = type.generic(); assert(invoke.type().dropParameterTypes(0,1) == gtype); - MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), null); - MethodHandle gtarget = convertArguments(target, gtype, type, null); - MethodHandle gfallback = convertArguments(fallback, gtype, type, null); + MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), 0); + MethodHandle gtarget = convertArguments(target, gtype, type, 0); + MethodHandle gfallback = convertArguments(fallback, gtype, type, 0); if (gtest == null || gtarget == null || gfallback == null) return null; MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback); - return convertArguments(gguard, type, gtype, null); + return convertArguments(gguard, type, gtype, 0); } else { MethodHandle invoke = VARARGS_INVOKE; MethodType gtype = MethodType.genericMethodType(1); @@ -925,8 +1003,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; GuardWithCatch(MethodHandle target, Class exType, MethodHandle catcher) { this(INVOKES[target.type().parameterCount()], target, exType, catcher); } - GuardWithCatch(MethodHandle invoker, - MethodHandle target, Class exType, MethodHandle catcher) { + // FIXME: Build the control flow out of foldArguments. + GuardWithCatch(MethodHandle invoker, + MethodHandle target, Class exType, MethodHandle catcher) { super(invoker); this.target = target; this.exType = exType; @@ -1057,11 +1136,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (nargs < GuardWithCatch.INVOKES.length) { MethodType gtype = type.generic(); MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); - MethodHandle gtarget = convertArguments(target, gtype, type, null); - MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, null); + MethodHandle gtarget = convertArguments(target, gtype, type, 0); + MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 0); MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher); if (gtarget == null || gcatcher == null || gguard == null) return null; - return convertArguments(gguard, type, gtype, null); + return convertArguments(gguard, type, gtype, 0); } else { MethodType gtype = MethodType.genericMethodType(0, true); MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); diff --git a/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/share/classes/java/lang/invoke/MethodHandleNatives.java index 82d5d898638f7e4a3a6969fbae7857b973bdc460..40272644e286f1f7d039f8bb28a9191db6a42097 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -115,6 +115,8 @@ class MethodHandleNatives { /** Which conv-ops are implemented by the JVM? */ static final int CONV_OP_IMPLEMENTED_MASK; + /** Derived mode flag. Only false on some old JVM implementations. */ + static final boolean HAVE_RICOCHET_FRAMES; private static native void registerNatives(); static { @@ -141,6 +143,7 @@ class MethodHandleNatives { if (CONV_OP_IMPLEMENTED_MASK_ == 0) CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK; CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_; + HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<int, Object->T) OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive - OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI) + OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg) OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg) OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS) OP_DROP_ARGS = 0x9, // remove one or more argument slots - OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI) + OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size) - OP_FLYBY = 0xC, // operate first on reified argument list (NYI) - OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI) + OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result + //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration /** Shift and mask values for decoding the AMH.conversion field. * These numbers are shared with the JVM for creating AMHs. */ static final int CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field + CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK @@ -244,8 +248,9 @@ class MethodHandleNatives { T_LONG = 11, T_OBJECT = 12, //T_ARRAY = 13 - T_VOID = 14; + T_VOID = 14, //T_ADDRESS = 15 + T_ILLEGAL = 99; /** * Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries. @@ -273,16 +278,29 @@ class MethodHandleNatives { try { Field con = Constants.class.getDeclaredField(name); int jval = con.getInt(null); - if (jval != vmval) - throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval); + if (jval == vmval) continue; + String err = (name+": JVM has "+vmval+" while Java has "+jval); + if (name.equals("CONV_OP_LIMIT")) { + System.err.println("warning: "+err); + continue; + } + throw new InternalError(err); } catch (Exception ex) { + if (ex instanceof NoSuchFieldException) { + String err = (name+": JVM has "+vmval+" which Java does not define"); + // ignore exotic ops the JVM cares about; we just wont issue them + if (name.startsWith("OP_") || name.startsWith("GC_")) { + System.err.println("warning: "+err); + continue; + } + } throw new InternalError(name+": access failed, got "+ex); } } return true; } static { - verifyConstants(); + assert(verifyConstants()); } // Up-calls from the JVM. @@ -313,7 +331,7 @@ class MethodHandleNatives { } /** - * The JVM wants to use a MethodType with invokeGeneric. Give the runtime fair warning. + * The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning. */ static void notifyGenericMethodType(MethodType type) { type.form().notifyGenericMethodType(); @@ -323,15 +341,39 @@ class MethodHandleNatives { * The JVM wants to raise an exception. Here's the path. */ static void raiseException(int code, Object actual, Object required) { - String message; + String message = null; + switch (code) { + case 190: // arraylength + try { + String reqLength = ""; + if (required instanceof AdapterMethodHandle) { + int conv = ((AdapterMethodHandle)required).getConversion(); + int spChange = AdapterMethodHandle.extractStackMove(conv); + reqLength = " of length "+(spChange+1); + } + int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual); + message = "required array"+reqLength+", but encountered wrong length "+actualLength; + break; + } catch (IllegalArgumentException ex) { + } + required = Object[].class; // should have been an array + code = 192; // checkcast + break; + } // disregard the identity of the actual object, if it is not a class: - if (!(actual instanceof Class) && !(actual instanceof MethodType)) - actual = actual.getClass(); - if (actual != null) - message = "required "+required+" but encountered "+actual; - else - message = "required "+required; + if (message == null) { + if (!(actual instanceof Class) && !(actual instanceof MethodType)) + actual = actual.getClass(); + if (actual != null) + message = "required "+required+" but encountered "+actual; + else + message = "required "+required; + } switch (code) { + case 190: // arraylength + throw new ArrayIndexOutOfBoundsException(message); + case 50: //_aaload + throw new ClassCastException(message); case 192: // checkcast throw new ClassCastException(message); default: @@ -365,4 +407,13 @@ class MethodHandleNatives { throw err; } } + + /** + * This assertion marks code which was written before ricochet frames were implemented. + * Such code will go away when the ports catch up. + */ + static boolean workaroundWithoutRicochetFrames() { + assert(!HAVE_RICOCHET_FRAMES) : "this code should not be executed if `-XX:+UseRicochetFrames is enabled"; + return true; + } } diff --git a/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/src/share/classes/java/lang/invoke/MethodHandleStatics.java index 6ea8f978b63f1c4b22cd3f74b3e61d40ca8ef073..3eeeec71ac354ee93acb02b3030b78821e8c0e83 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java +++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java @@ -63,8 +63,17 @@ package java.lang.invoke; } static void checkSpreadArgument(Object av, int n) { - if (av == null ? n != 0 : ((Object[])av).length != n) - throw newIllegalArgumentException("Array is not of length "+n); + if (av == null) { + if (n == 0) return; + } else if (av instanceof Object[]) { + int len = ((Object[])av).length; + if (len == n) return; + } else { + int len = java.lang.reflect.Array.getLength(av); + if (len == n) return; + } + // fall through to error: + throw newIllegalArgumentException("Array is not of length "+n); } // handy shared exception makers (they simplify the common case code) @@ -80,6 +89,9 @@ package java.lang.invoke; /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) { return new IllegalArgumentException(message(message, obj)); } + /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) { + return new IllegalArgumentException(message(message, obj, obj2)); + } /*non-public*/ static Error uncaughtException(Exception ex) { Error err = new InternalError("uncaught exception"); err.initCause(ex); @@ -89,4 +101,8 @@ package java.lang.invoke; if (obj != null) message = message + ": " + obj; return message; } + private static String message(String message, Object obj, Object obj2) { + if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2; + return message; + } } diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java index 12e36da623a4c47bf59289f74e8f49bc1e642996..99985a09feec3aa5c6498b2a7f9080354252df19 100644 --- a/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/share/classes/java/lang/invoke/MethodHandles.java @@ -190,7 +190,7 @@ public class MethodHandles { * is not symbolically accessible from the lookup class's loader, * the lookup can still succeed. * For example, lookups for {@code MethodHandle.invokeExact} and - * {@code MethodHandle.invokeGeneric} will always succeed, regardless of requested type. + * {@code MethodHandle.invoke} will always succeed, regardless of requested type. *
  • If there is a security manager installed, it can forbid the lookup * on various grounds (see below). * By contrast, the {@code ldc} instruction is not subject to @@ -590,10 +590,10 @@ public class MethodHandles { * Because of the general equivalence between {@code invokevirtual} * instructions and method handles produced by {@code findVirtual}, * if the class is {@code MethodHandle} and the name string is - * {@code invokeExact} or {@code invokeGeneric}, the resulting + * {@code invokeExact} or {@code invoke}, the resulting * method handle is equivalent to one produced by * {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or - * {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker} + * {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker} * with the same {@code type} argument. * * @param refc the class or interface from which the method is accessed @@ -1080,7 +1080,7 @@ return mh1; MethodType rawType = mh.type(); if (rawType.parameterType(0) == caller) return mh; MethodType narrowType = rawType.changeParameterType(0, caller); - MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, null); + MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0); return fixVarargs(narrowMH, mh); } @@ -1148,7 +1148,7 @@ return mh1; *
  • an {@code Object[]} array containing more arguments * *

    - * The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} with + * The invoker will behave 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} @@ -1166,7 +1166,7 @@ return mh1; *

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

    -MethodHandle invoker = MethodHandles.genericInvoker(type);
    +MethodHandle invoker = MethodHandles.invoker(type);
     int spreadArgCount = type.parameterCount - objectArgCount;
     invoker = invoker.asSpreader(Object[].class, spreadArgCount);
     return invoker;
    @@ -1186,7 +1186,7 @@ return invoker;
     
         /**
          * Produces a special invoker method handle which can be used to
    -     * invoke any method handle of the given type, as if by {@code invokeExact}.
    +     * invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
          * The resulting invoker will have a type which is
          * exactly equal to the desired type, except that it will accept
          * an additional leading argument of type {@code MethodHandle}.
    @@ -1203,7 +1203,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
          * For example, to emulate an {@code invokeExact} call to a variable method
          * handle {@code M}, extract its type {@code T},
          * look up the invoker method {@code X} for {@code T},
    -     * and call the invoker method, as {@code X.invokeGeneric(T, A...)}.
    +     * and call the invoker method, as {@code X.invoke(T, A...)}.
          * (It would not work to call {@code X.invokeExact}, since the type {@code T}
          * is unknown.)
          * If spreading, collecting, or other argument transformations are required,
    @@ -1212,7 +1212,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
          * 

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

    * This method throws no reflective or security exceptions. @@ -1226,20 +1226,20 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type) /** * Produces a special invoker method handle which can be used to - * invoke any method handle of the given type, as if by {@code invokeGeneric}. + * invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}. * The resulting invoker will have a type which is * 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 - * necessary and unbox and widen primitive arguments, as if by {@link #convertArguments convertArguments}. - * 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. + * 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}. *

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

    -publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
    +publicLookup().findVirtual(MethodHandle.class, "invoke", type)
          * 
    *

    * This method throws no reflective or security exceptions. @@ -1247,8 +1247,17 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type) * @return a method handle suitable for invoking any method handle convertible to the given type */ static public + MethodHandle invoker(MethodType type) { + return type.invokers().generalInvoker(); + } + + /** + * 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 type.invokers().genericInvoker(); + return invoker(type); } /** @@ -1368,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type) */ public static MethodHandle convertArguments(MethodHandle target, MethodType newType) { - MethodType oldType = target.type(); - if (oldType.equals(newType)) - return target; - MethodHandle res = null; - try { - res = MethodHandleImpl.convertArguments(target, - newType, oldType, null); - } catch (IllegalArgumentException ex) { - } - if (res == null) - throw new WrongMethodTypeException("cannot convert to "+newType+": "+target); - return res; + return MethodHandleImpl.convertArguments(target, newType, 1); } /** @@ -1422,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type) */ public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { - return convertArguments(target, newType); // FIXME! + return MethodHandleImpl.convertArguments(target, newType, 2); } /* @@ -1517,23 +1515,32 @@ assert((int)twice.invokeExact(21) == 42); MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { MethodType oldType = target.type(); checkReorder(reorder, newType, oldType); - return MethodHandleImpl.convertArguments(target, + return MethodHandleImpl.permuteArguments(target, newType, oldType, reorder); } private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) { + if (newType.returnType() != oldType.returnType()) + throw newIllegalArgumentException("return types do not match", + oldType, newType); if (reorder.length == oldType.parameterCount()) { int limit = newType.parameterCount(); boolean bad = false; - for (int i : reorder) { + for (int j = 0; j < reorder.length; j++) { + int i = reorder[j]; if (i < 0 || i >= limit) { bad = true; break; } + Class src = newType.parameterType(i); + Class dst = oldType.parameterType(j); + if (src != dst) + throw newIllegalArgumentException("parameter types do not match after reorder", + oldType, newType); } if (!bad) return; } - throw newIllegalArgumentException("bad reorder array"); + throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder)); } /** @@ -1622,7 +1629,7 @@ assert((int)twice.invokeExact(21) == 42); if (type == void.class) throw newIllegalArgumentException("void type"); Wrapper w = Wrapper.forPrimitiveType(type); - return identity(type).bindTo(w.convert(value, type)); + return insertArguments(identity(type), 0, w.convert(value, type)); } else { return identity(type).bindTo(type.cast(value)); } @@ -1857,7 +1864,8 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { MethodType targetType = target.type(); MethodHandle adapter = target; - MethodType adapterType = targetType; + MethodType adapterType = null; + assert((adapterType = targetType) != null); int maxPos = targetType.parameterCount(); if (pos + filters.length > maxPos) throw newIllegalArgumentException("too many filters"); @@ -1865,19 +1873,23 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY for (MethodHandle filter : filters) { curPos += 1; if (filter == null) continue; // ignore null elements of filters - MethodType filterType = filter.type(); - if (filterType.parameterCount() != 1 - || filterType.returnType() != targetType.parameterType(curPos)) - throw newIllegalArgumentException("target and filter types do not match"); - adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0)); - adapter = MethodHandleImpl.filterArgument(adapter, curPos, filter); + adapter = filterArgument(adapter, curPos, filter); + assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null); } - MethodType midType = adapter.type(); - if (midType != adapterType) - adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null); + assert(adapterType.equals(adapter.type())); return adapter; } + /*non-public*/ static + MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) { + MethodType targetType = target.type(); + MethodType filterType = filter.type(); + if (filterType.parameterCount() != 1 + || filterType.returnType() != targetType.parameterType(pos)) + throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); + return MethodHandleImpl.filterArgument(target, pos, filter); + } + /** * Adapts a target method handle {@code target} by post-processing * its return value with a unary filter function. @@ -1913,14 +1925,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { MethodType targetType = target.type(); MethodType filterType = filter.type(); - if (filterType.parameterCount() != 1 - || filterType.parameterType(0) != targetType.returnType()) - throw newIllegalArgumentException("target and filter types do not match"); + Class rtype = targetType.returnType(); + int filterValues = filterType.parameterCount(); + if (filterValues == 0 + ? (rtype != void.class) + : (rtype != filterType.parameterType(0))) + throw newIllegalArgumentException("target and filter types do not match", target, filter); // result = fold( lambda(retval, arg...) { filter(retval) }, // lambda( arg...) { target(arg...) } ) + MethodType newType = targetType.changeReturnType(filterType.returnType()); + MethodHandle result = null; + if (AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false)) { + result = AdapterMethodHandle.makeCollectArguments(filter, target, 0, false); + if (result != null) return result; + } // FIXME: Too many nodes here. - MethodHandle returner = dropArguments(filter, 1, targetType.parameterList()); - return foldArguments(returner, target); + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList()); + result = foldArguments(returner, target); + assert(result.type().equals(newType)); + return result; } /** @@ -1972,16 +1996,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { MethodType targetType = target.type(); MethodType combinerType = combiner.type(); + int foldPos = 0; // always at the head, at present int foldArgs = combinerType.parameterCount(); - boolean ok = (targetType.parameterCount() >= 1 + foldArgs); - if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1))) + int foldVals = combinerType.returnType() == void.class ? 0 : 1; + int afterInsertPos = foldPos + foldVals; + boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs); + if (ok && !(combinerType.parameterList() + .equals(targetType.parameterList().subList(afterInsertPos, + afterInsertPos + foldArgs)))) ok = false; - if (ok && !combinerType.returnType().equals(targetType.parameterType(0))) + if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0))) ok = false; if (!ok) throw misMatchedTypes("target and combiner types", targetType, combinerType); - MethodType newType = targetType.dropParameterTypes(0, 1); - return MethodHandleImpl.foldArguments(target, newType, combiner); + MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos); + MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner); + if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType); + return res; } /** @@ -2142,7 +2173,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * 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.invokeGeneric}. + * {@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}. diff --git a/src/share/classes/java/lang/invoke/MethodType.java b/src/share/classes/java/lang/invoke/MethodType.java index a7fa147fa3296a195e49b6cd8f81f9580dcec100..02c12c69aad61c037576bbb92e4458dd2e43eff8 100644 --- a/src/share/classes/java/lang/invoke/MethodType.java +++ b/src/share/classes/java/lang/invoke/MethodType.java @@ -25,6 +25,7 @@ package java.lang.invoke; +import sun.invoke.util.Wrapper; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -39,7 +40,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * matched between a method handle and all its callers, * and the JVM's operations enforce this matching at, specifically * during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact} - * and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution + * and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution * of {@code invokedynamic} instructions. *

    * The structure is a return type accompanied by any number of parameter types. @@ -262,18 +263,18 @@ class MethodType implements java.io.Serializable { * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All parameters and the return type will be {@code Object}, - * except the final varargs parameter if any, which will be {@code Object[]}. - * @param objectArgCount number of parameters (excluding the varargs parameter if any) - * @param varargs whether there will be a varargs parameter, of type {@code Object[]} - * @return a totally generic method type, given only its count of parameters and varargs - * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 + * except the final array parameter if any, which will be {@code Object[]}. + * @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}) * @see #genericMethodType(int) */ public static - MethodType genericMethodType(int objectArgCount, boolean varargs) { + MethodType genericMethodType(int objectArgCount, boolean finalArray) { MethodType mt; checkSlotCount(objectArgCount); - int ivarargs = (!varargs ? 0 : 1); + int ivarargs = (!finalArray ? 0 : 1); int ootIndex = objectArgCount*2 + ivarargs; if (ootIndex < objectOnlyTypes.length) { mt = objectOnlyTypes[ootIndex]; @@ -294,7 +295,7 @@ class MethodType implements java.io.Serializable { * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All parameters and the return type will be Object. * @param objectArgCount number of parameters - * @return a totally generic method type, given only its count of parameters + * @return a generally applicable method type, for all calls of the given argument count * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 * @see #genericMethodType(int, boolean) */ @@ -626,6 +627,30 @@ class MethodType implements java.io.Serializable { return sb.toString(); } + + /*non-public*/ + boolean isConvertibleTo(MethodType newType) { + if (!canConvert(returnType(), newType.returnType())) + return false; + int argc = parameterCount(); + if (argc != newType.parameterCount()) + return false; + for (int i = 0; i < argc; i++) { + if (!canConvert(newType.parameterType(i), parameterType(i))) + return false; + } + 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))) + return false; + } + return true; + } + /// Queries which have to do with the bytecode architecture /** Reports the number of JVM stack slots required to invoke a method diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java index db73b24d06759f937e4ca3102c4bfcfaa46ef4e1..6ad4a5a8eead889f155d34e134ebb2ac9c8849b9 100644 --- a/src/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java @@ -46,6 +46,7 @@ class MethodTypeForm { final long argCounts; // packed slot & value counts final long primCounts; // packed prim & double counts final int vmslots; // total number of parameter slots + private Object vmlayout; // vm-specific information for calls final MethodType erasedType; // the canonical erasure /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers @@ -59,7 +60,7 @@ class MethodTypeForm { /*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with /*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly - /*lazy*/ MethodHandle genericInvoker; // hook for invokeGeneric + /*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke public MethodType erasedType() { return erasedType; @@ -460,9 +461,9 @@ class MethodTypeForm { if (genericInvoker != null) return; try { // Trigger adapter creation. - genericInvoker = InvokeGeneric.genericInvokerOf(erasedType); + genericInvoker = InvokeGeneric.generalInvokerOf(erasedType); } catch (Exception ex) { - Error err = new InternalError("Exception while resolving invokeGeneric"); + Error err = new InternalError("Exception while resolving inexact invoke"); err.initCause(ex); throw err; } diff --git a/src/share/classes/java/lang/invoke/SpreadGeneric.java b/src/share/classes/java/lang/invoke/SpreadGeneric.java index a862723e0250d8aa1322e77a9c742dd3a271b4eb..be1606673815e8cc0e16c3042ac80e02705e0a97 100644 --- a/src/share/classes/java/lang/invoke/SpreadGeneric.java +++ b/src/share/classes/java/lang/invoke/SpreadGeneric.java @@ -66,6 +66,10 @@ class SpreadGeneric { this.entryPoint = ep[0]; } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + /** From targetType remove the last spreadCount arguments, and instead * append a simple Object argument. */ diff --git a/src/share/classes/java/lang/invoke/ToGeneric.java b/src/share/classes/java/lang/invoke/ToGeneric.java index 22723975d5bfbe158fa08407d9bb5f573c9e1d5c..2d46a3910e1c44aea329f7ed6bcd4bc4e9590cb3 100644 --- a/src/share/classes/java/lang/invoke/ToGeneric.java +++ b/src/share/classes/java/lang/invoke/ToGeneric.java @@ -96,7 +96,7 @@ class ToGeneric { ToGeneric va2 = ToGeneric.of(primsAtEnd); this.adapter = va2.adapter; if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType); - this.entryPoint = MethodHandleImpl.convertArguments( + this.entryPoint = MethodHandleImpl.permuteArguments( va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder); // example: for entryType of (int,Object,Object), the reordered // type is (Object,Object,int) and the order is {1,2,0}, @@ -128,7 +128,7 @@ class ToGeneric { assert(eptWithInts.parameterType(i) == int.class); MethodType nextType = midType.changeParameterType(i, int.class); rawEntryPoint = MethodHandleImpl.convertArguments( - rawEntryPoint, nextType, midType, null); + rawEntryPoint, nextType, midType, 0); midType = nextType; } } @@ -152,6 +152,10 @@ class ToGeneric { this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType); } + static { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated + } + /** A generic argument list will be created by a call of type 'raw'. * The values need to be reboxed for to match 'cooked'. * Do this on the fly. @@ -171,7 +175,7 @@ class ToGeneric { invoker.type().generic(), invoker, 0, MethodHandle.class); if (filteredInvoker == null) throw new UnsupportedOperationException("NYI"); } - MethodHandle reboxer = ValueConversions.rebox(dst, false); + MethodHandle reboxer = ValueConversions.rebox(dst); filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker); if (filteredInvoker == null) throw new InternalError(); } @@ -199,13 +203,13 @@ class ToGeneric { assert(!rret.isPrimitive()); if (rret == Object.class && !mustCast) return null; - return ValueConversions.cast(tret, false); + return ValueConversions.cast(tret); } else if (tret == rret) { - return ValueConversions.unbox(tret, false); + return ValueConversions.unbox(tret); } else { assert(rret.isPrimitive()); assert(tret == double.class ? rret == long.class : rret == int.class); - return ValueConversions.unboxRaw(tret, false); + return ValueConversions.unboxRaw(tret); } } @@ -311,7 +315,7 @@ class ToGeneric { } static Adapter buildAdapterFromBytecodes(MethodType entryPointType) { - throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException("NYI: "+entryPointType); } /** diff --git a/src/share/classes/java/lang/invoke/package-info.java b/src/share/classes/java/lang/invoke/package-info.java index b6a1992dca874c6f6ad1bb25eb4ddc25a43e253d..8ec18c461febdb3cea6287cbefaccedb630db7f8 100644 --- a/src/share/classes/java/lang/invoke/package-info.java +++ b/src/share/classes/java/lang/invoke/package-info.java @@ -185,7 +185,7 @@ * 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 invokeGeneric}. + * 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. *

    @@ -243,7 +243,7 @@ *

  • optionally, one or more additional static arguments
  • * * The method handle is then applied to the other values as if by - * {@link java.lang.invoke.MethodHandle#invokeGeneric invokeGeneric}. + * {@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 * derived from the dynamic call site's type descriptor and passed to @@ -251,7 +251,7 @@ * The call site then becomes permanently linked to the dynamic call site. *

    * As long as each bootstrap method can be correctly invoked - * by invokeGeneric, its detailed type is arbitrary. + * 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}. @@ -272,7 +272,7 @@ * (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 invokeGeneric} to throw {@code WrongMethodTypeException}
  • + * 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 @@ -381,10 +381,10 @@ * 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.) - * The bootstrap method will be invoked as if by either {@code invokeGeneric} + * The bootstrap method will be invoked as if by either {@code MethodHandle.invoke} * or {@code invokeWithArguments}. (There is no way to tell the difference.) *

    - * The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments. + * The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments. * For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion. * If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set), * then some or all of the arguments specified here may be collected into a trailing array parameter. @@ -419,8 +419,8 @@ * For example, the fourth argument could be {@code MethodHandle}, * if that is the type of the corresponding constant in * the {@code CONSTANT_InvokeDynamic} entry. - * In that case, the {@code invokeGeneric} call will pass the extra method handle - * constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric} + * In that case, the {@code MethodHandle.invoke} call will pass the extra method handle + * constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke} * will cast the reference back to {@code MethodHandle} before invoking the bootstrap method. * (If a string constant were passed instead, by badly generated code, that cast would then fail, * resulting in a {@code BootstrapMethodError}.) diff --git a/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html b/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html index 721bdb2e702798a527903c7ec047d01b966ff48f..5d3b0ea5e7d573b247a6db56734d47bcd3e7bc1f 100644 --- a/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html +++ b/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html @@ -211,6 +211,20 @@ to any listeners.

    +Optional ColorSpace support: +Handling of PhotoYCC (YCC), PhotoYCCA (YCCA), RGBA and YCbCrA color spaces +by the standard plugin, as described below, is dependent on capabilities +of the libraries used to interpret the JPEG data. Thus all consequential +behaviors are optional. If the support is not available when decoding, +the color space will be treated as unrecognized and the appropriate +default color space for the specified number of component channels +may be used. +When writing, an Exception may be thrown if no suitable conversion +can be applied before encoding. +But where the support for these color spaces is available, the behavior +must be as documented. +

    + When reading, the contents of the stream are interpreted by the usual JPEG conventions, as follows: @@ -241,8 +255,11 @@ JPEG conventions, as follows: 2-channel images are assumed to be grayscale with an alpha channel. For 3- and 4-channel images, the component ids are consulted. If these values are 1-3 for a 3-channel image, then the image is assumed to be - YCbCr. If these values are 1-4 for a 4-channel image, then the image - is assumed to be YCbCrA. If these values are > 4, they are checked + YCbCr. Subject to the availability of the + optional color space support + described above, if these values are 1-4 for a 4-channel image, + then the image is assumed to be YCbCrA. + If these values are > 4, they are checked against the ASCII codes for 'R', 'G', 'B', 'A', 'C', 'c'. These can encode the following colorspaces:

    @@ -346,12 +363,16 @@ If no metadata object is specified, then the following defaults apply: component ids in the frame and scan headers are set to 1, 2, and 3. -

  • RGBA images are converted to YCbCrA, subsampled in the +
  • Subject to the optional library support + described above, + RGBA images are converted to YCbCrA, subsampled in the chrominance channels by half both vertically and horizontally, and written without any special marker segments. The component ids in the frame and scan headers are set to 1, 2, 3, and 4. -
  • PhotoYCC and YCCAimages are subsampled by half in the chrominance +
  • Subject to the optional library support + described above, + PhotoYCC and YCCAimages are subsampled by half in the chrominance channels both vertically and horizontally and written with an Adobe APP14 marker segment and 'Y','C', and 'c' (and 'A' if an alpha channel is present) as component ids in the frame @@ -433,6 +454,8 @@ in the frame header node of the metadata object, regardless of color space.)
  • RGBA images: + Subject to the optional library support + described above,
    • If an app0JFIF node is present in the metadata object, it is ignored and a warning is sent to listeners, as JFIF does not @@ -456,6 +479,8 @@ in the frame header node of the metadata object, regardless of color space.)
  • PhotoYCC Images: + Subject to the optional library support + described above,
    • If an app0JFIF node is present in the metadata object, the image is converted to sRGB, and then to YCbCr during encoding, @@ -471,6 +496,8 @@ in the frame header node of the metadata object, regardless of color space.)
  • PhotoYCCA Images: + Subject to the optional library support + described above,
    • If an app0JFIF node is present in the metadata object, it is ignored and a warning is sent to listeners, as JFIF does not diff --git a/src/share/classes/javax/swing/ComboBoxModel.java b/src/share/classes/javax/swing/ComboBoxModel.java index 78ac525ec7dbc8df6abd9f151647d20daa1f3015..4b657daf88135732789aed26dc8eb47b8e2ed30b 100644 --- a/src/share/classes/javax/swing/ComboBoxModel.java +++ b/src/share/classes/javax/swing/ComboBoxModel.java @@ -33,9 +33,11 @@ package javax.swing; * ListModel. This disjoint behavior allows for the temporary * storage and retrieval of a selected item in the model. * + * @param the type of the elements of this model + * * @author Arnaud Weber */ -public interface ComboBoxModel extends ListModel { +public interface ComboBoxModel extends ListModel { /** * Set the selected item. The implementation of this method should notify diff --git a/src/share/classes/javax/swing/DefaultComboBoxModel.java b/src/share/classes/javax/swing/DefaultComboBoxModel.java index f7cc50b1a7078353c83313560000d462e9b5e762..f0266fc73511f1edfdcb234a4af96a47ea696e60 100644 --- a/src/share/classes/javax/swing/DefaultComboBoxModel.java +++ b/src/share/classes/javax/swing/DefaultComboBoxModel.java @@ -24,39 +24,28 @@ */ package javax.swing; -import java.beans.*; import java.util.*; -import java.awt.*; -import java.awt.event.*; - import java.io.Serializable; -import java.io.ObjectOutputStream; -import java.io.ObjectInputStream; -import java.io.IOException; - -import javax.swing.event.*; -import javax.swing.plaf.*; -import javax.swing.border.*; - -import javax.accessibility.*; /** * The default model for combo boxes. * + * @param the type of the elements of this model + * * @author Arnaud Weber * @author Tom Santos */ -public class DefaultComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable { - Vector objects; +public class DefaultComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable { + Vector objects; Object selectedObject; /** * Constructs an empty DefaultComboBoxModel object. */ public DefaultComboBoxModel() { - objects = new Vector(); + objects = new Vector(); } /** @@ -65,8 +54,8 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo * * @param items an array of Object objects */ - public DefaultComboBoxModel(final Object items[]) { - objects = new Vector(); + public DefaultComboBoxModel(final E items[]) { + objects = new Vector(); objects.ensureCapacity( items.length ); int i,c; @@ -84,7 +73,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo * * @param v a Vector object ... */ - public DefaultComboBoxModel(Vector v) { + public DefaultComboBoxModel(Vector v) { objects = v; if ( getSize() > 0 ) { @@ -117,7 +106,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo } // implements javax.swing.ListModel - public Object getElementAt(int index) { + public E getElementAt(int index) { if ( index >= 0 && index < objects.size() ) return objects.elementAt(index); else @@ -136,7 +125,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo } // implements javax.swing.MutableComboBoxModel - public void addElement(Object anObject) { + public void addElement(E anObject) { objects.addElement(anObject); fireIntervalAdded(this,objects.size()-1, objects.size()-1); if ( objects.size() == 1 && selectedObject == null && anObject != null ) { @@ -145,7 +134,7 @@ public class DefaultComboBoxModel extends AbstractListModel implements MutableCo } // implements javax.swing.MutableComboBoxModel - public void insertElementAt(Object anObject,int index) { + public void insertElementAt(E anObject,int index) { objects.insertElementAt(anObject,index); fireIntervalAdded(this, index, index); } diff --git a/src/share/classes/javax/swing/JComboBox.java b/src/share/classes/javax/swing/JComboBox.java index b4a414bbf1aa29374263999cec935c0b62fd1fb7..8ac710910a84ead4e0e1007e23a9eb05de2515c2 100644 --- a/src/share/classes/javax/swing/JComboBox.java +++ b/src/share/classes/javax/swing/JComboBox.java @@ -69,6 +69,8 @@ import javax.accessibility.*; * @see ComboBoxModel * @see DefaultComboBoxModel * + * @param the type of the elements of this combo box + * * @beaninfo * attribute: isContainer false * description: A combination of a text field and a drop-down list. @@ -76,7 +78,7 @@ import javax.accessibility.*; * @author Arnaud Weber * @author Mark Davidson */ -public class JComboBox extends JComponent +public class JComboBox extends JComponent implements ItemSelectable,ListDataListener,ActionListener, Accessible { /** * @see #getUIClassID @@ -91,7 +93,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @see #getModel * @see #setModel */ - protected ComboBoxModel dataModel; + protected ComboBoxModel dataModel; /** * This protected field is implementation specific. Do not access directly * or override. Use the accessor methods instead. @@ -99,7 +101,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @see #getRenderer * @see #setRenderer */ - protected ListCellRenderer renderer; + protected ListCellRenderer renderer; /** * This protected field is implementation specific. Do not access directly * or override. Use the accessor methods instead. @@ -156,7 +158,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ protected Object selectedItemReminder = null; - private Object prototypeDisplayValue; + private E prototypeDisplayValue; // Flag to ensure that infinite loops do not occur with ActionEvents. private boolean firingActionEvent = false; @@ -175,7 +177,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * displayed list of items * @see DefaultComboBoxModel */ - public JComboBox(ComboBoxModel aModel) { + public JComboBox(ComboBoxModel aModel) { super(); setModel(aModel); init(); @@ -189,9 +191,9 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @param items an array of objects to insert into the combo box * @see DefaultComboBoxModel */ - public JComboBox(final Object items[]) { + public JComboBox(E[] items) { super(); - setModel(new DefaultComboBoxModel(items)); + setModel(new DefaultComboBoxModel(items)); init(); } @@ -203,9 +205,9 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @param items an array of vectors to insert into the combo box * @see DefaultComboBoxModel */ - public JComboBox(Vector items) { + public JComboBox(Vector items) { super(); - setModel(new DefaultComboBoxModel(items)); + setModel(new DefaultComboBoxModel(items)); init(); } @@ -219,7 +221,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ public JComboBox() { super(); - setModel(new DefaultComboBoxModel()); + setModel(new DefaultComboBoxModel()); init(); } @@ -263,7 +265,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { public void updateUI() { setUI((ComboBoxUI)UIManager.getUI(this)); - ListCellRenderer renderer = getRenderer(); + ListCellRenderer renderer = getRenderer(); if (renderer instanceof Component) { SwingUtilities.updateComponentTreeUI((Component)renderer); } @@ -302,8 +304,8 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * bound: true * description: Model that the combo box uses to get data to display. */ - public void setModel(ComboBoxModel aModel) { - ComboBoxModel oldModel = dataModel; + public void setModel(ComboBoxModel aModel) { + ComboBoxModel oldModel = dataModel; if (oldModel != null) { oldModel.removeListDataListener(this); } @@ -322,7 +324,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @return the ComboBoxModel that provides the displayed * list of items */ - public ComboBoxModel getModel() { + public ComboBoxModel getModel() { return dataModel; } @@ -461,8 +463,8 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * expert: true * description: The renderer that paints the item selected in the list. */ - public void setRenderer(ListCellRenderer aRenderer) { - ListCellRenderer oldRenderer = renderer; + public void setRenderer(ListCellRenderer aRenderer) { + ListCellRenderer oldRenderer = renderer; renderer = aRenderer; firePropertyChange( "renderer", oldRenderer, renderer ); invalidate(); @@ -475,7 +477,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @return the ListCellRenderer that displays * the selected item. */ - public ListCellRenderer getRenderer() { + public ListCellRenderer getRenderer() { return renderer; } @@ -558,7 +560,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { // will be rejected. boolean found = false; for (int i = 0; i < dataModel.getSize(); i++) { - Object element = dataModel.getElementAt(i); + E element = dataModel.getElementAt(i); if (anObject.equals(element)) { found = true; objectToSelect = element; @@ -640,7 +642,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { public int getSelectedIndex() { Object sObject = dataModel.getSelectedItem(); int i,c; - Object obj; + E obj; for ( i=0,c=dataModel.getSize();i * - * @param anObject the Object to add to the list + * @param item the item to add to the list * @see MutableComboBoxModel */ - public void addItem(Object anObject) { + public void addItem(E item) { checkMutableComboBoxModel(); - ((MutableComboBoxModel)dataModel).addElement(anObject); + ((MutableComboBoxModel)dataModel).addElement(item); } /** @@ -721,14 +723,14 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * This method works only if the JComboBox uses a * mutable data model. * - * @param anObject the Object to add to the list + * @param item the item to add to the list * @param index an integer specifying the position at which * to add the item * @see MutableComboBoxModel */ - public void insertItemAt(Object anObject, int index) { + public void insertItemAt(E item, int index) { checkMutableComboBoxModel(); - ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index); + ((MutableComboBoxModel)dataModel).insertElementAt(item,index); } /** @@ -756,7 +758,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ public void removeItemAt(int anIndex) { checkMutableComboBoxModel(); - ((MutableComboBoxModel)dataModel).removeElementAt( anIndex ); + ((MutableComboBoxModel)dataModel).removeElementAt( anIndex ); } /** @@ -764,7 +766,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { */ public void removeAllItems() { checkMutableComboBoxModel(); - MutableComboBoxModel model = (MutableComboBoxModel)dataModel; + MutableComboBoxModel model = (MutableComboBoxModel)dataModel; int size = model.getSize(); if ( model instanceof DefaultComboBoxModel ) { @@ -772,7 +774,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { } else { for ( int i = 0; i < size; ++i ) { - Object element = model.getElementAt( 0 ); + E element = model.getElementAt( 0 ); model.removeElement( element ); } } @@ -1188,11 +1190,11 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { private static class ComboBoxActionPropertyChangeListener - extends ActionPropertyChangeListener { - ComboBoxActionPropertyChangeListener(JComboBox b, Action a) { + extends ActionPropertyChangeListener> { + ComboBoxActionPropertyChangeListener(JComboBox b, Action a) { super(b, a); } - protected void actionPropertyChanged(JComboBox cb, + protected void actionPropertyChanged(JComboBox cb, Action action, PropertyChangeEvent e) { if (AbstractAction.shouldReconfigure(e)) { @@ -1454,10 +1456,10 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * * @param index an integer indicating the list position, where the first * item starts at zero - * @return the Object at that list position; or + * @return the item at that list position; or * null if out of range */ - public Object getItemAt(int index) { + public E getItemAt(int index) { return dataModel.getElementAt(index); } diff --git a/src/share/classes/javax/swing/MutableComboBoxModel.java b/src/share/classes/javax/swing/MutableComboBoxModel.java index 03b75631af93c3548e9cdf68f7c5469968e125b8..90f9474a7ea95b3fcb1b537a120899a8ba5bb74e 100644 --- a/src/share/classes/javax/swing/MutableComboBoxModel.java +++ b/src/share/classes/javax/swing/MutableComboBoxModel.java @@ -27,19 +27,21 @@ package javax.swing; /** * A mutable version of ComboBoxModel. * + * @param the type of the elements of this model + * * @author Tom Santos */ -public interface MutableComboBoxModel extends ComboBoxModel { +public interface MutableComboBoxModel extends ComboBoxModel { /** * Adds an item at the end of the model. The implementation of this method * should notify all registered ListDataListeners that the * item has been added. * - * @param obj the Object to be added + * @param item the item to be added */ - public void addElement( Object obj ); + public void addElement( E item ); /** * Removes an item from the model. The implementation of this method should @@ -55,17 +57,17 @@ public interface MutableComboBoxModel extends ComboBoxModel { * should notify all registered ListDataListeners that the * item has been added. * - * @param obj the Object to be added + * @param item the item to be added * @param index location to add the object */ - public void insertElementAt( Object obj, int index ); + public void insertElementAt( E item, int index ); /** * Removes an item at a specific index. The implementation of this method * should notify all registered ListDataListeners that the * item has been removed. * - * @param index location of object to be removed + * @param index location of the item to be removed */ public void removeElementAt( int index ); } diff --git a/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java b/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java index f34b8d1684c7c87547da66ee868c4f2519da5399..23252d77a581e073d02e7b2fb3e95ba16d8500cf 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java @@ -40,7 +40,7 @@ import sun.awt.shell.ShellFolder; * * @author Jeff Dinkins */ -public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener { +public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener { private JFileChooser filechooser = null; // PENDING(jeff) pick the size more sensibly diff --git a/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java b/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java index e36a849dce4cf43311501e63205e6e20c90b6105..b3d38477bee511662be623ed47cc1dbcff95b779 100644 --- a/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -906,7 +906,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI { /** * Data model for a type-face selection combo-box. */ - protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { + protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { Vector directories = new Vector(); int[] depths = null; File selectedDirectory = null; @@ -1063,7 +1063,7 @@ public class MetalFileChooserUI extends BasicFileChooserUI { /** * Data model for a type-face selection combo-box. */ - protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { + protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { protected FileFilter[] filters; protected FilterComboBoxModel() { super(); diff --git a/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java b/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java index 84a773ab300bdd74c1574616b61588f6fd0429b6..0686967412d6c8b60cae2856292d5fdd615659a3 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java @@ -488,6 +488,18 @@ public class SynthTabbedPaneUI extends BasicTabbedPaneUI paintContentBorder(tabContentContext, g, tabPlacement, selectedIndex); } + protected void paintTabArea(Graphics g, int tabPlacement, + int selectedIndex) { + // This can be invoked from ScrollabeTabPanel + Insets insets = tabPane.getInsets(); + int x = insets.left; + int y = insets.top; + int width = tabPane.getWidth() - insets.left - insets.right; + int height = tabPane.getHeight() - insets.top - insets.bottom; + + paintTabArea(tabAreaContext, g, tabPlacement, selectedIndex, + new Rectangle(x, y, width, height)); + } private void paintTabArea(SynthContext ss, Graphics g, int tabPlacement, int selectedIndex, diff --git a/src/share/classes/sun/invoke/util/ValueConversions.java b/src/share/classes/sun/invoke/util/ValueConversions.java index c6a56abf665ffe54b28f82831e6c73099c74cfc7..06e9a278daa6a8ce81c24d2c0adbfc8c9366abd9 100644 --- a/src/share/classes/sun/invoke/util/ValueConversions.java +++ b/src/share/classes/sun/invoke/util/ValueConversions.java @@ -31,10 +31,15 @@ import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumMap; import java.util.List; public class ValueConversions { + private static final Class THIS_CLASS = ValueConversions.class; + // Do not adjust this except for special platforms: + private static final int MAX_ARITY = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255); + private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); private static EnumMap[] newWrapperCaches(int n) { @@ -42,88 +47,101 @@ public class ValueConversions { EnumMap[] caches = (EnumMap[]) new EnumMap[n]; // unchecked warning expected here for (int i = 0; i < n; i++) - caches[i] = new EnumMap(Wrapper.class); + caches[i] = new EnumMap<>(Wrapper.class); return caches; } /// Converting references to values. - static int unboxInteger(Object x) { - if (x == null) return 0; // never NPE - return ((Integer) x).intValue(); + // There are several levels of this unboxing conversions: + // no conversions: exactly Integer.valueOf, etc. + // implicit conversions sanctioned by JLS 5.1.2, etc. + // explicit conversions as allowed by explicitCastArguments + + static int unboxInteger(Object x, boolean cast) { + if (x instanceof Integer) + return ((Integer) x).intValue(); + return primitiveConversion(Wrapper.INT, x, cast).intValue(); } - static byte unboxByte(Object x) { - if (x == null) return 0; // never NPE - return ((Byte) x).byteValue(); + static byte unboxByte(Object x, boolean cast) { + if (x instanceof Byte) + return ((Byte) x).byteValue(); + return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); } - static short unboxShort(Object x) { - if (x == null) return 0; // never NPE - return ((Short) x).shortValue(); + static short unboxShort(Object x, boolean cast) { + if (x instanceof Short) + return ((Short) x).shortValue(); + return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); } - static boolean unboxBoolean(Object x) { - if (x == null) return false; // never NPE - return ((Boolean) x).booleanValue(); + static boolean unboxBoolean(Object x, boolean cast) { + if (x instanceof Boolean) + return ((Boolean) x).booleanValue(); + return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; } - static char unboxCharacter(Object x) { - if (x == null) return 0; // never NPE - return ((Character) x).charValue(); + static char unboxCharacter(Object x, boolean cast) { + if (x instanceof Character) + return ((Character) x).charValue(); + return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); } - static long unboxLong(Object x) { - if (x == null) return 0; // never NPE - return ((Long) x).longValue(); + static long unboxLong(Object x, boolean cast) { + if (x instanceof Long) + return ((Long) x).longValue(); + return primitiveConversion(Wrapper.LONG, x, cast).longValue(); } - static float unboxFloat(Object x) { - if (x == null) return 0; // never NPE - return ((Float) x).floatValue(); + static float unboxFloat(Object x, boolean cast) { + if (x instanceof Float) + return ((Float) x).floatValue(); + return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); } - static double unboxDouble(Object x) { - if (x == null) return 0; // never NPE - return ((Double) x).doubleValue(); + static double unboxDouble(Object x, boolean cast) { + if (x instanceof Double) + return ((Double) x).doubleValue(); + return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); } /// Converting references to "raw" values. /// A raw primitive value is always an int or long. - static int unboxByteRaw(Object x) { - return unboxByte(x); + static int unboxByteRaw(Object x, boolean cast) { + return unboxByte(x, cast); } - static int unboxShortRaw(Object x) { - return unboxShort(x); + static int unboxShortRaw(Object x, boolean cast) { + return unboxShort(x, cast); } - static int unboxBooleanRaw(Object x) { - return unboxBoolean(x) ? 1 : 0; + static int unboxBooleanRaw(Object x, boolean cast) { + return unboxBoolean(x, cast) ? 1 : 0; } - static int unboxCharacterRaw(Object x) { - return unboxCharacter(x); + static int unboxCharacterRaw(Object x, boolean cast) { + return unboxCharacter(x, cast); } - static int unboxFloatRaw(Object x) { - return Float.floatToIntBits(unboxFloat(x)); + static int unboxFloatRaw(Object x, boolean cast) { + return Float.floatToIntBits(unboxFloat(x, cast)); } - static long unboxDoubleRaw(Object x) { - return Double.doubleToRawLongBits(unboxDouble(x)); + static long unboxDoubleRaw(Object x, boolean cast) { + return Double.doubleToRawLongBits(unboxDouble(x, cast)); } private static MethodType unboxType(Wrapper wrap, boolean raw) { - return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType()); + return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class); } private static final EnumMap[] UNBOX_CONVERSIONS = newWrapperCaches(4); - private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) { - EnumMap cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)]; + private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) { + EnumMap cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -136,7 +154,7 @@ public class ValueConversions { mh = raw ? ALWAYS_ZERO : IGNORE; break; case INT: case LONG: // these guys don't need separate raw channels - if (raw) mh = unbox(wrap, exact, false); + if (raw) mh = unbox(wrap, false, cast); break; } if (mh != null) { @@ -146,37 +164,62 @@ public class ValueConversions { // look up the method String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : ""); MethodType type = unboxType(wrap, raw); - if (!exact) { - try { - // actually, type is wrong; the Java method takes Object - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } else { - mh = unbox(wrap, !exact, raw).asType(type); + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + } catch (ReflectiveOperationException ex) { + mh = null; } if (mh != null) { + mh = MethodHandles.insertArguments(mh, 1, cast); cache.put(wrap, mh); return mh; } - throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : "")); + throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + + (cast ? " (cast)" : "") + (raw ? " (raw)" : "")); } - public static MethodHandle unbox(Wrapper type, boolean exact) { - return unbox(type, exact, false); + public static MethodHandle unboxCast(Wrapper type) { + return unbox(type, false, true); } - public static MethodHandle unboxRaw(Wrapper type, boolean exact) { - return unbox(type, exact, true); + public static MethodHandle unboxRaw(Wrapper type) { + return unbox(type, true, false); } - public static MethodHandle unbox(Class type, boolean exact) { - return unbox(Wrapper.forPrimitiveType(type), exact, false); + public static MethodHandle unbox(Class type) { + return unbox(Wrapper.forPrimitiveType(type), false, false); } - public static MethodHandle unboxRaw(Class type, boolean exact) { - return unbox(Wrapper.forPrimitiveType(type), exact, true); + public static MethodHandle unboxCast(Class type) { + return unbox(Wrapper.forPrimitiveType(type), false, true); + } + + public static MethodHandle unboxRaw(Class type) { + return unbox(Wrapper.forPrimitiveType(type), true, false); + } + + /// 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(); + } + if (x instanceof Number) { + res = (Number) x; + } else if (x instanceof Boolean) { + res = ((boolean)x ? 1 : 0); + } 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()))) + // this will fail with the required ClassCastException: + res = (Number) wrap.wrapperType().cast(x); + return res; } /// Converting primitives to references @@ -285,7 +328,7 @@ public class ValueConversions { MethodType type = boxType(wrap, raw); if (exact) { try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); } catch (ReflectiveOperationException ex) { mh = null; } @@ -296,22 +339,31 @@ public class ValueConversions { cache.put(wrap, mh); return mh; } - throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : "")); + throw new IllegalArgumentException("cannot find box adapter for " + + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : "")); } - public static MethodHandle box(Class type, boolean exact) { + public static MethodHandle box(Class type) { + boolean exact = false; + // e.g., boxShort(short)Short if exact, + // e.g., boxShort(short)Object if !exact return box(Wrapper.forPrimitiveType(type), exact, false); } - public static MethodHandle boxRaw(Class type, boolean exact) { + public static MethodHandle boxRaw(Class type) { + boolean exact = false; + // e.g., boxShortRaw(int)Short if exact + // e.g., boxShortRaw(int)Object if !exact return box(Wrapper.forPrimitiveType(type), exact, true); } - public static MethodHandle box(Wrapper type, boolean exact) { + public static MethodHandle box(Wrapper type) { + boolean exact = false; return box(type, exact, false); } - public static MethodHandle boxRaw(Wrapper type, boolean exact) { + public static MethodHandle boxRaw(Wrapper type) { + boolean exact = false; return box(type, exact, true); } @@ -319,16 +371,16 @@ public class ValueConversions { static int unboxRawInteger(Object x) { if (x instanceof Integer) - return unboxInteger(x); + return (int) x; else - return (int) unboxLong(x); + return (int) unboxLong(x, false); } static Integer reboxRawInteger(Object x) { if (x instanceof Integer) return (Integer) x; else - return (int) unboxLong(x); + return (int) unboxLong(x, false); } static Byte reboxRawByte(Object x) { @@ -362,7 +414,7 @@ public class ValueConversions { static Double reboxRawDouble(Object x) { if (x instanceof Double) return (Double) x; - return boxDoubleRaw(unboxLong(x)); + return boxDoubleRaw(unboxLong(x, true)); } private static MethodType reboxType(Wrapper wrap) { @@ -371,7 +423,7 @@ public class ValueConversions { } private static final EnumMap[] - REBOX_CONVERSIONS = newWrapperCaches(2); + REBOX_CONVERSIONS = newWrapperCaches(1); /** * Because we normalize primitive types to reduce the number of signatures, @@ -380,10 +432,10 @@ public class ValueConversions { * When the erased primitive value is then boxed into an Integer or Long, * the final boxed primitive is sometimes required. This transformation * is called a "rebox". It takes an Integer or Long and produces some - * other boxed value. + * other boxed value, typed (inexactly) as an Object */ - public static MethodHandle rebox(Wrapper wrap, boolean exact) { - EnumMap cache = REBOX_CONVERSIONS[exact?1:0]; + public static MethodHandle rebox(Wrapper wrap) { + EnumMap cache = REBOX_CONVERSIONS[0]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -402,14 +454,11 @@ public class ValueConversions { // look up the method String name = "reboxRaw" + wrap.simpleName(); MethodType type = reboxType(wrap); - if (exact) { - try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } else { - mh = rebox(wrap, !exact).asType(IDENTITY.type()); + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + mh = mh.asType(IDENTITY.type()); + } catch (ReflectiveOperationException ex) { + mh = null; } if (mh != null) { cache.put(wrap, mh); @@ -418,8 +467,8 @@ public class ValueConversions { throw new IllegalArgumentException("cannot find rebox adapter for " + wrap); } - public static MethodHandle rebox(Class type, boolean exact) { - return rebox(Wrapper.forPrimitiveType(type), exact); + public static MethodHandle rebox(Class type) { + return rebox(Wrapper.forPrimitiveType(type)); } /// Width-changing conversions between int and long. @@ -486,9 +535,10 @@ public class ValueConversions { case VOID: mh = EMPTY; break; + case OBJECT: case INT: case LONG: case FLOAT: case DOUBLE: try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type); } catch (ReflectiveOperationException ex) { mh = null; } @@ -592,7 +642,7 @@ public class ValueConversions { return t.cast(x); } - private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY; + private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY; static { try { MethodType idType = MethodType.genericMethodType(1); @@ -600,40 +650,56 @@ public class ValueConversions { MethodType alwaysZeroType = idType.changeReturnType(int.class); MethodType ignoreType = idType.changeReturnType(void.class); MethodType zeroObjectType = MethodType.genericMethodType(0); - IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType); - IDENTITY_I = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(int.class, int.class)); - IDENTITY_J = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(long.class, long.class)); + IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType); + IDENTITY_I = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(int.class, int.class)); + IDENTITY_J = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(long.class, long.class)); //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); - CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType); - ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType); - ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType); - ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType); - IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType); - EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1)); - } catch (Exception ex) { + CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType); + ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType); + ALWAYS_ZERO = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysZero", alwaysZeroType); + ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType); + IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType); + EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); + NEW_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "newArray", MethodType.methodType(Object[].class, int.class)); + } catch (NoSuchMethodException | IllegalAccessException ex) { Error err = new InternalError("uncaught exception"); err.initCause(ex); throw err; } } - private static final EnumMap WRAPPER_CASTS - = new EnumMap(Wrapper.class); + // Varargs methods need to be in a separately initialized class, to bootstrapping problems. + static class LazyStatics { + private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST; + static { + try { + //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class)); + COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class)); + COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class)); + MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class)); + } catch (ReflectiveOperationException ex) { + Error err = new InternalError("uncaught exception"); + err.initCause(ex); + throw err; + } + } + } - private static final EnumMap EXACT_WRAPPER_CASTS - = new EnumMap(Wrapper.class); + private static final EnumMap[] WRAPPER_CASTS + = newWrapperCaches(2); /** Return a method that casts its sole argument (an Object) to the given type * and returns it as the given type (if exact is true), or as plain Object (if erase is true). */ - public static MethodHandle cast(Class type, boolean exact) { + public static MethodHandle cast(Class type) { + boolean exact = false; if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type); MethodHandle mh = null; Wrapper wrap = null; EnumMap cache = null; if (Wrapper.isWrapperType(type)) { wrap = Wrapper.forWrapperType(type); - cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS); + cache = WRAPPER_CASTS[exact?1:0]; mh = cache.get(wrap); if (mh != null) return mh; } @@ -673,7 +739,7 @@ public class ValueConversions { if (wrap != Wrapper.VOID) type = type.appendParameterTypes(wrap.primitiveType()); try { - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type); + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type); } catch (ReflectiveOperationException ex) { mh = null; } @@ -692,6 +758,210 @@ public class ValueConversions { throw new IllegalArgumentException("cannot find identity for " + wrap); } + /// Float/non-float conversions. + + static float doubleToFloat(double x) { + return (float) x; + } + static double floatToDouble(float x) { + return x; + } + + // narrow double to integral type + static long doubleToLong(double x) { + return (long) x; + } + static int doubleToInt(double x) { + return (int) x; + } + static short doubleToShort(double x) { + return (short) x; + } + static char doubleToChar(double x) { + return (char) x; + } + static byte doubleToByte(double x) { + return (byte) x; + } + static boolean doubleToBoolean(double x) { + return toBoolean((byte) x); + } + + // narrow float to integral type + static long floatToLong(float x) { + return (long) x; + } + static int floatToInt(float x) { + return (int) x; + } + static short floatToShort(float x) { + return (short) x; + } + static char floatToChar(float x) { + return (char) x; + } + static byte floatToByte(float x) { + return (byte) x; + } + static boolean floatToBoolean(float x) { + return toBoolean((byte) x); + } + + // widen integral type to double + static double longToDouble(long x) { + return x; + } + static double intToDouble(int x) { + return x; + } + static double shortToDouble(short x) { + return x; + } + static double charToDouble(char x) { + return x; + } + static double byteToDouble(byte x) { + return x; + } + static double booleanToDouble(boolean x) { + return fromBoolean(x); + } + + // widen integral type to float + static float longToFloat(long x) { + return x; + } + static float intToFloat(int x) { + return x; + } + static float shortToFloat(short x) { + return x; + } + static float charToFloat(char x) { + return x; + } + static float byteToFloat(byte x) { + return x; + } + static float booleanToFloat(boolean x) { + return fromBoolean(x); + } + + static boolean toBoolean(byte x) { + // see javadoc for MethodHandles.explicitCastArguments + return ((x & 1) != 0); + } + static byte fromBoolean(boolean x) { + // see javadoc for MethodHandles.explicitCastArguments + return (x ? (byte)1 : (byte)0); + } + + private static final EnumMap[] + CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4); + + static MethodHandle convertFloatFunction(Wrapper wrap, boolean toFloat, boolean doubleSize) { + EnumMap cache = CONVERT_FLOAT_FUNCTIONS[(toFloat?1:0)+(doubleSize?2:0)]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + Wrapper fwrap = (doubleSize ? Wrapper.DOUBLE : Wrapper.FLOAT); + Class fix = wrap.primitiveType(); + Class flt = (doubleSize ? double.class : float.class); + Class src = toFloat ? fix : flt; + Class dst = toFloat ? flt : fix; + if (src == dst) return identity(wrap); + MethodType type = MethodType.methodType(dst, src); + switch (wrap) { + case VOID: + mh = toFloat ? zeroConstantFunction(fwrap) : MethodHandles.dropArguments(EMPTY, 0, flt); + break; + case OBJECT: + mh = toFloat ? unbox(flt) : box(flt); + break; + default: + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type); + } catch (ReflectiveOperationException ex) { + mh = null; + } + break; + } + if (mh != null) { + assert(mh.type() == type) : mh; + cache.put(wrap, mh); + return mh; + } + + throw new IllegalArgumentException("cannot find float conversion constant for " + + src.getSimpleName()+" -> "+dst.getSimpleName()); + } + + public static MethodHandle convertFromFloat(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, false, false); + } + public static MethodHandle convertFromDouble(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, false, true); + } + public static MethodHandle convertToFloat(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, true, false); + } + public static MethodHandle convertToDouble(Class fixType) { + Wrapper wrap = Wrapper.forPrimitiveType(fixType); + return convertFloatFunction(wrap, true, true); + } + + private static String capitalize(String x) { + return Character.toUpperCase(x.charAt(0))+x.substring(1); + } + + /// Collection of multiple arguments. + + public static Object convertArrayElements(Class arrayType, Object array) { + Class src = array.getClass().getComponentType(); + Class dst = arrayType.getComponentType(); + if (src == null || dst == null) throw new IllegalArgumentException("not array type"); + Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null); + Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null); + int length; + if (sw == null) { + Object[] a = (Object[]) array; + length = a.length; + if (dw == null) + return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class)); + Object res = dw.makeArray(length); + dw.copyArrayUnboxing(a, 0, res, 0, length); + return res; + } + length = java.lang.reflect.Array.getLength(array); + Object[] res; + if (dw == null) { + res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class)); + } else { + res = new Object[length]; + } + sw.copyArrayBoxing(array, 0, res, 0, length); + if (dw == null) return res; + Object a = dw.makeArray(length); + dw.copyArrayUnboxing(res, 0, a, 0, length); + return a; + } + + private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { + MethodType type = MethodType.genericMethodType(nargs) + .changeReturnType(rtype) + .insertParameterTypes(0, ptypes); + try { + return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + } catch (ReflectiveOperationException ex) { + return null; + } + } + private static final Object[] NO_ARGS_ARRAY = {}; private static Object[] makeArray(Object... args) { return args; } private static Object[] array() { return NO_ARGS_ARRAY; } @@ -723,36 +993,176 @@ public class ValueConversions { Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - static MethodHandle[] makeArrays() { - ArrayList arrays = new ArrayList(); - MethodHandles.Lookup lookup = IMPL_LOOKUP; + private static MethodHandle[] makeArrays() { + ArrayList mhs = new ArrayList<>(); for (;;) { - int nargs = arrays.size(); - MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class); - String name = "array"; - MethodHandle array = null; - try { - array = lookup.findStatic(ValueConversions.class, name, type); - } catch (ReflectiveOperationException ex) { - } - if (array == null) break; - arrays.add(array); + MethodHandle mh = findCollector("array", mhs.size(), Object[].class); + if (mh == null) break; + mhs.add(mh); } - assert(arrays.size() == 11); // current number of methods - return arrays.toArray(new MethodHandle[0]); + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[MAX_ARITY+1]); + } + private static final MethodHandle[] ARRAYS = makeArrays(); + + // mh-fill versions of the above: + private static Object[] newArray(int len) { return new Object[len]; } + private static void fillWithArguments(Object[] a, int pos, Object... args) { + System.arraycopy(args, 0, a, pos, args.length); + } + // using Integer pos instead of int pos to avoid bootstrapping problems + private static Object[] fillArray(Object[] a, Integer pos, Object a0) + { fillWithArguments(a, pos, a0); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1) + { fillWithArguments(a, pos, a0, a1); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2) + { fillWithArguments(a, pos, a0, a1, a2); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3) + { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } + private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } + private static MethodHandle[] makeFillArrays() { + ArrayList mhs = new ArrayList<>(); + mhs.add(null); // there is no empty fill; at least a0 is required + for (;;) { + MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Object[].class, Integer.class); + if (mh == null) break; + mhs.add(mh); + } + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[0]); + } + private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); + + private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { + return Arrays.copyOf(a, a.length, arrayType); + } + private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { + Object a = w.makeArray(boxes.length); + w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); + return a; } - static final MethodHandle[] ARRAYS = makeArrays(); /** Return a method handle that takes the indicated number of Object * arguments and returns an Object array of them, as if for varargs. */ public static MethodHandle varargsArray(int nargs) { - if (nargs < ARRAYS.length) - return ARRAYS[nargs]; - // else need to spin bytecode or do something else fancy - throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs); + MethodHandle mh = ARRAYS[nargs]; + if (mh != null) return mh; + mh = findCollector("array", nargs, Object[].class); + if (mh != null) return ARRAYS[nargs] = mh; + MethodHandle producer = filler(0); // identity function produces result + return ARRAYS[nargs] = buildVarargsArray(producer, nargs); + } + + private static MethodHandle buildVarargsArray(MethodHandle producer, int nargs) { + // Build up the result mh as a sequence of fills like this: + // producer(fill(fill(fill(newArray(23),0,x1..x10),10,x11..x20),20,x21..x23)) + // The various fill(_,10*I,___*[J]) are reusable. + MethodHandle filler = filler(nargs); + MethodHandle mh = producer; + mh = MethodHandles.dropArguments(mh, 1, filler.type().parameterList()); + mh = MethodHandles.foldArguments(mh, filler); + mh = MethodHandles.foldArguments(mh, buildNewArray(nargs)); + return mh; } + private static MethodHandle buildNewArray(int nargs) { + return MethodHandles.insertArguments(NEW_ARRAY, 0, (int) nargs); + } + + private static final MethodHandle[] FILLERS = new MethodHandle[MAX_ARITY+1]; + // filler(N).invoke(a, arg0..arg[N-1]) fills a[0]..a[N-1] + private static MethodHandle filler(int nargs) { + MethodHandle filler = FILLERS[nargs]; + if (filler != null) return filler; + return FILLERS[nargs] = buildFiller(nargs); + } + private static MethodHandle buildFiller(int nargs) { + if (nargs == 0) + return MethodHandles.identity(Object[].class); + final int CHUNK = (FILL_ARRAYS.length - 1); + int rightLen = nargs % CHUNK; + int leftLen = nargs - rightLen; + if (rightLen == 0) { + leftLen = nargs - (rightLen = CHUNK); + if (FILLERS[leftLen] == null) { + // build some precursors from left to right + for (int j = 0; j < leftLen; j += CHUNK) filler(j); + } + } + MethodHandle leftFill = filler(leftLen); // recursive fill + MethodHandle rightFill = FILL_ARRAYS[rightLen]; + rightFill = MethodHandles.insertArguments(rightFill, 1, (int) leftLen); // [leftLen..nargs-1] + + // Combine the two fills: right(left(newArray(nargs), x1..x20), x21..x23) + MethodHandle mh = filler(0); // identity function produces result + mh = MethodHandles.dropArguments(mh, 1, rightFill.type().parameterList()); + mh = MethodHandles.foldArguments(mh, rightFill); + if (leftLen > 0) { + mh = MethodHandles.dropArguments(mh, 1, leftFill.type().parameterList()); + mh = MethodHandles.foldArguments(mh, leftFill); + } + return mh; + } + + // Type-polymorphic version of varargs maker. + private static final ClassValue TYPED_COLLECTORS + = new ClassValue() { + protected MethodHandle[] computeValue(Class type) { + return new MethodHandle[256]; + } + }; + + /** Return a method handle that takes the indicated number of + * typed arguments and returns an array of them. + * The type argument is the array type. + */ + public static MethodHandle varargsArray(Class arrayType, int nargs) { + Class elemType = arrayType.getComponentType(); + if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); + // FIXME: Need more special casing and caching here. + if (elemType == Object.class) + return varargsArray(nargs); + // other cases: primitive arrays, subtypes of Object[] + MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); + MethodHandle mh = nargs < cache.length ? cache[nargs] : null; + if (mh != null) return mh; + MethodHandle producer = buildArrayProducer(arrayType); + mh = buildVarargsArray(producer, nargs); + mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); + cache[nargs] = mh; + return mh; + } + + private static MethodHandle buildArrayProducer(Class arrayType) { + Class elemType = arrayType.getComponentType(); + if (elemType.isPrimitive()) + return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType)); + else + return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType); + } + + // List version of varargs maker. + private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); private static List makeList(Object... args) { return Arrays.asList(args); } private static List list() { return NO_ARGS_LIST; } @@ -784,34 +1194,29 @@ public class ValueConversions { Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - static MethodHandle[] makeLists() { - ArrayList arrays = new ArrayList(); - MethodHandles.Lookup lookup = IMPL_LOOKUP; + private static MethodHandle[] makeLists() { + ArrayList mhs = new ArrayList<>(); for (;;) { - int nargs = arrays.size(); - MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class); - String name = "list"; - MethodHandle array = null; - try { - array = lookup.findStatic(ValueConversions.class, name, type); - } catch (ReflectiveOperationException ex) { - } - if (array == null) break; - arrays.add(array); + MethodHandle mh = findCollector("list", mhs.size(), List.class); + if (mh == null) break; + mhs.add(mh); } - assert(arrays.size() == 11); // current number of methods - return arrays.toArray(new MethodHandle[0]); + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[MAX_ARITY+1]); } - static final MethodHandle[] LISTS = makeLists(); + private static final MethodHandle[] LISTS = makeLists(); /** Return a method handle that takes the indicated number of Object - * arguments and returns List. + * arguments and returns a List. */ public static MethodHandle varargsList(int nargs) { - if (nargs < LISTS.length) - return LISTS[nargs]; - // else need to spin bytecode or do something else fancy - throw new UnsupportedOperationException("NYI"); + MethodHandle mh = LISTS[nargs]; + if (mh != null) return mh; + mh = findCollector("list", nargs, List.class); + if (mh != null) return LISTS[nargs] = mh; + return LISTS[nargs] = buildVarargsList(nargs); + } + private static MethodHandle buildVarargsList(int nargs) { + return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST); } } - diff --git a/src/share/classes/sun/invoke/util/VerifyType.java b/src/share/classes/sun/invoke/util/VerifyType.java index 39286d09ca4aa26433111ce7d81227f94e15afd5..2970135c1b04ec024aebd5ef441016341a1627ae 100644 --- a/src/share/classes/sun/invoke/util/VerifyType.java +++ b/src/share/classes/sun/invoke/util/VerifyType.java @@ -54,9 +54,15 @@ public class VerifyType { if (dst == void.class) return true; // drop any return value if (isNullType(src)) return !dst.isPrimitive(); if (!src.isPrimitive()) return dst.isAssignableFrom(src); + if (!dst.isPrimitive()) return false; // Verifier allows an int to carry byte, short, char, or even boolean: - if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt(); - return false; + Wrapper sw = Wrapper.forPrimitiveType(src); + if (dst == int.class) return sw.isSubwordOrInt(); + Wrapper dw = Wrapper.forPrimitiveType(dst); + if (!sw.isSubwordOrInt()) return false; + if (!dw.isSubwordOrInt()) return false; + if (!dw.isSigned() && sw.isSigned()) return false; + return dw.bitWidth() > sw.bitWidth(); } /** @@ -155,6 +161,7 @@ public class VerifyType { return -1; // truncation may be required if (!dw.isSigned() && sw.isSigned()) return -1; // sign elimination may be required + return 1; } if (src == float.class || dst == float.class) { if (src == double.class || dst == double.class) diff --git a/src/share/classes/sun/invoke/util/Wrapper.java b/src/share/classes/sun/invoke/util/Wrapper.java index 8e2ce57838607f90450b4054a770b60cad278e56..4b3179a3e883e1efac15df6a511357d41ce3f6ee 100644 --- a/src/share/classes/sun/invoke/util/Wrapper.java +++ b/src/share/classes/sun/invoke/util/Wrapper.java @@ -26,37 +26,47 @@ package sun.invoke.util; public enum Wrapper { - BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), + BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)), // These must be in the order defined for widening primitive conversions in JLS 5.1.2 - BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)), - SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)), - CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)), - INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), - LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), - FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)), - DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)), - //NULL(Null.class, null.class, 'N', null, Format.other(1)), - OBJECT(Object.class, Object.class, 'L', null, Format.other(1)), + BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)), + SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)), + CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)), + INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)), + LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)), + FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)), + DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)), + //NULL(Null.class, null.class, 'N', null, null, Format.other(1)), + OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)), // VOID must be the last type, since it is "assignable" from any other type: - VOID(Void.class, void.class, 'V', null, Format.other(0)), + VOID(Void.class, void.class, 'V', null, null, Format.other(0)), ; private final Class wrapperType; private final Class primitiveType; private final char basicTypeChar; private final Object zero; + private final Object emptyArray; private final int format; private final String simpleName; - private Wrapper(Class wtype, Class ptype, char tchar, Object zero, int format) { + private Wrapper(Class wtype, Class ptype, char tchar, Object zero, Object emptyArray, int format) { this.wrapperType = wtype; this.primitiveType = ptype; this.basicTypeChar = tchar; this.zero = zero; + this.emptyArray = emptyArray; this.format = format; this.simpleName = wtype.getSimpleName(); } + /** For debugging, give the details of this wrapper. */ + public String detailString() { + return simpleName+ + java.util.Arrays.asList(wrapperType, primitiveType, + basicTypeChar, zero, + "0x"+Integer.toHexString(format)); + } + private static abstract class Format { static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12; static final int @@ -114,16 +124,18 @@ public enum Wrapper { public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; } /** Is the wrapped type either float or double? */ public boolean isFloating() { return format >= Format.FLOAT; } + /** Is the wrapped type either void or a reference? */ + public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; } - /** Does the JVM verifier allow a variable of this wrapper's + /** Does the JLS 5.1.2 allow a variable of this wrapper's * primitive type to be assigned from a value of the given wrapper's primitive type? * Cases: *
        *
      • unboxing followed by widening primitive conversion - *
      • any type converted to {@code void} + *
      • any type converted to {@code void} (i.e., dropping a method call's value) *
      • boxing conversion followed by widening reference conversion to {@code Object} - *
      • conversion of {@code boolean} to any type *
      + * These are the cases allowed by MethodHandle.asType and convertArguments. */ public boolean isConvertibleFrom(Wrapper source) { if (this == source) return true; @@ -131,13 +143,75 @@ public enum Wrapper { // At best, this is a narrowing conversion. return false; } - if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) { - assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT); + // All conversions are allowed in the enum order between floats and signed ints. + // First detect non-signed non-float types (boolean, char, Object, void). + boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0); + if (!floatOrSigned) { + if (this.isOther()) return true; + // can convert char to int or wider, but nothing else + if (source.format == Format.CHAR) return true; + // no other conversions are classified as widening return false; } + // All signed and float conversions in the enum order are widening. + assert(this.isFloating() || this.isSigned()); + assert(source.isFloating() || source.isSigned()); return true; } + static { assert(checkConvertibleFrom()); } + private static boolean checkConvertibleFrom() { + // Check the matrix for correct classification of widening conversions. + for (Wrapper w : values()) { + assert(w.isConvertibleFrom(w)); + assert(VOID.isConvertibleFrom(w)); + if (w != VOID) { + assert(OBJECT.isConvertibleFrom(w)); + assert(!w.isConvertibleFrom(VOID)); + } + // check relations with unsigned integral types: + if (w != CHAR) { + assert(!CHAR.isConvertibleFrom(w)); + if (!w.isConvertibleFrom(INT)) + assert(!w.isConvertibleFrom(CHAR)); + } + if (w != BOOLEAN) { + assert(!BOOLEAN.isConvertibleFrom(w)); + if (w != VOID && w != OBJECT) + assert(!w.isConvertibleFrom(BOOLEAN)); + } + // check relations with signed integral types: + if (w.isSigned()) { + for (Wrapper x : values()) { + if (w == x) continue; + if (x.isFloating()) + assert(!w.isConvertibleFrom(x)); + else if (x.isSigned()) { + if (w.compareTo(x) < 0) + assert(!w.isConvertibleFrom(x)); + else + assert(w.isConvertibleFrom(x)); + } + } + } + // check relations with floating types: + if (w.isFloating()) { + for (Wrapper x : values()) { + if (w == x) continue; + if (x.isSigned()) + assert(w.isConvertibleFrom(x)); + else if (x.isFloating()) { + if (w.compareTo(x) < 0) + assert(!w.isConvertibleFrom(x)); + else + assert(w.isConvertibleFrom(x)); + } + } + } + } + return true; // i.e., assert(true) + } + /** Produce a zero value for the given wrapper type. * This will be a numeric zero for a number or character, * false for a boolean, and null for a reference or void. @@ -549,7 +623,7 @@ public enum Wrapper { } private static boolean boolValue(long bits) { - //bits &= 1; // simple 31-bit zero extension + bits &= 1; // simple 31-bit zero extension return (bits != 0); } @@ -559,4 +633,31 @@ public enum Wrapper { private static RuntimeException newIllegalArgumentException(String message) { return new IllegalArgumentException(message); } + + // primitive array support + public Object makeArray(int len) { + return java.lang.reflect.Array.newInstance(primitiveType, len); + } + public Class arrayType() { + return emptyArray.getClass(); + } + public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) { + if (a.getClass() != arrayType()) + arrayType().cast(a); // throw NPE or CCE if bad type + for (int i = 0; i < length; i++) { + Object value = values[i+vpos]; + value = convert(value, primitiveType); + java.lang.reflect.Array.set(a, i+apos, value); + } + } + public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) { + if (a.getClass() != arrayType()) + arrayType().cast(a); // throw NPE or CCE if bad type + for (int i = 0; i < length; i++) { + Object value = java.lang.reflect.Array.get(a, i+apos); + //Already done: value = convert(value, primitiveType); + assert(value.getClass() == wrapperType); + values[i+vpos] = value; + } + } } diff --git a/src/share/classes/sun/java2d/opengl/OGLRenderer.java b/src/share/classes/sun/java2d/opengl/OGLRenderer.java index df9377e69e5628eb316ee24261ec127354590af7..935c68c170e3d556cdff994da7bc44545a4d2821 100644 --- a/src/share/classes/sun/java2d/opengl/OGLRenderer.java +++ b/src/share/classes/sun/java2d/opengl/OGLRenderer.java @@ -102,15 +102,20 @@ class OGLRenderer extends BufferedRenderPipe { final ParallelogramPipe realpipe = oglr.getAAParallelogramPipe(); return new ParallelogramPipe() { public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("OGLFillAAParallelogram"); realpipe.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -118,6 +123,7 @@ class OGLRenderer extends BufferedRenderPipe { { GraphicsPrimitive.tracePrimitive("OGLDrawAAParallelogram"); realpipe.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2, lw1, lw2); } @@ -166,21 +172,29 @@ class OGLRenderer extends BufferedRenderPipe { oglr.fillSpans(sg2d, si, transx, transy); } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("OGLFillParallelogram"); - oglr.fillParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2); + oglr.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2) { GraphicsPrimitive.tracePrimitive("OGLDrawParallelogram"); - oglr.drawParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2, lw1, lw2); + oglr.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2, lw1, lw2); } public void copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) diff --git a/src/share/classes/sun/java2d/pipe/AAShapePipe.java b/src/share/classes/sun/java2d/pipe/AAShapePipe.java index 6547f0e234e9b4a79916b1b4b30b5f45f75eb4d7..eaa821d90228ebf5e35437dc0007e9da339b09f0 100644 --- a/src/share/classes/sun/java2d/pipe/AAShapePipe.java +++ b/src/share/classes/sun/java2d/pipe/AAShapePipe.java @@ -68,21 +68,23 @@ public class AAShapePipe renderPath(sg, s, null); } - private static Rectangle2D computeBBox(double x, double y, - double dx1, double dy1, - double dx2, double dy2) + private static Rectangle2D computeBBox(double ux1, double uy1, + double ux2, double uy2) { - double lox, loy, hix, hiy; - lox = hix = x; - loy = hiy = y; - if (dx1 < 0) { lox += dx1; } else { hix += dx1; } - if (dy1 < 0) { loy += dy1; } else { hiy += dy1; } - if (dx2 < 0) { lox += dx2; } else { hix += dx2; } - if (dy2 < 0) { loy += dy2; } else { hiy += dy2; } - return new Rectangle2D.Double(lox, loy, hix-lox, hiy-loy); + if ((ux2 -= ux1) < 0) { + ux1 += ux2; + ux2 = -ux2; + } + if ((uy2 -= uy1) < 0) { + uy1 += uy2; + uy2 = -uy2; + } + return new Rectangle2D.Double(ux1, uy1, ux2, uy2); } public void fillParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -97,10 +99,12 @@ public class AAShapePipe return; } - renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox); + renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox); } public void drawParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -118,7 +122,7 @@ public class AAShapePipe // Note that bbox is of the original shape, not the wide path. // This is appropriate for handing to Paint methods... - renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox); + renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox); } private static byte[] theTile; diff --git a/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java b/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java index d19a895cb16bcc0377f136a5bf80f534715a0b04..2b0ea08280e68b427eed0835ffc264ae185749f4 100644 --- a/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java +++ b/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java @@ -66,6 +66,8 @@ public class AlphaColorPipe implements CompositePipe, ParallelogramPipe { } public void fillParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -75,6 +77,8 @@ public class AlphaColorPipe implements CompositePipe, ParallelogramPipe { } public void drawParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java b/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java index 675c8d71ff81b9c6fe20cbca083a32cf3acbd4e1..c5f30976958019cb7e27b7eff5b8578eaec0a96a 100644 --- a/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java +++ b/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java @@ -408,6 +408,8 @@ public abstract class BufferedRenderPipe } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -429,6 +431,8 @@ public abstract class BufferedRenderPipe } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -454,6 +458,8 @@ public abstract class BufferedRenderPipe private class AAParallelogramPipe implements ParallelogramPipe { public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -475,6 +481,8 @@ public abstract class BufferedRenderPipe } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/src/share/classes/sun/java2d/pipe/LoopPipe.java b/src/share/classes/sun/java2d/pipe/LoopPipe.java index 652239d784a93c7d2784ff3498b950627142665b..3df4adc893c6c1af3a91e73eb0325f6ae13db745 100644 --- a/src/share/classes/sun/java2d/pipe/LoopPipe.java +++ b/src/share/classes/sun/java2d/pipe/LoopPipe.java @@ -352,6 +352,8 @@ public class LoopPipe } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) @@ -362,6 +364,8 @@ public class LoopPipe } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java b/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java index cefc09f10f2f4a4f4966be18d8e2b8f1615d2a67..57a9372ec13c02e39d299315ca7775e8905e374b 100644 --- a/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java +++ b/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 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 @@ -40,9 +40,17 @@ import sun.java2d.SunGraphics2D; * => (x+dx2, y+dy2) * => origin * + * The four u[xy][12] parameters are the unsorted extreme coordinates + * of the primitive in user space. They may have been generated by a + * line or a rectangle so they could have u[xy]2 < u[xy]1 in some cases. + * They should be sorted before calculating the bounds of the original + * primitive (such as for calculating the user space bounds for the + * Paint.createContext() method). */ public interface ParallelogramPipe { public void fillParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2); @@ -59,6 +67,8 @@ public interface ParallelogramPipe { * difference between the outer and inner parallelograms. */ public void drawParallelogram(SunGraphics2D sg, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, diff --git a/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java b/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java index 3564fdf001ee8e8579baf96cd68bfe9760627794..86fcc5eaa17fb68ffd1b2da5e746d9655b687648 100644 --- a/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java +++ b/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java @@ -175,8 +175,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter } public boolean drawGeneralLine(SunGraphics2D sg2d, - double x1, double y1, - double x2, double y2) + double ux1, double uy1, + double ux2, double uy2) { if (sg2d.strokeState == SunGraphics2D.STROKE_CUSTOM || sg2d.strokeState == SunGraphics2D.STROKE_THINDASHED) @@ -194,13 +194,14 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter double lw = bs.getLineWidth(); // Save the original dx, dy in case we need it to transform // the linewidth as a perpendicular vector below - double dx = x2 - x1; - double dy = y2 - y1; + double dx = ux2 - ux1; + double dy = uy2 - uy1; + double x1, y1, x2, y2; switch (sg2d.transformState) { case SunGraphics2D.TRANSFORM_GENERIC: case SunGraphics2D.TRANSFORM_TRANSLATESCALE: { - double coords[] = {x1, y1, x2, y2}; + double coords[] = {ux1, uy1, ux2, uy2}; sg2d.transform.transform(coords, 0, coords, 0, 2); x1 = coords[0]; y1 = coords[1]; @@ -213,13 +214,17 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter { double tx = sg2d.transform.getTranslateX(); double ty = sg2d.transform.getTranslateY(); - x1 += tx; - y1 += ty; - x2 += tx; - y2 += ty; + x1 = ux1 + tx; + y1 = uy1 + ty; + x2 = ux2 + tx; + y2 = uy2 + ty; } break; case SunGraphics2D.TRANSFORM_ISIDENT: + x1 = ux1; + y1 = uy1; + x2 = ux2; + y2 = uy2; break; default: throw new InternalError("unknown TRANSFORM state..."); @@ -279,7 +284,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter dx += udx; dy += udy; } - outrenderer.fillParallelogram(sg2d, px, py, -udy, udx, dx, dy); + outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2, + px, py, -udy, udx, dx, dy); return true; } @@ -313,7 +319,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter px = newx; py = newy; } - outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2); + outrenderer.fillParallelogram(sg2d, rx, ry, rx+rw, ry+rh, + px, py, dx1, dy1, dx2, dy2); } public void drawRectangle(SunGraphics2D sg2d, @@ -360,10 +367,12 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter // entire hole in the middle of the parallelogram // so we can just fill the outer parallelogram. fillOuterParallelogram(sg2d, + rx, ry, rx+rw, ry+rh, px, py, dx1, dy1, dx2, dy2, len1, len2, lw1, lw2); } else { outrenderer.drawParallelogram(sg2d, + rx, ry, rx+rw, ry+rh, px, py, dx1, dy1, dx2, dy2, lw1 / len1, lw2 / len2); } @@ -377,6 +386,8 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter * and issues a single fillParallelogram request to fill it. */ public void fillOuterParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double px, double py, double dx1, double dy1, double dx2, double dy2, @@ -412,6 +423,7 @@ public class PixelToParallelogramConverter extends PixelToShapeConverter dx2 += udx2; dy2 += udy2; - outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2); + outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2, + px, py, dx1, dy1, dx2, dy2); } } diff --git a/src/share/classes/sun/text/bidi/BidiBase.java b/src/share/classes/sun/text/bidi/BidiBase.java index 8be648e931c794fc49bc0f9925030b1dbb69f407..8983ab6a668f0308ce714a8ca505be21024195be 100644 --- a/src/share/classes/sun/text/bidi/BidiBase.java +++ b/src/share/classes/sun/text/bidi/BidiBase.java @@ -2889,10 +2889,6 @@ public class BidiBase { verifyValidPara(); verifyRange(start, 0, limit); verifyRange(limit, 0, length+1); - if (getParagraphIndex(start) != getParagraphIndex(limit - 1)) { - /* the line crosses a paragraph boundary */ - throw new IllegalArgumentException(); - } return BidiLine.setLine(bidi, this, newBidi, newBidiBase, start, limit); } diff --git a/src/solaris/classes/sun/awt/X11/XRobotPeer.java b/src/solaris/classes/sun/awt/X11/XRobotPeer.java index 1463f283f0bd33b06551bf3d0ae687bd51a96803..a6acd8eb5f58241958e601b14a5fd3710838b222 100644 --- a/src/solaris/classes/sun/awt/X11/XRobotPeer.java +++ b/src/solaris/classes/sun/awt/X11/XRobotPeer.java @@ -48,7 +48,7 @@ class XRobotPeer implements RobotPeer { } public void dispose() { - _dispose(); + // does nothing } public void mouseMove(int x, int y) { @@ -88,7 +88,6 @@ class XRobotPeer implements RobotPeer { } private static native synchronized void setup(int numberOfButtons, int[] buttonDownMasks); - private static native synchronized void _dispose(); private static native synchronized void mouseMoveImpl(X11GraphicsConfig xgc, int x, int y); private static native synchronized void mousePressImpl(int buttons); diff --git a/src/solaris/native/sun/awt/awt_Robot.c b/src/solaris/native/sun/awt/awt_Robot.c index 38ad27863667f4e7fe49f8f702470dc36500f4ff..c3384b2eb1a3a5461bac73c2cebd1f84c3105e39 100644 --- a/src/solaris/native/sun/awt/awt_Robot.c +++ b/src/solaris/native/sun/awt/awt_Robot.c @@ -48,28 +48,12 @@ #ifdef __linux__ #include #endif -#include extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs; static jint * masks; static jint num_buttons; -static unsigned int s_robotInstanceCounter = 0; - -static void* xcompositeLibHandle = NULL; -static Bool xcompositeExtAvailable = False; -static Bool xcompositeExtTested = False; - -typedef Status (*T_XCompositeQueryVersion)(Display *dpy, int *major_versionp, int *minor_versionp); -typedef Window (*T_XCompositeGetOverlayWindow)(Display *dpy, Window window); -typedef void (*T_XCompositeReleaseOverlayWindow)(Display *dpy, Window window); - -static T_XCompositeQueryVersion XCompositeQueryVersion = NULL; -static T_XCompositeGetOverlayWindow XCompositeGetOverlayWindow = NULL; -static T_XCompositeReleaseOverlayWindow XCompositeReleaseOverlayWindow = NULL; - - static int32_t isXTestAvailable() { int32_t major_opcode, first_event, first_error; int32_t event_basep, error_basep, majorp, minorp; @@ -210,80 +194,8 @@ Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls, jint numberOfButton } AWT_UNLOCK(); - - s_robotInstanceCounter++; } -JNIEXPORT void JNICALL -Java_sun_awt_X11_XRobotPeer__1dispose (JNIEnv * env, jclass cls) -{ - if (--s_robotInstanceCounter) { - return; - } - - // This is the last instance of the XRobotPeer being released - - if (xcompositeExtTested && xcompositeExtAvailable && xcompositeLibHandle) { - // The lib is loaded in IsXCompositeAvailable(). Unload under AWT_LOCK - // so that the shutdown function of the lib behaves correctly. - AWT_LOCK(); - dlclose(xcompositeLibHandle); - AWT_UNLOCK(); - } - - xcompositeExtTested = False; - xcompositeExtAvailable = False; - xcompositeLibHandle = NULL; -} - -/* - * Returns True only if XCOMPOSITE is of version 0.3 or higher. - * The functions that we need are available since that version. - * - * Must be invoked under AWT_LOCK. - * - * Leaves the library loaded if the version is correct. - */ -static Bool IsXCompositeAvailable() -{ - if (!xcompositeExtTested) { - int opcode, eventb, errorb; - - if (XQueryExtension(awt_display, "Composite", &opcode, &eventb, &errorb)) { - xcompositeLibHandle = dlopen("libXcomposite.so.1", RTLD_LAZY | RTLD_GLOBAL); -#ifndef __linux__ /* SOLARIS */ - if (xcompositeLibHandle == NULL) { - xcompositeLibHandle = dlopen("/usr/sfw/lib/libXcomposite.so.1", - RTLD_LAZY | RTLD_GLOBAL); - } -#endif - - if (xcompositeLibHandle) { - int major, minor; - XCompositeQueryVersion = (T_XCompositeQueryVersion)dlsym(xcompositeLibHandle, "XCompositeQueryVersion"); - - if (XCompositeQueryVersion && XCompositeQueryVersion(awt_display, &major, &minor)) { - if (major >= 0 && minor >= 3) { - XCompositeGetOverlayWindow = (T_XCompositeGetOverlayWindow)dlsym(xcompositeLibHandle, "XCompositeGetOverlayWindow"); - XCompositeReleaseOverlayWindow = (T_XCompositeReleaseOverlayWindow)dlsym(xcompositeLibHandle, "XCompositeReleaseOverlayWindow"); - - if (XCompositeGetOverlayWindow && XCompositeReleaseOverlayWindow) { - xcompositeExtAvailable = True; - } - } - } - - if (!xcompositeExtAvailable) { - dlclose(xcompositeLibHandle); - } /* else the lib is unloaded in _dispose() */ - } - } - - xcompositeExtTested = True; - } - - return xcompositeExtAvailable; -} JNIEXPORT void JNICALL Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, @@ -299,7 +211,7 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, jint *ary; /* Array of jints for sending pixel values back * to parent process. */ - Window window; + Window rootWindow; AwtGraphicsConfigDataPtr adata; DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, x, y, width, height, pixelArray); @@ -316,24 +228,14 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData); DASSERT(adata != NULL); - window = XRootWindow(awt_display, adata->awt_visInfo.screen); - - if (IsXCompositeAvailable()) { - // Use 'composite overlay window' instead of the root window. - // See 6903034 for details. - window = XCompositeGetOverlayWindow(awt_display, window); - } - - image = getWindowImage(awt_display, window, x, y, width, height); + rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen); + image = getWindowImage(awt_display, rootWindow, x, y, width, height); /* Array to use to crunch around the pixel values */ ary = (jint *) malloc(width * height * sizeof (jint)); if (ary == NULL) { JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); XDestroyImage(image); - if (IsXCompositeAvailable()) { - XCompositeReleaseOverlayWindow(awt_display, window); - } AWT_UNLOCK(); return; } @@ -354,9 +256,6 @@ Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env, free(ary); XDestroyImage(image); - if (IsXCompositeAvailable()) { - XCompositeReleaseOverlayWindow(awt_display, window); - } AWT_UNLOCK(); } diff --git a/src/windows/classes/sun/awt/windows/WFramePeer.java b/src/windows/classes/sun/awt/windows/WFramePeer.java index a3e4a1d17fc0c1a0614fb164de8d9c7987bd3a45..94cde03850f46a085f28a2d23070b9abd474e56e 100644 --- a/src/windows/classes/sun/awt/windows/WFramePeer.java +++ b/src/windows/classes/sun/awt/windows/WFramePeer.java @@ -107,8 +107,16 @@ class WFramePeer extends WWindowPeer implements FramePeer { Rectangle currentDevBounds = currentDevGC.getBounds(); Rectangle primaryDevBounds = primaryDevGC.getBounds(); - b.width -= (currentDevBounds.width - primaryDevBounds.width); - b.height -= (currentDevBounds.height - primaryDevBounds.height); + boolean isCurrentDevLarger = + ((currentDevBounds.width - primaryDevBounds.width > 0) || + (currentDevBounds.height - primaryDevBounds.height > 0)); + + // the window manager doesn't seem to compensate for differences when + // the primary monitor is larger than the monitor that display the window + if (isCurrentDevLarger) { + b.width -= (currentDevBounds.width - primaryDevBounds.width); + b.height -= (currentDevBounds.height - primaryDevBounds.height); + } } } diff --git a/src/windows/classes/sun/java2d/d3d/D3DRenderer.java b/src/windows/classes/sun/java2d/d3d/D3DRenderer.java index cd4e40df92a673718710f1fb4dfff5bf4c8fb7c5..1fc4f661535dfc352e31b04d8256549c9283d1ad 100644 --- a/src/windows/classes/sun/java2d/d3d/D3DRenderer.java +++ b/src/windows/classes/sun/java2d/d3d/D3DRenderer.java @@ -102,15 +102,20 @@ class D3DRenderer extends BufferedRenderPipe { final ParallelogramPipe realpipe = d3dr.getAAParallelogramPipe(); return new ParallelogramPipe() { public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("D3DFillAAParallelogram"); realpipe.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, @@ -118,6 +123,7 @@ class D3DRenderer extends BufferedRenderPipe { { GraphicsPrimitive.tracePrimitive("D3DDrawAAParallelogram"); realpipe.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2, lw1, lw2); } @@ -167,21 +173,29 @@ class D3DRenderer extends BufferedRenderPipe { d3dr.fillSpans(sg2d, si, transx, transy); } public void fillParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2) { GraphicsPrimitive.tracePrimitive("D3DFillParallelogram"); - d3dr.fillParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2); + d3dr.fillParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2); } public void drawParallelogram(SunGraphics2D sg2d, + double ux1, double uy1, + double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2) { GraphicsPrimitive.tracePrimitive("D3DDrawParallelogram"); - d3dr.drawParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2, lw1, lw2); + d3dr.drawParallelogram(sg2d, + ux1, uy1, ux2, uy2, + x, y, dx1, dy1, dx2, dy2, lw1, lw2); } public void copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) diff --git a/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp b/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp index a563c78802b5af58678ec6b555ec1566a34d90f6..3f0284cb073f747dd1548d3bcefa49a60fc9a766 100644 --- a/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp +++ b/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp @@ -192,6 +192,14 @@ void D3DPipelineManager::NotifyAdapterEventListeners(UINT adapter, pMgr = D3DPipelineManager::GetInstance(); RETURN_IF_NULL(pMgr); hMon = pMgr->pd3d9->GetAdapterMonitor(adapter); + + /* + * If we don't have devices initialized yet, no sense to clear them. + */ + if (!Devices::GetInstance()){ + return; + } + gdiScreen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hMon); JNU_CallStaticMethodByName(env, NULL, diff --git a/src/windows/native/sun/windows/Devices.h b/src/windows/native/sun/windows/Devices.h index bbd8a497aaf90a20de9010a6e55858084c678dab..14e9e7b7f0dbe9ec906354ecd1e3edaf751e168e 100644 --- a/src/windows/native/sun/windows/Devices.h +++ b/src/windows/native/sun/windows/Devices.h @@ -36,6 +36,7 @@ class AwtWin32GraphicsDevice; class Devices { public: +static Devices* GetInstance(); static BOOL UpdateInstance(JNIEnv *env); int GetNumDevices() { return numDevices; } AwtWin32GraphicsDevice* GetDeviceReference(int index, BOOL adjust = TRUE); @@ -59,7 +60,6 @@ friend class InstanceAccess; private: Devices(int numElements); void AddReference(); -static Devices* GetInstance(); AwtWin32GraphicsDevice** devices; int refCount; diff --git a/src/windows/native/sun/windows/awt_Choice.cpp b/src/windows/native/sun/windows/awt_Choice.cpp index dfd9a6de6b3260e31385494c07cdd6409a2fc138..0d014ac56f945e9f4cf62b790d3e60cfc307c3bd 100644 --- a/src/windows/native/sun/windows/awt_Choice.cpp +++ b/src/windows/native/sun/windows/awt_Choice.cpp @@ -396,6 +396,12 @@ LRESULT CALLBACK AwtChoice::ListWindowProc(HWND hwnd, UINT message, DASSERT(::IsWindow(hwnd)); + // This branch is required for the proper work of AwtComponent::GetComponent() method + // while hovering drop-down list + if (message == WmAwtIsComponent) { + return (LRESULT)TRUE; + } + switch (message) { case WM_LBUTTONDOWN: { DWORD curPos = ::GetMessagePos(); diff --git a/src/windows/native/sun/windows/awt_Component.cpp b/src/windows/native/sun/windows/awt_Component.cpp index 024915fcffcf691044d3442edd6d57f8f302e9c1..d4fe9e8766ff2d49361532eeca94ec71dfa6b356 100644 --- a/src/windows/native/sun/windows/awt_Component.cpp +++ b/src/windows/native/sun/windows/awt_Component.cpp @@ -364,7 +364,6 @@ AwtComponent* AwtComponent::GetComponentImpl(HWND hWnd) { AwtComponent *component = (AwtComponent *)::GetWindowLongPtr(hWnd, GWLP_USERDATA); DASSERT(!component || !IsBadReadPtr(component, sizeof(AwtComponent)) ); - DASSERT(!component || component->GetHWnd() == hWnd ); return component; } diff --git a/src/windows/native/sun/windows/awt_Frame.cpp b/src/windows/native/sun/windows/awt_Frame.cpp index 6662d6d54c2f887fe92188b10ef58915418ca677..76811dfec035801af3276bbb7b9b11d850e2fb23 100644 --- a/src/windows/native/sun/windows/awt_Frame.cpp +++ b/src/windows/native/sun/windows/awt_Frame.cpp @@ -344,17 +344,6 @@ LRESULT AwtFrame::ProxyWindowProc(UINT message, WPARAM wParam, LPARAM lParam, Ms SetImeTargetComponent(NULL); } break; - // TODO: when a Choice's list is dropped down and we're scrolling in - // the list WM_MOUSEWHEEL messages come to the poxy, not to the list. Why? - case WM_MOUSEWHEEL: - focusOwner = AwtComponent::GetComponent(sm_focusOwner); - if (focusOwner != NULL && - focusOwner != this) // avoid recursive calls - { - retValue = focusOwner->WindowProc(message, wParam, lParam); - mr = mrConsume; - } - break; case WM_SETFOCUS: if (sm_inSynthesizeFocus) break; // pass it up the WindowProc chain diff --git a/test/java/awt/Component/Revalidate/Revalidate.java b/test/java/awt/Component/Revalidate/Revalidate.java index 670c374f87d4d151c30041c9cecff0ce16ec5c6a..386e5394c6f79b485b060c9381934c74c09ae7f6 100644 --- a/test/java/awt/Component/Revalidate/Revalidate.java +++ b/test/java/awt/Component/Revalidate/Revalidate.java @@ -26,7 +26,7 @@ @bug 7036669 @summary Test Component.revalidate() method @author anthony.petrov@oracle.com: area=awt.component - @run main Revalidate + @run main/othervm -Djava.awt.smartInvalidate=true Revalidate */ import java.awt.*; diff --git a/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java b/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java index 9fd38aa51ecbc0a0fb01c77a15bd9bd68c26505e..0986ebd7976b374896a0d5655793737dee1bb587 100644 --- a/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java +++ b/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java @@ -26,7 +26,7 @@ @bug 6852592 @summary invalidate() must stop when it encounters a validate root @author anthony.petrov@sun.com - @run main InvalidateMustRespectValidateRoots + @run main/othervm -Djava.awt.smartInvalidate=true InvalidateMustRespectValidateRoots */ import javax.swing.*; diff --git a/test/java/awt/Paint/PgramUserBoundsTest.java b/test/java/awt/Paint/PgramUserBoundsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9c71f3482199a285a406b9d199ad98cbfbbd58f0 --- /dev/null +++ b/test/java/awt/Paint/PgramUserBoundsTest.java @@ -0,0 +1,126 @@ +/* + * 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 7043054 + * @summary Verifies that Paint objects receive the appropriate user space + * bounds in their createContext() method + * @run main PgramUserBoundsTest + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.RenderingHints; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; + +public class PgramUserBoundsTest { + static final int MinX = 10; + static final int MinY = 20; + static final int MaxX = 30; + static final int MaxY = 50; + static AffineTransform identity = new AffineTransform(); + + public static void main(String argv[]) { + BufferedImage bimg = + new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bimg.createGraphics(); + g2d.setPaint(new BoundsCheckerPaint(MinX, MinY, MaxX, MaxY)); + testAll(g2d); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + testAll(g2d); + } + + static void testAll(Graphics2D g2d) { + g2d.setTransform(identity); + g2d.translate(100, 100); + testPrimitives(g2d); + + g2d.setTransform(identity); + g2d.scale(10, 10); + testPrimitives(g2d); + + g2d.setTransform(identity); + g2d.rotate(Math.PI/6); + testPrimitives(g2d); + } + + static void testPrimitives(Graphics2D g2d) { + testLine(g2d); + testRect(g2d); + } + + static void testLine(Graphics2D g2d) { + testLine(g2d, MinX, MinY, MaxX, MaxY); + testLine(g2d, MaxX, MinY, MinX, MaxY); + testLine(g2d, MinX, MaxY, MaxX, MinY); + testLine(g2d, MaxX, MaxY, MinX, MinY); + } + + static void testRect(Graphics2D g2d) { + g2d.fillRect(MinX, MinY, MaxX - MinX, MaxY - MinY); + g2d.fill(new Rectangle(MinX, MinY, MaxX - MinX, MaxY - MinY)); + } + + static void testLine(Graphics2D g2d, int x1, int y1, int x2, int y2) { + g2d.drawLine(x1, y1, x2, y2); + g2d.draw(new Line2D.Double(x1, y1, x2, y2)); + } + + static class BoundsCheckerPaint implements Paint { + private Color c = Color.WHITE; + private Rectangle2D expectedBounds; + + public BoundsCheckerPaint(double x1, double y1, + double x2, double y2) + { + expectedBounds = new Rectangle2D.Double(); + expectedBounds.setFrameFromDiagonal(x1, y1, x2, y2); + } + + public int getTransparency() { + return c.getTransparency(); + } + + public PaintContext createContext(ColorModel cm, + Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + System.out.println("user bounds = "+userBounds); + if (!userBounds.equals(expectedBounds)) { + throw new RuntimeException("bounds fail to match"); + } + return c.createContext(cm, deviceBounds, userBounds, xform, hints); + } + } +} diff --git a/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java b/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java new file mode 100644 index 0000000000000000000000000000000000000000..e6835e69e926e6a543ef0c6f24263229b180c2bc --- /dev/null +++ b/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java @@ -0,0 +1,336 @@ +/* + * 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. 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. + */ + +/* + @test + @bug 7040577 + @library ../../../regtesthelpers + @build Sysout + @summary Default implementation of Toolkit.loadSystemColors(int[]) and many others doesn't throw HE in hl env + @author andrei dmitriev: area=awt.headless + @run main/othervm -Djava.awt.headless=true ExceptionContract +*/ + +import java.awt.*; +import java.util.Properties; +import test.java.awt.regtesthelpers.Sysout; + +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.*; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.font.TextAttribute; +import java.awt.im.InputMethodHighlight; +import java.awt.image.*; +import java.awt.peer.*; +import java.net.URL; +import java.util.Map; +import java.util.Properties; + +public class ExceptionContract { + + private static boolean passed = false; + public static void main(String[] args) { + //Case1 + try{ + new _Toolkit().getLockingKeyState(1); + } catch (HeadlessException he){ + passed = true; + } + if (!passed){ + throw new RuntimeException("Tk.getLockingKeyState() didn't throw HeadlessException while in the headless mode."); + } + + passed = false; + //Case2 + try{ + new _Toolkit().setLockingKeyState(1, true); + } catch (HeadlessException he){ + passed = true; + } + if (!passed){ + throw new RuntimeException("Tk.setLockingKeyState() didn't throw HeadlessException while in the headless mode."); + } + + passed = false; + //Case3 + try{ + new _Toolkit().createCustomCursor(new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB), new Point(0,0), "Custom cursor"); + } catch (HeadlessException he){ + he.printStackTrace(); + passed = true; + } + if (!passed){ + throw new RuntimeException("Tk.createCustomCursor(args) didn't throw HeadlessException while in the headless mode."); + } + + } + + static class _Toolkit extends Toolkit { + + @Override + public Cursor createCustomCursor(Image cursor, Point hotSpot, String name) + throws IndexOutOfBoundsException, HeadlessException + { + return super.createCustomCursor(cursor, hotSpot, name); + } + + + @Override + public void setLockingKeyState(int keyCode, boolean on) throws UnsupportedOperationException { + super.setLockingKeyState(keyCode, on); + } + + @Override + public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationException { + return super.getLockingKeyState(keyCode); + } + + + @Override + public void loadSystemColors(int[] systemColors) throws HeadlessException { + return; + } + + @Override + protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException { + return null; + } + + @Override + protected ButtonPeer createButton(Button target) throws HeadlessException { + return null; + } + + @Override + protected TextFieldPeer createTextField(TextField target) throws HeadlessException { + return null; + } + + @Override + protected LabelPeer createLabel(Label target) throws HeadlessException { + return null; + } + + @Override + protected ListPeer createList(List target) throws HeadlessException { + return null; + } + + @Override + protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException { + return null; + } + + @Override + protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException { + return null; + } + + @Override + protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException { + return null; + } + + @Override + protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException { + return null; + } + + @Override + protected ChoicePeer createChoice(Choice target) throws HeadlessException { + return null; + } + + @Override + protected FramePeer createFrame(Frame target) throws HeadlessException { + return null; + } + + @Override + protected CanvasPeer createCanvas(Canvas target) { + return null; + } + + @Override + protected PanelPeer createPanel(Panel target) { + return null; + } + + @Override + protected WindowPeer createWindow(Window target) throws HeadlessException { + return null; + } + + @Override + protected DialogPeer createDialog(Dialog target) throws HeadlessException { + return null; + } + + @Override + protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException { + return null; + } + + @Override + protected MenuPeer createMenu(Menu target) throws HeadlessException { + return null; + } + + @Override + protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException { + return null; + } + + @Override + protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException { + return null; + } + + @Override + protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException { + return null; + } + + @Override + protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException { + return null; + } + + @Override + protected FontPeer getFontPeer(String name, int style) { + return null; + } + + @Override + public Dimension getScreenSize() throws HeadlessException { + return null; + } + + @Override + public int getScreenResolution() throws HeadlessException { + return 0; + } + + @Override + public ColorModel getColorModel() throws HeadlessException { + return null; + } + + @Override + public String[] getFontList() { + return new String[0]; + } + + @Override + public FontMetrics getFontMetrics(Font font) { + return null; + } + + @Override + public void sync() { + + } + + @Override + public Image getImage(String filename) { + return null; + } + + @Override + public Image getImage(URL url) { + return null; + } + + @Override + public Image createImage(String filename) { + return null; + } + + @Override + public Image createImage(URL url) { + return null; + } + + @Override + public boolean prepareImage(Image image, int width, int height, ImageObserver observer) { + return false; + } + + @Override + public int checkImage(Image image, int width, int height, ImageObserver observer) { + return 0; + } + + @Override + public Image createImage(ImageProducer producer) { + return null; + } + + @Override + public Image createImage(byte[] imagedata, int imageoffset, int imagelength) { + return null; + } + + @Override + public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) { + return null; + } + + @Override + public void beep() { + + } + + @Override + public Clipboard getSystemClipboard() throws HeadlessException { + return null; + } + + @Override + protected EventQueue getSystemEventQueueImpl() { + return null; + } + + @Override + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException { + return null; + } + + @Override + public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { + return false; + } + + @Override + public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType) { + return false; + } + + @Override + public Map mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException { + return null; + } + } +} diff --git a/test/java/awt/geom/Arc2D/SerializationTest.java b/test/java/awt/geom/Arc2D/SerializationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1d90a43e5a14159cdbe1bc2ef0a78f0577f802be --- /dev/null +++ b/test/java/awt/geom/Arc2D/SerializationTest.java @@ -0,0 +1,100 @@ +/* + * 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 7040717 6522514 + * @summary Verifies that subclasses of Arc2D can be serialized. + * @run main SerializationTest + */ + +import java.awt.geom.Rectangle2D; +import java.awt.geom.Arc2D; +import java.io.Serializable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class SerializationTest { + static boolean failed; + public static void main(String args[]) { + test(new Arc()); + test(new ArcF()); + test(new ArcD()); + if (failed) throw new RuntimeException("some tests failed"); + } + + public static void test(Object a) { + try { + File objectbin = new File("object.bin"); + FileOutputStream fos = new FileOutputStream(objectbin); + ObjectOutputStream out = new ObjectOutputStream(fos); + out.writeObject(a); + fos.close(); + FileInputStream fis = new FileInputStream(objectbin); + ObjectInputStream in = new ObjectInputStream(fis); + Object o = in.readObject(); + fis.close(); + System.err.println(o); + } catch (Throwable ex) { + ex.printStackTrace(); + failed = true; + } + } + + static class Arc extends Arc2D implements Serializable { + public Arc() { + super(Arc2D.OPEN); + } + + public Rectangle2D makeBounds(double x, double y, double w, double h) { + return new Rectangle2D.Double(x, y, w, h); + } + public double getX() { return 0; } + public double getY() { return 0; } + public double getWidth() { return 0; } + public double getHeight() { return 0; } + public double getAngleExtent() { return 0; } + public double getAngleStart() { return 0; } + public void setAngleExtent(double angExt) { } + public void setAngleStart(double angExt) { } + public void setFrame(double x, double y, double w, double h) {} + public void setArc(double x, double y, double w, double h, + double s, double e, int c) + { + } + public boolean isEmpty() { return false; }; + } + + static class ArcF extends Arc2D.Float implements Serializable { + public ArcF() { + } + } + + static class ArcD extends Arc2D.Double implements Serializable { + public ArcD() { + } + } +} diff --git a/test/java/lang/invoke/6998541/Test6998541.java b/test/java/lang/invoke/6998541/Test6998541.java new file mode 100644 index 0000000000000000000000000000000000000000..0f64eee4b0c05779fbe7a785e7d501a61ba69e92 --- /dev/null +++ b/test/java/lang/invoke/6998541/Test6998541.java @@ -0,0 +1,513 @@ +/* + * 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 6998541 + * @summary JSR 292 implement missing return-type conversion for OP_RETYPE_RAW + * + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2 + * -DTest6998541.N=100000 -DTest6998541.KIND=cast Test6998541 + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2 + * -DTest6998541.N=100000 -DTest6998541.KIND=normal Test6998541 + */ + +import java.util.*; + +import java.lang.invoke.*; +import static java.lang.invoke.MethodHandles.*; + +public class Test6998541 { + private static final Class CLASS = Test6998541.class; + private static final String NAME = "identity"; + private static final int N = Math.max(2, Integer.getInteger(CLASS.getSimpleName()+".N", 10000)); + private static final String KIND = System.getProperty(CLASS.getSimpleName()+".KIND", "cast"); + private static final int BITS = 0x00000201; + + private static final boolean DO_CASTS = !KIND.equals("normal"); + + public static void main(String[] args) throws Throwable { + System.out.println("KIND="+KIND+" DO_CASTS="+DO_CASTS+" N="+N); + doboolean(); + dobyte(); + dochar(); + doshort(); + doint(); + dolong(); + dofloat(); + dodouble(); + dovoid(); + } + + private static void doboolean() throws Throwable { + for (int i = 0; i < N; i++) { + boolean2prim(false); + boolean2prim(true); + } + boolean2prim_invalid(true); + } + private static void dobyte() throws Throwable { + byte x = Byte.MIN_VALUE; + for (int i = 0; i < N; i++, x++) + byte2prim(x); + byte2prim_invalid(x); + } + private static void dochar() throws Throwable { + char x = Character.MIN_VALUE; + for (int i = 0; i < N; i++, x++) + char2prim(x); + char2prim_invalid(x); + } + private static void doshort() throws Throwable { + short x = Short.MIN_VALUE; + for (int i = 0; i < N; i++, x++) + short2prim(x); + short2prim_invalid(x); + } + private static void doint() throws Throwable { + int x = Integer.MIN_VALUE; + int D = Integer.MAX_VALUE / (N / 2) | BITS; + for (int i = 0; i < N; i++, x += D) { + int2prim(x); + } + int2prim_invalid(x); + } + private static void dolong() throws Throwable { + long x = Long.MIN_VALUE; + long D = Long.MAX_VALUE / ((long) (N / 2)) | BITS; + for (int i = 0; i < N; i++, x += D) + long2prim(x); + long2prim_invalid(x); + } + private static void dofloat() throws Throwable { + float x = Float.MIN_VALUE; + float D = Float.MAX_VALUE / ((float) (N / 2)); + for (int i = 0; i < N; i++, x += D) + float2prim(x); + float2prim_invalid(x); + } + private static void dodouble() throws Throwable { + double x = Double.MIN_VALUE; + double D = Double.MAX_VALUE / ((double) (N / 2)); + for (int i = 0; i < N; i++, x += D) + double2prim(x); + double2prim_invalid(x); + } + private static void dovoid() throws Throwable { + for (int i = 0; i < N; i++) { + void2prim(i); + } + void2prim_invalid(0); + // do the other direction here also: + for (int i = 0; i < N; i++) { + prim2void(i); + } + prim2void_invalid(0); + } + + private static void assertEquals(Object o, Object o2) { + if (!o.equals(o2)) + throw new AssertionError("expected: " + o + ", found: " + o2); + } + private static void fail() { + throw new AssertionError(); + } + + private final static MethodHandles.Lookup lookup = MethodHandles.lookup(); + + private static MethodHandle mh(Class ret, Class... args) { + try { + MethodType mt = MethodType.methodType(ret, args); + Class lookupRet = (args.length == 0 ? void.class : args[0]); + MethodHandle mh = lookup.findStatic(CLASS, NAME, mt.changeReturnType(lookupRet)); + if (DO_CASTS) + return MethodHandles.explicitCastArguments(mh, mt); + if (canDoAsType(mh.type(), mt)) + return mh.asType(mt); + try { + mh.asType(mt); + throw new AssertionError("asType should not succeed: "+mh+" => "+mt); + } catch (WrongMethodTypeException ex) { + // this was a required WMTE + return mh.asType(mt.generic()).asType(mt); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + private static final Class[] NUMERIC_TYPE_WIDENING_ORDER = { + byte.class, short.class, int.class, long.class, float.class, double.class + }; + private static boolean canDoAsType(Class src, Class dst) { + if (src == dst) return true; + if (dst == void.class) return true; + 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; + if (dst == char.class) return false; + if (src == char.class) src = int.class; // can widen char to int + for (Class ntype : NUMERIC_TYPE_WIDENING_ORDER) { + if (src == ntype) return true; + if (dst == ntype) return false; + } + throw new AssertionError("should not reach here: "+src+", "+dst); + } + private static boolean canDoAsType(MethodType mt0, MethodType mt1) { + Class rt0 = mt0.returnType(); + Class rt1 = mt1.returnType(); + if (!canDoAsType(rt0, rt1)) return false; + int argc = mt0.parameterCount(); + if (argc != mt1.parameterCount()) return false; + for (int i = 0; i < argc; i++) { + if (!canDoAsType(mt1.parameterType(i), mt0.parameterType(i))) + return false; + } + return true; + } + + private static MethodHandle mh_z(Class ret) { return mh(ret, boolean.class); } + + private final static MethodHandle mh_zz = mh_z(boolean.class); + private final static MethodHandle mh_bz = mh_z(byte.class ); + private final static MethodHandle mh_cz = mh_z(char.class ); + private final static MethodHandle mh_sz = mh_z(short.class ); + private final static MethodHandle mh_iz = mh_z(int.class ); + private final static MethodHandle mh_jz = mh_z(long.class ); + private final static MethodHandle mh_fz = mh_z(float.class ); + private final static MethodHandle mh_dz = mh_z(double.class ); + + private static void boolean2prim(boolean x) throws Throwable { + int i = x ? 1 : 0; + assertEquals( x, (boolean) mh_zz.invokeExact(x)); // boolean -> boolean + if (!DO_CASTS) return; + assertEquals((byte) i, (byte) mh_bz.invokeExact(x)); // boolean -> byte + assertEquals((char) i, (char) mh_cz.invokeExact(x)); // boolean -> char + assertEquals((short) i, (short) mh_sz.invokeExact(x)); // boolean -> short + assertEquals((int) i, (int) mh_iz.invokeExact(x)); // boolean -> int + assertEquals((long) i, (long) mh_jz.invokeExact(x)); // boolean -> long + assertEquals((float) i, (float) mh_fz.invokeExact(x)); // boolean -> float + assertEquals((double) i, (double) mh_dz.invokeExact(x)); // boolean -> double + } + private static void boolean2prim_invalid(boolean x) throws Throwable { + if (DO_CASTS) return; + try { byte y = (byte) mh_bz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> byte + try { char y = (char) mh_cz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> char + try { short y = (short) mh_sz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> short + try { int y = (int) mh_iz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> int + try { long y = (long) mh_jz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> long + try { float y = (float) mh_fz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> float + try { double y = (double) mh_dz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> double + } + + private static MethodHandle mh_b(Class ret) { return mh(ret, byte.class); } + + private final static MethodHandle mh_zb = mh_b(boolean.class); + private final static MethodHandle mh_bb = mh_b(byte.class ); + private final static MethodHandle mh_cb = mh_b(char.class ); + private final static MethodHandle mh_sb = mh_b(short.class ); + private final static MethodHandle mh_ib = mh_b(int.class ); + private final static MethodHandle mh_jb = mh_b(long.class ); + private final static MethodHandle mh_fb = mh_b(float.class ); + private final static MethodHandle mh_db = mh_b(double.class ); + + private static void byte2prim(byte x) throws Throwable { + assertEquals((byte) x, (byte) mh_bb.invokeExact(x)); // byte -> byte + assertEquals((short) x, (short) mh_sb.invokeExact(x)); // byte -> short + assertEquals((int) x, (int) mh_ib.invokeExact(x)); // byte -> int + assertEquals((long) x, (long) mh_jb.invokeExact(x)); // byte -> long + assertEquals((float) x, (float) mh_fb.invokeExact(x)); // byte -> float + assertEquals((double) x, (double) mh_db.invokeExact(x)); // byte -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((char) x, (char) mh_cb.invokeExact(x)); // byte -> char + assertEquals((boolean) z, (boolean) mh_zb.invokeExact(x)); // byte -> boolean + } + private static void byte2prim_invalid(byte x) throws Throwable { + if (DO_CASTS) return; + try { char y = (char) mh_cb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> char + try { boolean y = (boolean) mh_zb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> boolean + } + + private static MethodHandle mh_c(Class ret) { return mh(ret, char.class); } + + private final static MethodHandle mh_zc = mh_c(boolean.class); + private final static MethodHandle mh_bc = mh_c(byte.class ); + private final static MethodHandle mh_cc = mh_c(char.class ); + private final static MethodHandle mh_sc = mh_c(short.class ); + private final static MethodHandle mh_ic = mh_c(int.class ); + private final static MethodHandle mh_jc = mh_c(long.class ); + private final static MethodHandle mh_fc = mh_c(float.class ); + private final static MethodHandle mh_dc = mh_c(double.class ); + + private static void char2prim(char x) throws Throwable { + assertEquals((char) x, (char) mh_cc.invokeExact(x)); // char -> char + assertEquals((int) x, (int) mh_ic.invokeExact(x)); // char -> int + assertEquals((long) x, (long) mh_jc.invokeExact(x)); // char -> long + assertEquals((float) x, (float) mh_fc.invokeExact(x)); // char -> float + assertEquals((double) x, (double) mh_dc.invokeExact(x)); // char -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zc.invokeExact(x)); // char -> boolean + assertEquals((byte) x, (byte) mh_bc.invokeExact(x)); // char -> byte + assertEquals((short) x, (short) mh_sc.invokeExact(x)); // char -> short + } + private static void char2prim_invalid(char x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> boolean + try { byte y = (byte) mh_bc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> byte + try { short y = (short) mh_sc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> short + } + + private static MethodHandle mh_s(Class ret) { return mh(ret, short.class); } + + private final static MethodHandle mh_zs = mh_s(boolean.class); + private final static MethodHandle mh_bs = mh_s(byte.class ); + private final static MethodHandle mh_cs = mh_s(char.class ); + private final static MethodHandle mh_ss = mh_s(short.class ); + private final static MethodHandle mh_is = mh_s(int.class ); + private final static MethodHandle mh_js = mh_s(long.class ); + private final static MethodHandle mh_fs = mh_s(float.class ); + private final static MethodHandle mh_ds = mh_s(double.class ); + + private static void short2prim(short x) throws Throwable { + assertEquals((short) x, (short) mh_ss.invokeExact(x)); // short -> short + assertEquals((int) x, (int) mh_is.invokeExact(x)); // short -> int + assertEquals((long) x, (long) mh_js.invokeExact(x)); // short -> long + assertEquals((float) x, (float) mh_fs.invokeExact(x)); // short -> float + assertEquals((double) x, (double) mh_ds.invokeExact(x)); // short -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zs.invokeExact(x)); // short -> boolean + assertEquals((byte) x, (byte) mh_bs.invokeExact(x)); // short -> byte + assertEquals((char) x, (char) mh_cs.invokeExact(x)); // short -> char + } + private static void short2prim_invalid(short x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> boolean + try { byte y = (byte) mh_bs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> byte + try { char y = (char) mh_cs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> char + } + + private static MethodHandle mh_i(Class ret) { return mh(ret, int.class); } + + private final static MethodHandle mh_zi = mh_i(boolean.class); + private final static MethodHandle mh_bi = mh_i(byte.class ); + private final static MethodHandle mh_ci = mh_i(char.class ); + private final static MethodHandle mh_si = mh_i(short.class ); + private final static MethodHandle mh_ii = mh_i(int.class ); + private final static MethodHandle mh_ji = mh_i(long.class ); + private final static MethodHandle mh_fi = mh_i(float.class ); + private final static MethodHandle mh_di = mh_i(double.class ); + + private static void int2prim(int x) throws Throwable { + assertEquals((int) x, (int) mh_ii.invokeExact(x)); // int -> int + assertEquals((long) x, (long) mh_ji.invokeExact(x)); // int -> long + assertEquals((float) x, (float) mh_fi.invokeExact(x)); // int -> float + assertEquals((double) x, (double) mh_di.invokeExact(x)); // int -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zi.invokeExact(x)); // int -> boolean + assertEquals((byte) x, (byte) mh_bi.invokeExact(x)); // int -> byte + assertEquals((char) x, (char) mh_ci.invokeExact(x)); // int -> char + assertEquals((short) x, (short) mh_si.invokeExact(x)); // int -> short + } + private static void int2prim_invalid(int x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> boolean + try { byte y = (byte) mh_bi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> byte + try { char y = (char) mh_ci.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> char + try { short y = (short) mh_si.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> short + } + + private static MethodHandle mh_j(Class ret) { return mh(ret, long.class); } + + private final static MethodHandle mh_zj = mh_j(boolean.class); + private final static MethodHandle mh_bj = mh_j(byte.class ); + private final static MethodHandle mh_cj = mh_j(char.class ); + private final static MethodHandle mh_sj = mh_j(short.class ); + private final static MethodHandle mh_ij = mh_j(int.class ); + private final static MethodHandle mh_jj = mh_j(long.class ); + private final static MethodHandle mh_fj = mh_j(float.class ); + private final static MethodHandle mh_dj = mh_j(double.class ); + + private static void long2prim(long x) throws Throwable { + assertEquals((long) x, (long) mh_jj.invokeExact(x)); // long -> long + assertEquals((float) x, (float) mh_fj.invokeExact(x)); // long -> float + assertEquals((double) x, (double) mh_dj.invokeExact(x)); // long -> double + if (!DO_CASTS) return; + boolean z = ((x & 1) != 0); + assertEquals((boolean)z, (boolean) mh_zj.invokeExact(x)); // long -> boolean + assertEquals((byte) x, (byte) mh_bj.invokeExact(x)); // long -> byte + assertEquals((char) x, (char) mh_cj.invokeExact(x)); // long -> char + assertEquals((short) x, (short) mh_sj.invokeExact(x)); // long -> short + assertEquals((int) x, (int) mh_ij.invokeExact(x)); // long -> int + } + private static void long2prim_invalid(long x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> boolean + try { byte y = (byte) mh_bj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> byte + try { char y = (char) mh_cj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> char + try { short y = (short) mh_sj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> short + try { int y = (int) mh_ij.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> int + } + + private static MethodHandle mh_f(Class ret) { return mh(ret, float.class); } + + private final static MethodHandle mh_zf = mh_f(boolean.class); + private final static MethodHandle mh_bf = mh_f(byte.class ); + private final static MethodHandle mh_cf = mh_f(char.class ); + private final static MethodHandle mh_sf = mh_f(short.class ); + private final static MethodHandle mh_if = mh_f(int.class ); + private final static MethodHandle mh_jf = mh_f(long.class ); + private final static MethodHandle mh_ff = mh_f(float.class ); + private final static MethodHandle mh_df = mh_f(double.class ); + + private static void float2prim(float x) throws Throwable { + assertEquals((float) x, (float) mh_ff.invokeExact(x)); // float -> float + assertEquals((double) x, (double) mh_df.invokeExact(x)); // float -> double + if (!DO_CASTS) return; + boolean z = (((byte) x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zf.invokeExact(x)); // float -> boolean + assertEquals((byte) x, (byte) mh_bf.invokeExact(x)); // float -> byte + assertEquals((char) x, (char) mh_cf.invokeExact(x)); // float -> char + assertEquals((short) x, (short) mh_sf.invokeExact(x)); // float -> short + assertEquals((int) x, (int) mh_if.invokeExact(x)); // float -> int + assertEquals((long) x, (long) mh_jf.invokeExact(x)); // float -> long + } + private static void float2prim_invalid(float x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> boolean + try { byte y = (byte) mh_bf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> byte + try { char y = (char) mh_cf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> char + try { short y = (short) mh_sf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> short + try { int y = (int) mh_if.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> int + try { long y = (long) mh_jf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> long + } + + private static MethodHandle mh_d(Class ret) { return mh(ret, double.class); } + + private final static MethodHandle mh_zd = mh_d(boolean.class); + private final static MethodHandle mh_bd = mh_d(byte.class ); + private final static MethodHandle mh_cd = mh_d(char.class ); + private final static MethodHandle mh_sd = mh_d(short.class ); + private final static MethodHandle mh_id = mh_d(int.class ); + private final static MethodHandle mh_jd = mh_d(long.class ); + private final static MethodHandle mh_fd = mh_d(float.class ); + private final static MethodHandle mh_dd = mh_d(double.class ); + + private static void double2prim(double x) throws Throwable { + assertEquals((double) x, (double) mh_dd.invokeExact(x)); // double -> double + if (!DO_CASTS) return; + boolean z = (((byte) x & 1) != 0); + assertEquals((boolean) z, (boolean) mh_zd.invokeExact(x)); // double -> boolean + assertEquals((byte) x, (byte) mh_bd.invokeExact(x)); // double -> byte + assertEquals((char) x, (char) mh_cd.invokeExact(x)); // double -> char + assertEquals((short) x, (short) mh_sd.invokeExact(x)); // double -> short + assertEquals((int) x, (int) mh_id.invokeExact(x)); // double -> int + assertEquals((long) x, (long) mh_jd.invokeExact(x)); // double -> long + assertEquals((float) x, (float) mh_fd.invokeExact(x)); // double -> float + } + private static void double2prim_invalid(double x) throws Throwable { + if (DO_CASTS) return; + try { boolean y = (boolean) mh_zd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> boolean + try { byte y = (byte) mh_bd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> byte + try { char y = (char) mh_cd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> char + try { short y = (short) mh_sd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> short + try { int y = (int) mh_id.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> int + try { long y = (long) mh_jd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> long + try { float y = (float) mh_fd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> float + } + + private final static MethodHandle mh_zv = mh(boolean.class); + private final static MethodHandle mh_bv = mh(byte.class ); + private final static MethodHandle mh_cv = mh(char.class ); + private final static MethodHandle mh_sv = mh(short.class ); + private final static MethodHandle mh_iv = mh(int.class ); + private final static MethodHandle mh_jv = mh(long.class ); + private final static MethodHandle mh_fv = mh(float.class ); + 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 + assertEquals((short) 0, (short) mh_sv.invokeExact()); // void -> short + assertEquals( 0, (int) mh_iv.invokeExact()); // void -> int + assertEquals( 0L, (long) mh_jv.invokeExact()); // void -> long + assertEquals( 0.0f, (float) mh_fv.invokeExact()); // void -> float + assertEquals( 0.0d, (double) mh_dv.invokeExact()); // void -> double + } + + 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 + } + + private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); } + + private final static MethodHandle mh_vz = mh_v(boolean.class); + private final static MethodHandle mh_vb = mh_v(byte.class ); + private final static MethodHandle mh_vc = mh_v(char.class ); + private final static MethodHandle mh_vs = mh_v(short.class ); + private final static MethodHandle mh_vi = mh_v(int.class ); + private final static MethodHandle mh_vj = mh_v(long.class ); + private final static MethodHandle mh_vf = mh_v(float.class ); + private final static MethodHandle mh_vd = mh_v(double.class ); + + private static void prim2void(int x) throws Throwable { + boolean z = ((x & 1) != 0); + mh_vz.invokeExact( z); // boolean -> void + mh_vb.invokeExact((byte) x); // byte -> void + mh_vc.invokeExact((char) x); // char -> void + mh_vs.invokeExact((short) x); // short -> void + mh_vi.invokeExact((int) x); // int -> void + mh_vj.invokeExact((long) x); // long -> void + mh_vf.invokeExact((float) x); // float -> void + mh_vd.invokeExact((double) x); // double -> void + } + + private static void prim2void_invalid(int x) throws Throwable { + // no cases + } + + private static boolean identity(boolean v) { return v; } + private static byte identity(byte v) { return v; } + private static char identity(char v) { return v; } + private static short identity(short v) { return v; } + private static int identity(int v) { return v; } + private static long identity(long v) { return v; } + private static float identity(float v) { return v; } + private static double identity(double v) { return v; } + private static void identity() {} +} diff --git a/test/java/lang/invoke/InvokeGenericTest.java b/test/java/lang/invoke/InvokeGenericTest.java index 2da9634c464e8885a19329b0a2f9b782b89c936d..8ced6c6658204e53233bfcb9af6bd61c4e700d01 100644 --- a/test/java/lang/invoke/InvokeGenericTest.java +++ b/test/java/lang/invoke/InvokeGenericTest.java @@ -24,7 +24,7 @@ */ /* @test - * @summary unit tests for java.lang.invoke.MethodHandle.invokeGeneric + * @summary unit tests for java.lang.invoke.MethodHandle.invoke * @compile -target 7 InvokeGenericTest.java * @run junit/othervm test.java.lang.invoke.InvokeGenericTest */ @@ -53,6 +53,10 @@ public class InvokeGenericTest { if (vstr != null) verbosity = Integer.parseInt(vstr); } + public static void main(String... av) throws Throwable { + new InvokeGenericTest().testFirst(); + } + @Test public void testFirst() throws Throwable { verbosity += 9; try { @@ -103,7 +107,7 @@ public class InvokeGenericTest { void startTest(String name) { if (testName != null) printCounts(); if (verbosity >= 1) - System.out.println(name); + System.out.println("["+name+"]"); posTests = negTests = 0; testName = name; } @@ -350,6 +354,30 @@ public class InvokeGenericTest { String[] args = { "one", "two" }; MethodHandle mh = callable(Object.class, String.class); Object res; List resl; + res = resl = (List) mh.invoke((String)args[0], (Object)args[1]); + //System.out.println(res); + assertEquals(Arrays.asList(args), res); + } + + @Test + public void testSimplePrims() throws Throwable { + startTest("testSimplePrims"); + countTest(); + int[] args = { 1, 2 }; + MethodHandle mh = callable(Object.class, Object.class); + Object res; List resl; + res = resl = (List) mh.invoke(args[0], args[1]); + //System.out.println(res); + assertEquals(Arrays.toString(args), res.toString()); + } + + @Test + public void testAlternateName() throws Throwable { + startTest("testAlternateName"); + countTest(); + 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]); //System.out.println(res); assertEquals(Arrays.asList(args), res); @@ -388,24 +416,24 @@ public class InvokeGenericTest { try { switch (args.length) { case 0: - junk = target.invokeGeneric(); break; + junk = target.invoke(); break; case 1: - junk = target.invokeGeneric(args[0]); break; + junk = target.invoke(args[0]); break; case 2: - junk = target.invokeGeneric(args[0], args[1]); break; + junk = target.invoke(args[0], args[1]); break; case 3: - junk = target.invokeGeneric(args[0], args[1], args[2]); break; + junk = target.invoke(args[0], args[1], args[2]); break; case 4: - junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break; + junk = target.invoke(args[0], args[1], args[2], args[3]); break; default: junk = target.invokeWithArguments(args); break; } } catch (WrongMethodTypeException ex) { return; } catch (Exception ex) { - throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex); + throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex); } - throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args)); + throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args)); } /** Make a list of all combinations of the given types, with the given arities. @@ -451,7 +479,7 @@ public class InvokeGenericTest { startTest("testReferenceConversions"); toString_MH = LOOKUP. findVirtual(Object.class, "toString", MethodType.methodType(String.class)); - String[] args = { "one", "two" }; + Object[] args = { "one", "two" }; for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) { testReferenceConversions(type, args); } @@ -463,7 +491,7 @@ public class InvokeGenericTest { MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList()); mh = MethodHandles.foldArguments(tsdrop, mh); mh = mh.asType(type); - Object res = mh.invokeGeneric((String)args[0], (Object)args[1]); + Object res = mh.invoke((String)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args).toString(), res); } @@ -473,10 +501,10 @@ public class InvokeGenericTest { public void testBoxConversions() throws Throwable { startTest("testBoxConversions"); countTest(); - Integer[] args = { 1, 2 }; + Object[] args = { 1, 2 }; MethodHandle mh = callable(Object.class, int.class); Object res; List resl; - res = resl = (List) mh.invokeGeneric((int)args[0], (Object)args[1]); + res = resl = (List) mh.invoke((int)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args), res); } diff --git a/test/java/lang/invoke/JavaDocExamplesTest.java b/test/java/lang/invoke/JavaDocExamplesTest.java index 7810399c62dec5cbce949bfe05e635b4cea4d497..9f68a20061f7d1f41aede195be3902e68f3e6556 100644 --- a/test/java/lang/invoke/JavaDocExamplesTest.java +++ b/test/java/lang/invoke/JavaDocExamplesTest.java @@ -170,8 +170,8 @@ assert(s.equals("savvy")); mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); -x = mh.invokeGeneric("one", "two"); -// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; +x = mh.invoke("one", "two"); +// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assert(x.equals(java.util.Arrays.asList("one","two"))); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); @@ -199,12 +199,12 @@ mh.invokeExact(System.out, "Hello, world."); MethodHandle asList = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) .asVarargsCollector(Object[].class); -assertEquals("[]", asList.invokeGeneric().toString()); -assertEquals("[1]", asList.invokeGeneric(1).toString()); -assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString()); +assertEquals("[]", asList.invoke().toString()); +assertEquals("[1]", asList.invoke(1).toString()); +assertEquals("[two, too]", asList.invoke("two", "too").toString()); Object[] argv = { "three", "thee", "tee" }; -assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString()); -List ls = (List) asList.invokeGeneric((Object)argv); +assertEquals("[three, thee, tee]", asList.invoke(argv).toString()); +List ls = (List) asList.invoke((Object)argv); assertEquals(1, ls.size()); assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); }} @@ -218,9 +218,9 @@ MethodHandle vamh = publicLookup() .asVarargsCollector(Object[].class); MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh); assert(vamh.type().equals(mh.type())); -assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString()); +assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString()); boolean failed = false; -try { mh.invokeGeneric(1,2,3); } +try { mh.invoke(1,2,3); } catch (WrongMethodTypeException ex) { failed = true; } assert(failed); {} diff --git a/test/java/lang/invoke/MethodHandlesTest.java b/test/java/lang/invoke/MethodHandlesTest.java index 427229de7c30d80fcefd8da8f3ea06de449fa010..9ebe37a3b965e075c556f14cb80214c84f27cf29 100644 --- a/test/java/lang/invoke/MethodHandlesTest.java +++ b/test/java/lang/invoke/MethodHandlesTest.java @@ -105,24 +105,6 @@ public class MethodHandlesTest { public MethodHandlesTest() { } - @Before - public void checkImplementedPlatform() { - boolean platformOK = false; - Properties properties = System.getProperties(); - String vers = properties.getProperty("java.vm.version"); - String name = properties.getProperty("java.vm.name"); - String arch = properties.getProperty("os.arch"); - if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") || - arch.equals("sparc") || arch.equals("sparcv9")) && - (name.contains("Client") || name.contains("Server")) - ) { - platformOK = true; - } else { - System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); - } - assumeTrue(platformOK); - } - String testName; static int allPosTests, allNegTests; int posTests, negTests; @@ -331,6 +313,9 @@ public class MethodHandlesTest { static MethodHandle varargsArray(int arity) { return ValueConversions.varargsArray(arity); } + static MethodHandle varargsArray(Class arrayType, int arity) { + return ValueConversions.varargsArray(arrayType, arity); + } /** Variation of varargsList, but with the given rtype. */ static MethodHandle varargsList(int arity, Class rtype) { MethodHandle list = varargsList(arity); @@ -865,7 +850,7 @@ public class MethodHandlesTest { Class type = (Class) t[1]; Object value; Field field; - try { + try { field = HasFields.class.getDeclaredField(name); } catch (Exception ex) { throw new InternalError("no field HasFields."+name); @@ -1144,16 +1129,9 @@ public class MethodHandlesTest { : MethodHandles.arrayElementSetter(arrayType); assertSame(mh.type(), expType); if (elemType != int.class && elemType != boolean.class) { - MethodType gtype; - if (true) { // FIXME: remove this path (and remove below in the mh.invokes) - gtype = mh.type().changeParameterType(0, Object.class); - if (testSetter) - gtype = gtype.changeParameterType(2, Object.class); - else - gtype = gtype.changeReturnType(Object.class); - } else - // FIXME: This simpler path hits a bug in convertArguments => ToGeneric - gtype = mh.type().generic().changeParameterType(1, int.class); + // 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); } Object sawValue, expValue; @@ -1169,7 +1147,7 @@ public class MethodHandlesTest { else if (elemType == boolean.class) mh.invokeExact((boolean[]) array, i, (boolean)random); else - mh.invokeExact(array, i, random); + mh.invokeExact(array, (Integer)i, random); assertEquals(model, array2list(array)); } else { Array.set(array, i, random); @@ -1189,7 +1167,7 @@ public class MethodHandlesTest { else if (elemType == boolean.class) sawValue = (boolean) mh.invokeExact((boolean[]) array, i); else - sawValue = mh.invokeExact(array, i); + sawValue = mh.invokeExact(array, (Integer)i); assertEquals(sawValue, expValue); assertEquals(model, array2list(array)); } @@ -1341,21 +1319,15 @@ public class MethodHandlesTest { int numcases = 1; for (int outargs = 0; outargs <= max; outargs++) { if (outargs - inargs >= MAX_ARG_INCREASE) continue; - int[] reorder = new int[outargs]; int casStep = dilution + 1; // Avoid some common factors: while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) || (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0)) casStep++; - for (int cas = 0; cas < numcases; cas += casStep) { - for (int i = 0, c = cas; i < outargs; i++) { - reorder[i] = c % inargs; - c /= inargs; - } - testPermuteArguments(args, types, reorder); - } + testPermuteArguments(args, types, outargs, numcases, casStep); numcases *= inargs; if (dilution > 10 && outargs >= 4) { + int[] reorder = new int[outargs]; // Do some special patterns, which we probably missed. // Replication of a single argument or argument pair. for (int i = 0; i < inargs; i++) { @@ -1383,6 +1355,19 @@ public class MethodHandlesTest { } } + public void testPermuteArguments(Object[] args, Class[] types, + int outargs, int numcases, int casStep) throws Throwable { + int inargs = args.length; + int[] reorder = new int[outargs]; + for (int cas = 0; cas < numcases; cas += casStep) { + for (int i = 0, c = cas; i < outargs; i++) { + reorder[i] = c % inargs; + c /= inargs; + } + testPermuteArguments(args, types, reorder); + } + } + static int[] reverse(int[] reorder) { reorder = reorder.clone(); for (int i = 0, imax = reorder.length / 2; i < imax; i++) { @@ -1433,6 +1418,12 @@ public class MethodHandlesTest { MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); Object result = newTarget.invokeWithArguments(args); Object expected = Arrays.asList(permArgs); + if (!expected.equals(result)) { + System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types)); + System.out.println("in args: "+Arrays.asList(args)); + System.out.println("out args: "+expected); + System.out.println("bad args: "+result); + } assertEquals(expected, result); } @@ -1456,26 +1447,27 @@ public class MethodHandlesTest { } public void testSpreadArguments(Class argType, int pos, int nargs) throws Throwable { countTest(); - MethodHandle target = varargsArray(nargs); - MethodHandle target2 = changeArgTypes(target, argType); + Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); + MethodHandle target2 = varargsArray(arrayType, nargs); + MethodHandle target = target2.asType(target2.type().generic()); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: - if (pos == 0 && nargs < 5) { - Object[] check = (Object[]) target.invokeWithArguments(args); + if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { + Object[] check = (Object[]) (Object) target.invokeWithArguments(args); assertArrayEquals(args, check); switch (nargs) { case 0: - check = (Object[]) target.invokeExact(); + check = (Object[]) (Object) target.invokeExact(); assertArrayEquals(args, check); break; case 1: - check = (Object[]) target.invokeExact(args[0]); + check = (Object[]) (Object) target.invokeExact(args[0]); assertArrayEquals(args, check); break; case 2: - check = (Object[]) target.invokeExact(args[0], args[1]); + check = (Object[]) (Object) target.invokeExact(args[0], args[1]); assertArrayEquals(args, check); break; } @@ -1483,23 +1475,50 @@ public class MethodHandlesTest { List> newParams = new ArrayList>(target2.type().parameterList()); { // modify newParams in place List> spreadParams = newParams.subList(pos, nargs); - spreadParams.clear(); spreadParams.add(Object[].class); + spreadParams.clear(); spreadParams.add(arrayType); } - MethodType newType = MethodType.methodType(Object.class, newParams); - MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType); - Object[] returnValue; + MethodType newType = MethodType.methodType(arrayType, newParams); + MethodHandle result = target2.asSpreader(arrayType, nargs-pos); + assert(result.type() == newType) : Arrays.asList(result, newType); + result = result.asType(newType.generic()); + Object returnValue; if (pos == 0) { - // In the following line, the first cast implies - // normal Object return value for the MH call (Object[])->Object, - // while the second cast dynamically converts to an Object array. - // Such a double cast is typical of MH.invokeExact. - returnValue = (Object[]) (Object) result.invokeExact(args); + Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); + returnValue = result.invokeExact(args2); } else { Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); - args1[pos] = Arrays.copyOfRange(args, pos, args.length); - returnValue = (Object[]) result.invokeWithArguments(args1); + args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); + returnValue = result.invokeWithArguments(args1); + } + String argstr = Arrays.toString(args); + if (!argType.isPrimitive()) { + Object[] rv = (Object[]) returnValue; + String rvs = Arrays.toString(rv); + if (!Arrays.equals(args, rv)) { + System.out.println("method: "+result); + System.out.println("expected: "+argstr); + System.out.println("returned: "+rvs); + assertArrayEquals(args, rv); + } + } else if (argType == int.class) { + String rvs = Arrays.toString((int[]) returnValue); + if (!argstr.equals(rvs)) { + System.out.println("method: "+result); + System.out.println("expected: "+argstr); + System.out.println("returned: "+rvs); + assertEquals(argstr, rvs); + } + } else if (argType == long.class) { + String rvs = Arrays.toString((long[]) returnValue); + if (!argstr.equals(rvs)) { + System.out.println("method: "+result); + System.out.println("expected: "+argstr); + System.out.println("returned: "+rvs); + assertEquals(argstr, rvs); + } + } else { + // cannot test... } - assertArrayEquals(args, returnValue); } @Test @@ -1780,7 +1799,7 @@ public class MethodHandlesTest { assertCalled("invokee", args); // generic invoker countTest(); - inv = MethodHandles.genericInvoker(type); + inv = MethodHandles.invoker(type); if (nargs <= 3) { calledLog.clear(); switch (nargs) { @@ -2130,15 +2149,12 @@ public class MethodHandlesTest { Object z = surprise.invokeExact(x); System.out.println("Failed to throw; got z="+z); assertTrue(false); - } catch (Exception ex) { + } catch (ClassCastException ex) { if (verbosity > 2) System.out.println("caught "+ex); if (verbosity > 3) ex.printStackTrace(); - assertTrue(ex instanceof ClassCastException - // FIXME: accept only one of the two for any given unit test - || ex instanceof WrongMethodTypeException - ); + assertTrue(true); // all is well } } @@ -2328,6 +2344,34 @@ class ValueConversions { // else need to spin bytecode or do something else fancy throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs); } + public static MethodHandle varargsArray(Class arrayType, int nargs) { + Class elemType = arrayType.getComponentType(); + MethodType vaType = MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)); + MethodHandle mh = varargsArray(nargs); + if (arrayType != Object[].class) + mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType)); + return mh.asType(vaType); + } + static Object changeArrayType(Class arrayType, Object[] a) { + Class elemType = arrayType.getComponentType(); + if (!elemType.isPrimitive()) + return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class)); + Object b = java.lang.reflect.Array.newInstance(elemType, a.length); + for (int i = 0; i < a.length; i++) + java.lang.reflect.Array.set(b, i, a[i]); + return b; + } + private static final MethodHandle CHANGE_ARRAY_TYPE; + static { + try { + CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType", + MethodType.methodType(Object.class, Class.class, Object[].class)); + } catch (NoSuchMethodException | IllegalAccessException ex) { + Error err = new InternalError("uncaught exception"); + err.initCause(ex); + throw err; + } + } private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); private static List makeList(Object... args) { return Arrays.asList(args); } diff --git a/test/java/lang/invoke/RicochetTest.java b/test/java/lang/invoke/RicochetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..310b576ddc8df60ce5279dc7726c6d88b1cc863f --- /dev/null +++ b/test/java/lang/invoke/RicochetTest.java @@ -0,0 +1,582 @@ +/* + * 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. 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. + */ + +/* @test + * @summary unit tests for recursive method handles + * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest + */ + +package test.java.lang.invoke; + +import java.lang.invoke.*; +import java.util.*; +import org.junit.*; +import static java.lang.invoke.MethodType.*; +import static java.lang.invoke.MethodHandles.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + + +/** + * + * @author jrose + */ +public class RicochetTest { + private static final Class CLASS = RicochetTest.class; + private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); + + public static void main(String... av) throws Throwable { + RicochetTest test = new RicochetTest(); + if (av.length > 0) test.testOnly = Arrays.asList(av).toString(); + if (REPEAT == 1 || test.testOnly != null) { + test.testAll(); + if (test.testOnlyTests == null) throw new RuntimeException("no matching test: "+test.testOnly); + } else if (REPEAT == 0) { + org.junit.runner.JUnitCore.runClasses(RicochetTest.class); + } else { + verbose(1, "REPEAT="+REPEAT); + for (int i = 0; i < REPEAT; i++) { + test.testRepetition = (i+1); + verbose(0, "[#"+test.testRepetition+"]"); + test.testAll(); + } + } + } + int testRepetition; + + public void testAll() throws Throwable { + testNull(); + testBoxInteger(); + testFilterReturnValue(); + testFilterObject(); + testBoxLong(); + testFilterInteger(); + testIntSpreads(); + testByteSpreads(); + testLongSpreads(); + testIntCollects(); + testReturns(); + } + + @Test + public void testNull() throws Throwable { + if (testRepetition > (1+REPEAT/100)) return; // trivial test + if (!startTest("testNull")) return; + assertEquals(opI(37), opI.invokeWithArguments(37)); + assertEqualFunction(opI, opI); + } + + @Test + public void testBoxInteger() throws Throwable { + if (!startTest("testBoxInteger")) return; + assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type())); + } + + @Test + public void testFilterReturnValue() throws Throwable { + if (!startTest("testFilterReturnValue")) return; + int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 }; + Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]); + assertEquals(Arrays.toString(ints), res.toString()); + MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class)); + res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]); + assertEquals(Arrays.toString(ints), res.toString()); + MethodHandle add0 = addL.bindTo(0); + assertEqualFunction(filterReturnValue(opL2, add0), opL2); + } + + @Test + public void testFilterObject() throws Throwable { + if (!startTest("testFilterObject")) return; + MethodHandle add0 = addL.bindTo(0); + assertEqualFunction(sequence(opL2, add0), opL2); + int bump13 = -13; // value near 20 works as long as test values are near [-80..80] + MethodHandle add13 = addL.bindTo(bump13); + MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0)); + MethodHandle add13_1 = addL.bindTo(opI2(0, bump13)); + assertEqualFunction(sequence(opL2, add13_0), + filterArguments(opL2, 0, add13)); + assertEqualFunction(sequence(opL2, add13_1), + filterArguments(opL2, 1, add13)); + System.out.println("[testFilterObject done]"); + } + + @Test + public void testBoxLong() throws Throwable { + if (!startTest("testBoxLong")) return; + assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type())); + } + + @Test + public void testFilterInteger() throws Throwable { + if (!startTest("testFilterInteger")) return; + assertEqualFunction(opI, sequence(convI_L, opL_I)); + } + + @Test + public void testIntSpreads() throws Throwable { + if (!startTest("testIntSpreads")) return; + MethodHandle id = identity(int[].class); + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = 0; nargs <= MAX; nargs++) { + if (nargs > 30 && nargs < MAX-20) nargs += 10; + int[] args = new int[nargs]; + for (int j = 0; j < args.length; j++) args[j] = (int)(j + 11); + //System.out.println("testIntSpreads "+Arrays.toString(args)); + int[] args1 = (int[]) id.invokeExact(args); + assertArrayEquals(args, args1); + MethodHandle coll = id.asCollector(int[].class, nargs); + int[] args2 = args; + switch (nargs) { + case 0: args2 = (int[]) coll.invokeExact(); break; + case 1: args2 = (int[]) coll.invokeExact(args[0]); break; + case 2: args2 = (int[]) coll.invokeExact(args[0], args[1]); break; + case 3: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break; + case 4: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break; + case 5: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break; + } + assertArrayEquals(args, args2); + MethodHandle mh = coll.asSpreader(int[].class, nargs); + int[] args3 = (int[]) mh.invokeExact(args); + assertArrayEquals(args, args3); + } + } + + @Test + public void testByteSpreads() throws Throwable { + if (!startTest("testByteSpreads")) return; + MethodHandle id = identity(byte[].class); + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = 0; nargs <= MAX; nargs++) { + if (nargs > 30 && nargs < MAX-20) nargs += 10; + byte[] args = new byte[nargs]; + for (int j = 0; j < args.length; j++) args[j] = (byte)(j + 11); + //System.out.println("testByteSpreads "+Arrays.toString(args)); + byte[] args1 = (byte[]) id.invokeExact(args); + assertArrayEquals(args, args1); + MethodHandle coll = id.asCollector(byte[].class, nargs); + byte[] args2 = args; + switch (nargs) { + case 0: args2 = (byte[]) coll.invokeExact(); break; + case 1: args2 = (byte[]) coll.invokeExact(args[0]); break; + case 2: args2 = (byte[]) coll.invokeExact(args[0], args[1]); break; + case 3: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break; + case 4: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break; + case 5: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break; + } + assertArrayEquals(args, args2); + MethodHandle mh = coll.asSpreader(byte[].class, nargs); + byte[] args3 = (byte[]) mh.invokeExact(args); + assertArrayEquals(args, args3); + } + } + + @Test + public void testLongSpreads() throws Throwable { + if (!startTest("testLongSpreads")) return; + MethodHandle id = identity(long[].class); + final int MAX = (MAX_ARITY - 2) / 2; // 253/2+1 would cause parameter overflow with 'this' added + for (int nargs = 0; nargs <= MAX; nargs++) { + if (nargs > 30 && nargs < MAX-20) nargs += 10; + long[] args = new long[nargs]; + for (int j = 0; j < args.length; j++) args[j] = (long)(j + 11); + //System.out.println("testLongSpreads "+Arrays.toString(args)); + long[] args1 = (long[]) id.invokeExact(args); + assertArrayEquals(args, args1); + MethodHandle coll = id.asCollector(long[].class, nargs); + long[] args2 = args; + switch (nargs) { + case 0: args2 = (long[]) coll.invokeExact(); break; + case 1: args2 = (long[]) coll.invokeExact(args[0]); break; + case 2: args2 = (long[]) coll.invokeExact(args[0], args[1]); break; + case 3: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break; + case 4: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break; + case 5: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break; + } + assertArrayEquals(args, args2); + MethodHandle mh = coll.asSpreader(long[].class, nargs); + long[] args3 = (long[]) mh.invokeExact(args); + assertArrayEquals(args, args3); + } + } + + @Test + public void testIntCollects() throws Throwable { + if (!startTest("testIntCollects")) return; + for (MethodHandle lister : INT_LISTERS) { + int outputs = lister.type().parameterCount(); + for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) { + int inputs = outputs - 1 + collects; + if (inputs < 0) continue; + for (int pos = 0; pos + collects <= inputs; pos++) { + MethodHandle collector = INT_COLLECTORS[collects]; + int[] args = new int[inputs]; + int ap = 0, arg = 31; + for (int i = 0; i < pos; i++) + args[ap++] = arg++ + 0; + for (int i = 0; i < collects; i++) + args[ap++] = arg++ + 10; + while (ap < args.length) + args[ap++] = arg++ + 20; + // calculate piecemeal: + //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args)); + int[] collargs = Arrays.copyOfRange(args, pos, pos+collects); + int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs); + int[] listargs = Arrays.copyOfRange(args, 0, outputs); + System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1)); + listargs[pos] = coll; + //System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs)); + Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs); + //System.out.println(" expect="+expect); + + // now use the combined MH, and test the output: + MethodHandle mh = collectArguments(lister, pos, INT_COLLECTORS[collects]); + if (mh == null) continue; // no infix collection, yet + assert(mh.type().parameterCount() == inputs); + Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args); + assertEquals(expect, observe); + } + } + } + } + + private static MethodHandle collectArguments(MethodHandle lister, int pos, MethodHandle collector) { + int collects = collector.type().parameterCount(); + int outputs = lister.type().parameterCount(); + if (pos == outputs - 1) + return MethodHandles.filterArguments(lister, pos, + collector.asSpreader(int[].class, collects)) + .asCollector(int[].class, collects); + //return MethodHandles.collectArguments(lister, pos, collector); //no such animal + return null; + } + + private static final Class[] RETURN_TYPES = { + Object.class, String.class, Integer.class, + int.class, long.class, + boolean.class, byte.class, char.class, short.class, + float.class, double.class, + void.class, + }; + + @Test + public void testReturns() throws Throwable { + if (!startTest("testReturns")) return; + // fault injection: + int faultCount = 0; // total of 1296 tests + faultCount = Integer.getInteger("testReturns.faultCount", 0); + for (Class ret : RETURN_TYPES) { + // make a complicated identity function and pass something through it + System.out.println(ret.getSimpleName()); + Class vret = (ret == void.class) ? Void.class : ret; + MethodHandle id = // (vret)->ret + identity(vret).asType(methodType(ret, vret)); + final int LENGTH = 4; + int[] index = {0}; + Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH); + MethodHandle indexGetter = //()->int + insertArguments(arrayElementGetter(index.getClass()), 0, index, 0); + MethodHandle valSelector = // (int)->vret + arrayElementGetter(vals.getClass()).bindTo(vals); + MethodHandle valGetter = // ()->vret + foldArguments(valSelector, indexGetter); + if (ret != void.class) { + for (int i = 0; i < LENGTH; i++) { + Object val = (i + 50); + if (ret == boolean.class) val = (i % 3 == 0); + if (ret == String.class) val = "#"+i; + if (ret == char.class) val = (char)('a'+i); + if (ret == byte.class) val = (byte)~i; + if (ret == short.class) val = (short)(1< "+val); + index[0] = i; + if (--faultCount == 0) index[0] ^= 1; + Object x = valGetter.invokeWithArguments(); + assertEquals(val, x); + // make a return-filter call: x = id(valGetter()) + if (--faultCount == 0) index[0] ^= 1; + x = filterReturnValue(valGetter, id).invokeWithArguments(); + assertEquals(val, x); + // make a filter call: x = id(*,valGetter(),*) + for (int len = 1; len <= 4; len++) { + for (int pos = 0; pos < len; pos++) { + MethodHandle proj = id; // lambda(..., vret x,...){x} + for (int j = 0; j < len; j++) { + if (j == pos) continue; + proj = dropArguments(proj, j, Object.class); + } + assert(proj.type().parameterCount() == len); + // proj: (Object*, pos: vret, Object*)->ret + assertEquals(vret, proj.type().parameterType(pos)); + MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class); + if (--faultCount == 0) index[0] ^= 1; + x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]); + assertEquals(val, x); + } + } + // make a fold call: + for (int len = 0; len <= 4; len++) { + for (int fold = 0; fold <= len; fold++) { + MethodHandle proj = id; // lambda(ret x, ...){x} + if (ret == void.class) proj = constant(Object.class, null); + int arg0 = (ret == void.class ? 0 : 1); + for (int j = 0; j < len; j++) { + proj = dropArguments(proj, arg0, Object.class); + } + assert(proj.type().parameterCount() == arg0 + len); + // proj: (Object*, pos: vret, Object*)->ret + if (arg0 != 0) assertEquals(vret, proj.type().parameterType(0)); + MethodHandle vgFilter = valGetter.asType(methodType(ret)); + for (int j = 0; j < fold; j++) { + vgFilter = dropArguments(vgFilter, j, Object.class); + } + x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]); + if (--faultCount == 0) index[0] ^= 1; + assertEquals(val, x); + } + } + } + } + //System.out.println("faultCount="+faultCount); + } + + private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) { + MethodHandle res = mh1; + for (MethodHandle mh2 : mhs) + res = filterReturnValue(res, mh2); + return res; + } + private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable { + assertEquals(x.type(), y.type()); //?? + MethodType t = x.type(); + if (t.parameterCount() == 0) { + assertEqualFunctionAt(null, x, y); + return; + } + Class ptype = t.parameterType(0); + if (ptype == long.class || ptype == Long.class) { + for (long i = -10; i <= 10; i++) { + assertEqualFunctionAt(i, x, y); + } + } else { + for (int i = -10; i <= 10; i++) { + assertEqualFunctionAt(i, x, y); + } + } + } + private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable { + Object[] args = new Object[x.type().parameterCount()]; + Arrays.fill(args, v); + Object xval = invokeWithCatch(x, args); + Object yval = invokeWithCatch(y, args); + String msg = "ok"; + if (!Objects.equals(xval, yval)) { + msg = ("applying "+x+" & "+y+" to "+v); + } + assertEquals(msg, xval, yval); + } + private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable { + try { + return mh.invokeWithArguments(args); + } catch (Throwable ex) { + System.out.println("threw: "+mh+Arrays.asList(args)); + ex.printStackTrace(); + return ex; + } + } + + private static final Lookup LOOKUP = lookup(); + private static MethodHandle findStatic(String name, + Class rtype, + Class... ptypes) { + try { + return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes)); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + } + private static MethodHandle findStatic(String name, + Class rtype, + List ptypes) { + return findStatic(name, rtype, ptypes.toArray(new Class[ptypes.size()])); + } + static int getProperty(String name, int dflt) { + String qual = LOOKUP.lookupClass().getName(); + String prop = System.getProperty(qual+"."+name); + if (prop == null) prop = System.getProperty(name); + if (prop == null) return dflt; + return Integer.parseInt(prop); + } + + private static int opI(int... xs) { + stress(); + int base = 100; + int z = 0; + for (int x : xs) { + z = (z * base) + (x % base); + } + verbose("opI", xs.length, xs, z); + return z; + } + private static int opI2(int x, int y) { return opI(x, y); } // x*100 + y%100 + private static int opI3(int x, int y, int z) { return opI(x, y, z); } + private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); } + private static int opI(int x) { return opI2(x, 37); } + private static Object opI_L(int x) { return (Object) opI(x); } + private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); } + private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); } + private static long opJ(long x) { return (long) opI((int)x); } + private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); } + private static Object opL(Object x) { return (Object) opI((int)x); } + private static int opL2_I(Object x, Object y) { return (int) opI2((int)x, (int)y); } + private static int opL_I(Object x) { return (int) opI((int)x); } + private static long opL_J(Object x) { return (long) opI((int)x); } + private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J; + static { + opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class); + opI3 = findStatic("opI3", int.class, int.class, int.class, int.class); + opI2 = findStatic("opI2", int.class, int.class, int.class); + opI = findStatic("opI", int.class, int.class); + opI_L = findStatic("opI_L", Object.class, int.class); + opJ = findStatic("opJ", long.class, long.class); + opJ2 = findStatic("opJ2", long.class, long.class, long.class); + opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class); + opL2 = findStatic("opL2", Object.class, Object.class, Object.class); + opL = findStatic("opL", Object.class, Object.class); + opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class); + opL_I = findStatic("opL_I", int.class, Object.class); + opL_J = findStatic("opL_J", long.class, Object.class); + } + private static final MethodHandle[] INT_COLLECTORS = { + constant(int.class, 42), opI, opI2, opI3, opI4 + }; + private static final MethodHandle[] LONG_COLLECTORS = { + constant(long.class, 42), opJ, opJ2, opJ3 + }; + + private static int addI(int x, int y) { stress(); return x+y; } + private static Object addL(Object x, Object y) { return addI((int)x, (int)y); } + private static final MethodHandle addI, addL; + static { + addI = findStatic("addI", int.class, int.class, int.class); + addL = findStatic("addL", Object.class, Object.class, Object.class); + } + + private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) { + return Arrays.asList(a, b, c, d, e, f, g, h); + } + private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) { + return Arrays.asList(a, b, c, d, e, f, g, h); + } + private static final MethodHandle list8ints = findStatic("list8ints", Object.class, + Collections.nCopies(8, int.class)); + private static final MethodHandle list8longs = findStatic("list8longs", Object.class, + Collections.nCopies(8, long.class)); + private static final MethodHandle[] INT_LISTERS, LONG_LISTERS; + static { + int listerCount = list8ints.type().parameterCount() + 1; + INT_LISTERS = new MethodHandle[listerCount]; + LONG_LISTERS = new MethodHandle[listerCount]; + MethodHandle lister = list8ints; + MethodHandle llister = list8longs; + for (int i = listerCount - 1; ; i--) { + INT_LISTERS[i] = lister; + LONG_LISTERS[i] = llister; + if (i == 0) break; + lister = insertArguments(lister, i-1, (int)0); + llister = insertArguments(llister, i-1, (long)0); + } + } + + private static Object convI_L(int x) { stress(); return (Object) x; } + private static int convL_I(Object x) { stress(); return (int) x; } + private static Object convJ_L(long x) { stress(); return (Object) x; } + private static long convL_J(Object x) { stress(); return (long) x; } + private static int convJ_I(long x) { stress(); return (int) x; } + private static long convI_J(int x) { stress(); return (long) x; } + private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J; + static { + convI_L = findStatic("convI_L", Object.class, int.class); + convL_I = findStatic("convL_I", int.class, Object.class); + convJ_L = findStatic("convJ_L", Object.class, long.class); + convL_J = findStatic("convL_J", long.class, Object.class); + convJ_I = findStatic("convJ_I", int.class, long.class); + convI_J = findStatic("convI_J", long.class, int.class); + } + + // stress modes: + private static final int REPEAT = getProperty("REPEAT", 0); + private static final int STRESS = getProperty("STRESS", 0); + private static /*v*/ int STRESS_COUNT; + private static final Object[] SINK = new Object[4]; + private static void stress() { + if (STRESS <= 0) return; + int count = STRESS + (STRESS_COUNT++ & 0x1); // non-constant value + for (int i = 0; i < count; i++) { + SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)]; + } + } + + // verbosity: + private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1); + private static void verbose(Object a, Object b, Object c, Object d) { + if (VERBOSITY <= 0) return; + verbose(1, a, b, c, d); + } + private static void verbose(Object a, Object b, Object c) { + if (VERBOSITY <= 0) return; + verbose(1, a, b, c); + } + private static void verbose(int level, Object a, Object... bcd) { + if (level > VERBOSITY) return; + String m = a.toString(); + if (bcd != null && bcd.length > 0) { + List l = new ArrayList<>(bcd.length); + for (Object x : bcd) { + if (x instanceof Object[]) x = Arrays.asList((Object[])x); + if (x instanceof int[]) x = Arrays.toString((int[])x); + if (x instanceof long[]) x = Arrays.toString((long[])x); + l.add(x); + } + m = m+Arrays.asList(bcd); + } + System.out.println(m); + } + String testOnly; + String testOnlyTests; + private boolean startTest(String name) { + if (testOnly != null && !testOnly.contains(name)) + return false; + verbose(0, "["+name+"]"); + testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name; + return true; + } + +} diff --git a/test/java/text/Bidi/Bug7041232.java b/test/java/text/Bidi/Bug7041232.java new file mode 100644 index 0000000000000000000000000000000000000000..f8e84e293c476b1a24b16092b63967cc9acb28b9 --- /dev/null +++ b/test/java/text/Bidi/Bug7041232.java @@ -0,0 +1,48 @@ +/* + * 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 7041232 + * @summary verify that an unexpected exception isn't thrown for unnatural data to keep backward compatibility with JDK 6. + */ +import java.text.*; + +public class Bug7041232 { + + public static void main(String[] args) { + String UnicodeChars; + StringBuffer sb = new StringBuffer(); + + // Generates String which includes U+2028(line separator) and + // U+2029(paragraph separator) + for (int i = 0x2000; i < 0x2100; i++) { + sb.append((char)i); + } + UnicodeChars = sb.toString(); + + Bidi bidi = new Bidi(UnicodeChars, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + bidi.createLineBidi(0, UnicodeChars.length()); + } + +} diff --git a/test/javax/swing/JComboBox/7031551/bug7031551.java b/test/javax/swing/JComboBox/7031551/bug7031551.java new file mode 100644 index 0000000000000000000000000000000000000000..e246805c628218d72c4ba1e02021f2076b60166c --- /dev/null +++ b/test/javax/swing/JComboBox/7031551/bug7031551.java @@ -0,0 +1,123 @@ +/* + * 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 7031551 + * @summary Generics: JComboBox + * @author Pavel Porvatov + */ + +import javax.swing.*; +import java.util.Vector; + +public class bug7031551 { + + private static final String TEST_ELEMENT1 = "Test1"; + private static final String TEST_ELEMENT2 = "Test2"; + private static final String TEST_ELEMENT3 = "Test3"; + + /** + * @param args the command line arguments + */ + @SuppressWarnings("unchecked") + public static void main(String[] args) { + testRawSignatures(); + testGenericSignatures(); + } + + @SuppressWarnings("unchecked") + private static void testRawSignatures() { + // Test JComboBox + ComboBoxModel rawTestModel = new DefaultComboBoxModel(); + + JComboBox rawTestComboBox = new JComboBox(); + rawTestComboBox = new JComboBox(rawTestModel); + rawTestComboBox = new JComboBox(new Object[]{TEST_ELEMENT1}); + rawTestComboBox = new JComboBox(new Vector()); + + Object unused1 = rawTestComboBox.getPrototypeDisplayValue(); + rawTestComboBox.setPrototypeDisplayValue(TEST_ELEMENT1); + + ListCellRenderer unused2 = rawTestComboBox.getRenderer(); + rawTestComboBox.setRenderer(new DefaultListCellRenderer()); + + ComboBoxModel unused3 = rawTestComboBox.getModel(); + rawTestComboBox.setModel(rawTestModel); + + rawTestComboBox.addItem(TEST_ELEMENT2); + rawTestComboBox.insertItemAt(TEST_ELEMENT3, 1); + rawTestComboBox.removeItem(TEST_ELEMENT2); + assertEquals(rawTestComboBox.getItemAt(0), TEST_ELEMENT3); + rawTestComboBox.removeAllItems(); + + // Test DefaultComboBoxModel + DefaultComboBoxModel testModel = new DefaultComboBoxModel(); + testModel = new DefaultComboBoxModel(new Vector()); + testModel = new DefaultComboBoxModel(new Object[]{TEST_ELEMENT1}); + + testModel.addElement(TEST_ELEMENT2); + testModel.insertElementAt(TEST_ELEMENT3, 1); + assertEquals(testModel.getElementAt(2), TEST_ELEMENT2); + } + + private static void testGenericSignatures() { + // Test JComboBox + ComboBoxModel stringTestModel = new DefaultComboBoxModel(); + + JComboBox stringTestComboBox = new JComboBox(); + stringTestComboBox = new JComboBox(stringTestModel); + stringTestComboBox = new JComboBox(new String[]{TEST_ELEMENT1}); + stringTestComboBox = new JComboBox(new Vector()); + + String unused1 = stringTestComboBox.getPrototypeDisplayValue(); + stringTestComboBox.setPrototypeDisplayValue(TEST_ELEMENT1); + + ListCellRenderer unused2 = stringTestComboBox.getRenderer(); + stringTestComboBox.setRenderer(new DefaultListCellRenderer()); + + ComboBoxModel unused3 = stringTestComboBox.getModel(); + stringTestComboBox.setModel(stringTestModel); + + stringTestComboBox.addItem(TEST_ELEMENT2); + stringTestComboBox.insertItemAt(TEST_ELEMENT3, 1); + stringTestComboBox.removeItem(TEST_ELEMENT2); + assertEquals(stringTestComboBox.getItemAt(0), TEST_ELEMENT3); + stringTestComboBox.removeAllItems(); + + // Test DefaultComboBoxModel + DefaultComboBoxModel testModel = new DefaultComboBoxModel(); + testModel = new DefaultComboBoxModel(new Vector()); + testModel = new DefaultComboBoxModel(new String[]{TEST_ELEMENT1}); + + testModel.addElement(TEST_ELEMENT2); + testModel.insertElementAt(TEST_ELEMENT3, 1); + assertEquals(testModel.getElementAt(2), TEST_ELEMENT2); + } + + private static void assertEquals(Object expectedObject, Object actualObject) { + if (!expectedObject.equals(actualObject)) { + throw new RuntimeException("Expected: " + expectedObject + " but was: " + actualObject); + } + } +} + diff --git a/test/javax/swing/JTable/6788484/bug6788484.java b/test/javax/swing/JTable/6788484/bug6788484.java index ded6f1fb72e115030160e30d00e62f2771061960..392bc945042dac109d19c73e49f09256e2fd0bae 100644 --- a/test/javax/swing/JTable/6788484/bug6788484.java +++ b/test/javax/swing/JTable/6788484/bug6788484.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 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 @@ -24,9 +24,17 @@ /* @test @bug 6788484 @summary NPE in DefaultTableCellHeaderRenderer.getColumnSortOrder() with null table + @compile -XDignore.symbol.file=true bug6788484.java @author Alexander Potochkin @run main bug6788484 */ + +/* + * Compile with -XDignore.symbol.file=true option as a workaround for + * specific behaviour described in 6380059 which restricts proprietary + * package loading + */ + import sun.swing.table.DefaultTableCellHeaderRenderer; import javax.swing.*; diff --git a/test/sun/invoke/util/ValueConversionsTest.java b/test/sun/invoke/util/ValueConversionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6b1253e77b296ce544fe69099c0da56bf92557ac --- /dev/null +++ b/test/sun/invoke/util/ValueConversionsTest.java @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2009, 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. + */ + +package test.sun.invoke.util; + +import sun.invoke.util.ValueConversions; +import sun.invoke.util.Wrapper; +import java.lang.invoke.MethodType; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/* @test + * @summary unit tests for value-type conversion utilities + * @ignore This test requires a special compilation environment to access sun.inovke.util. Run by hand. + * @run junit/othervm test.sun.invoke.util.ValueConversionsTest + * @run junit/othervm + * -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250 + * test.sun.invoke.util.ValueConversionsTest + */ + +// This might take a while and burn lots of metadata: +// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest + +/** + * + * @author jrose + */ +public class ValueConversionsTest { + private static final Class CLASS = ValueConversionsTest.class; + private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); + private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0); + private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE"); + + @Test + public void testUnbox() throws Throwable { + testUnbox(false); + } + + @Test + public void testUnboxCast() throws Throwable { + testUnbox(true); + } + + private void testUnbox(boolean doCast) throws Throwable { + //System.out.println("unbox"); + for (Wrapper dst : Wrapper.values()) { + //System.out.println(dst); + for (Wrapper src : Wrapper.values()) { + testUnbox(doCast, dst, src); + } + } + } + + private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable { + boolean expectThrow = !doCast && !dst.isConvertibleFrom(src); + if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT) return; // must have prims + if (dst == Wrapper.OBJECT) + expectThrow = false; // everything (even VOID==null here) converts to OBJECT + try { + for (int n = -5; n < 10; n++) { + Object box = src.wrap(n); + switch (src) { + case VOID: assertEquals(box, null); break; + case OBJECT: box = box.toString(); break; + case SHORT: assertEquals(box.getClass(), Short.class); break; + default: assertEquals(box.getClass(), src.wrapperType()); break; + } + MethodHandle unboxer; + if (doCast) + unboxer = ValueConversions.unboxCast(dst.primitiveType()); + else + unboxer = ValueConversions.unbox(dst.primitiveType()); + Object expResult = (box == null) ? dst.zero() : dst.wrap(box); + Object result = null; + switch (dst) { + case INT: result = (int) unboxer.invokeExact(box); break; + case LONG: result = (long) unboxer.invokeExact(box); break; + case FLOAT: result = (float) unboxer.invokeExact(box); break; + case DOUBLE: result = (double) unboxer.invokeExact(box); break; + case CHAR: result = (char) unboxer.invokeExact(box); break; + case BYTE: result = (byte) unboxer.invokeExact(box); break; + case SHORT: result = (short) unboxer.invokeExact(box); break; + case OBJECT: result = (Object) unboxer.invokeExact(box); break; + case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break; + case VOID: result = null; unboxer.invokeExact(box); break; + } + if (expectThrow) { + expResult = "(need an exception)"; + } + assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box), + expResult, result); + } + } catch (RuntimeException ex) { + if (expectThrow) return; + System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src)); + throw ex; + } + } + + @Test + public void testUnboxRaw() throws Throwable { + //System.out.println("unboxRaw"); + for (Wrapper w : Wrapper.values()) { + if (w == Wrapper.OBJECT) continue; // skip this; no raw form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + long expResult = w.unwrapRaw(box); + Object box2 = w.wrapRaw(expResult); + assertEquals(box, box2); + MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType()); + long result = -1; + switch (w) { + case INT: result = (int) unboxer.invokeExact(box); break; + case LONG: result = (long) unboxer.invokeExact(box); break; + case FLOAT: result = (int) unboxer.invokeExact(box); break; + case DOUBLE: result = (long) unboxer.invokeExact(box); break; + case CHAR: result = (int) unboxer.invokeExact(box); break; + case BYTE: result = (int) unboxer.invokeExact(box); break; + case SHORT: result = (int) unboxer.invokeExact(box); break; + case BOOLEAN: result = (int) unboxer.invokeExact(box); break; + case VOID: result = (int) unboxer.invokeExact(box); break; + } + assertEquals("(w,n,box)="+Arrays.asList(w,n,box), + expResult, result); + } + } + } + + @Test + public void testBox() throws Throwable { + //System.out.println("box"); + for (Wrapper w : Wrapper.values()) { + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + MethodHandle boxer = ValueConversions.box(w.primitiveType()); + Object expResult = box; + Object result = null; + switch (w) { + case INT: result = boxer.invokeExact((int)n); break; + case LONG: result = boxer.invokeExact((long)n); break; + case FLOAT: result = boxer.invokeExact((float)n); break; + case DOUBLE: result = boxer.invokeExact((double)n); break; + case CHAR: result = boxer.invokeExact((char)n); break; + case BYTE: result = boxer.invokeExact((byte)n); break; + case SHORT: result = boxer.invokeExact((short)n); break; + case OBJECT: result = boxer.invokeExact((Object)n); break; + case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break; + } + assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), + expResult, result); + } + } + } + + @Test + public void testBoxRaw() throws Throwable { + //System.out.println("boxRaw"); + for (Wrapper w : Wrapper.values()) { + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + if (w == Wrapper.OBJECT) continue; // skip this; no raw form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + long raw = w.unwrapRaw(box); + Object expResult = box; + MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType()); + Object result = null; + switch (w) { + case INT: result = boxer.invokeExact((int)raw); break; + case LONG: result = boxer.invokeExact(raw); break; + case FLOAT: result = boxer.invokeExact((int)raw); break; + case DOUBLE: result = boxer.invokeExact(raw); break; + case CHAR: result = boxer.invokeExact((int)raw); break; + case BYTE: result = boxer.invokeExact((int)raw); break; + case SHORT: result = boxer.invokeExact((int)raw); break; + case BOOLEAN: result = boxer.invokeExact((int)raw); break; + } + assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), + expResult, result); + } + } + } + + @Test + public void testReboxRaw() throws Throwable { + //System.out.println("reboxRaw"); + for (Wrapper w : Wrapper.values()) { + Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType()); + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + if (w == Wrapper.OBJECT) continue; // skip this; no raw form + //System.out.println(w); + for (int n = -5; n < 10; n++) { + Object box = w.wrap(n); + Object raw = pw.wrap(w.unwrapRaw(box)); + Object expResult = box; + MethodHandle boxer = ValueConversions.rebox(w.primitiveType()); + Object result = null; + switch (w) { + case INT: result = boxer.invokeExact(raw); break; + case LONG: result = boxer.invokeExact(raw); break; + case FLOAT: result = boxer.invokeExact(raw); break; + case DOUBLE: result = boxer.invokeExact(raw); break; + case CHAR: result = boxer.invokeExact(raw); break; + case BYTE: result = boxer.invokeExact(raw); break; + case SHORT: result = boxer.invokeExact(raw); break; + case BOOLEAN: result = boxer.invokeExact(raw); break; + } + assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), + expResult, result); + } + } + } + + @Test + public void testCast() throws Throwable { + //System.out.println("cast"); + Class[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class }; + Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 }; + for (Class dst : types) { + MethodHandle caster = ValueConversions.cast(dst); + assertEquals(caster.type(), ValueConversions.identity().type()); + for (Object obj : objects) { + Class src = obj.getClass(); + boolean canCast; + if (dst.isInterface()) { + canCast = true; + } else { + canCast = dst.isAssignableFrom(src); + assertEquals(canCast, dst.isInstance(obj)); + } + //System.out.println("obj="+obj+" <: dst="+dst); + try { + Object result = caster.invokeExact(obj); + if (canCast) + assertEquals(obj, result); + else + assertEquals("cast should not have succeeded", dst, obj); + } catch (ClassCastException ex) { + if (canCast) + throw ex; + } + } + } + } + + @Test + public void testIdentity() throws Throwable { + //System.out.println("identity"); + MethodHandle id = ValueConversions.identity(); + Object expResult = "foo"; + Object result = id.invokeExact(expResult); + // compiler bug: ValueConversions.identity().invokeExact("bar"); + assertEquals(expResult, result); + } + + @Test + public void testVarargsArray() throws Throwable { + //System.out.println("varargsArray"); + final int MIN = START_ARITY; + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) { + MethodHandle target = ValueConversions.varargsArray(nargs); + Object[] args = new Object[nargs]; + for (int i = 0; i < nargs; i++) + args[i] = "#"+i; + Object res = target.invokeWithArguments(args); + assertArrayEquals(args, (Object[])res); + } + } + + @Test + public void testVarargsReferenceArray() throws Throwable { + //System.out.println("varargsReferenceArray"); + testTypedVarargsArray(Object[].class); + testTypedVarargsArray(String[].class); + testTypedVarargsArray(Number[].class); + } + + @Test + public void testVarargsPrimitiveArray() throws Throwable { + //System.out.println("varargsPrimitiveArray"); + testTypedVarargsArray(int[].class); + testTypedVarargsArray(long[].class); + testTypedVarargsArray(byte[].class); + testTypedVarargsArray(boolean[].class); + testTypedVarargsArray(short[].class); + testTypedVarargsArray(char[].class); + testTypedVarargsArray(float[].class); + testTypedVarargsArray(double[].class); + } + + private static int nextArgCount(int nargs, int density, int MAX) { + if (EXHAUSTIVE) return nargs + 1; + if (nargs >= MAX) return Integer.MAX_VALUE; + int BOT = 20, TOP = MAX-5; + if (density < 10) { BOT = 10; MAX = TOP-2; } + if (nargs <= BOT || nargs >= TOP) { + ++nargs; + } else { + int bump = Math.max(1, 100 / density); + nargs += bump; + if (nargs > TOP) nargs = TOP; + } + return nargs; + } + + private void testTypedVarargsArray(Class arrayType) throws Throwable { + System.out.println(arrayType.getSimpleName()); + Class elemType = arrayType.getComponentType(); + int MIN = START_ARITY; + int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + int density = 3; + if (elemType == int.class || elemType == long.class) density = 7; + if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; } + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) { + Object[] args = makeTestArray(elemType, nargs); + MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs); + MethodType vaType = varargsArray.type(); + assertEquals(arrayType, vaType.returnType()); + if (nargs != 0) { + assertEquals(elemType, vaType.parameterType(0)); + assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1)); + } + assertEquals(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)), + vaType); + Object res = varargsArray.invokeWithArguments(args); + String resString = toArrayString(res); + assertEquals(Arrays.toString(args), resString); + + MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs); + MethodType stype = spreader.type(); + assert(stype == MethodType.methodType(arrayType, arrayType)); + if (nargs <= 5) { + // invoke target as a spreader also: + Object res2 = spreader.invokeWithArguments((Object)res); + String res2String = toArrayString(res2); + assertEquals(Arrays.toString(args), res2String); + // invoke the spreader on a generic Object[] array; check for error + try { + Object res3 = spreader.invokeWithArguments((Object)args); + String res3String = toArrayString(res3); + assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + assertEquals(Arrays.toString(args), res3String); + } catch (ClassCastException ex) { + assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + } + } + if (nargs == 0) { + // invoke spreader on null arglist + Object res3 = spreader.invokeWithArguments((Object)null); + String res3String = toArrayString(res3); + assertEquals(Arrays.toString(args), res3String); + } + } + } + + private static Object[] makeTestArray(Class elemType, int len) { + Wrapper elem = null; + if (elemType.isPrimitive()) + elem = Wrapper.forPrimitiveType(elemType); + else if (Wrapper.isWrapperType(elemType)) + elem = Wrapper.forWrapperType(elemType); + Object[] args = new Object[len]; + for (int i = 0; i < len; i++) { + Object arg = i * 100; + if (elem == null) { + if (elemType == String.class) + arg = "#"+arg; + arg = elemType.cast(arg); // just to make sure + } else { + switch (elem) { + case BOOLEAN: arg = (i % 3 == 0); break; + case CHAR: arg = 'a' + i; break; + case LONG: arg = (long)i * 1000_000_000; break; + case FLOAT: arg = (float)i / 100; break; + case DOUBLE: arg = (double)i / 1000_000; break; + } + arg = elem.cast(arg, elemType); + } + args[i] = arg; + } + //System.out.println(elemType.getName()+Arrays.toString(args)); + return args; + } + + private static String toArrayString(Object a) { + if (a == null) return "null"; + Class elemType = a.getClass().getComponentType(); + if (elemType == null) return a.toString(); + if (elemType.isPrimitive()) { + switch (Wrapper.forPrimitiveType(elemType)) { + case INT: return Arrays.toString((int[])a); + case BYTE: return Arrays.toString((byte[])a); + case BOOLEAN: return Arrays.toString((boolean[])a); + case SHORT: return Arrays.toString((short[])a); + case CHAR: return Arrays.toString((char[])a); + case FLOAT: return Arrays.toString((float[])a); + case LONG: return Arrays.toString((long[])a); + case DOUBLE: return Arrays.toString((double[])a); + } + } + return Arrays.toString((Object[])a); + } + + @Test + public void testVarargsList() throws Throwable { + //System.out.println("varargsList"); + final int MIN = START_ARITY; + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) { + MethodHandle target = ValueConversions.varargsList(nargs); + Object[] args = new Object[nargs]; + for (int i = 0; i < nargs; i++) + args[i] = "#"+i; + Object res = target.invokeWithArguments(args); + assertEquals(Arrays.asList(args), res); + } + } +}