diff --git a/.hgtags b/.hgtags index 453018dc5fe1c22116edef017f7bf217006b7b7b..39177739d9e7bbd9f270e87427051c6e9cf3d912 100644 --- a/.hgtags +++ b/.hgtags @@ -105,3 +105,5 @@ ac311eb325bfc763698219252bf3cee9e091f3af jdk7-b122 f08682e23279d6cccbdcafda1eb0647ba4900874 jdk7-b128 14cd5d54a8d0b9c368d60ea83a066735b9931015 jdk7-b129 bdc069d3f9101f89ec3f81c2950ee2d68fa846d3 jdk7-b130 +8ac52c85f9e91336dc00b52ef90b42eecf3230b3 jdk7-b131 +6bbc7a4734952ae7604578f270e1566639fa8752 jdk7-b132 diff --git a/LICENSE b/LICENSE index eeab58c21c9a42ab1dfe8a17a9aed6607100bec2..b40a0f457d75c638172ceb89da9b91d17c7b7fe3 100644 --- a/LICENSE +++ b/LICENSE @@ -325,11 +325,11 @@ License instead of this License. "CLASSPATH" EXCEPTION TO THE GPL -Certain source files distributed by Sun Microsystems, Inc. are subject to -the following clarification and special exception to the GPL, but only where -Sun has expressly included in the particular source file's header the words -"Sun designates this particular file as subject to the "Classpath" exception -as provided by Sun in the LICENSE file that accompanied this code." +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of diff --git a/make/common/Defs-windows.gmk b/make/common/Defs-windows.gmk index 7899b0e668de22375fe587effaaca0faa2114a39..87bc793596dcd6b3877cbcdc4ab9d117be3eae4e 100644 --- a/make/common/Defs-windows.gmk +++ b/make/common/Defs-windows.gmk @@ -359,7 +359,13 @@ ifeq ($(CC_VERSION),msvc) # VS2008 has bufferoverflow baked in: LFLAGS_VS2008 = - LFLAGS_VS2010 = + + # VS2010, always need safe exception handlers, not needed on 64bit + ifeq ($(ARCH_DATA_MODEL), 32) + LFLAGS_VS2010 = -SAFESEH + else + LFLAGS_VS2010 = + endif # LFLAGS are the flags given to $(LINK) and used to build the actual DLL file BASELFLAGS = -nologo /opt:REF /incremental:no diff --git a/make/common/Release.gmk b/make/common/Release.gmk index 9af023f2c54139e2dab2f09709d082bc491cd35f..224dcd1650b38aa8119688f32e26e29965b4841b 100644 --- a/make/common/Release.gmk +++ b/make/common/Release.gmk @@ -124,9 +124,11 @@ JRE_MAN_PAGES = \ tnameserv.1 \ unpack200.1 +ifndef OPENJDK ifeq ($(ARCH_DATA_MODEL),32) JRE_MAN_PAGES += javaws.1 endif +endif JDK_MAN_PAGES = \ $(JRE_MAN_PAGES) \ diff --git a/make/common/shared/Defs-windows.gmk b/make/common/shared/Defs-windows.gmk index 461913ac9dc5097648dd72764cf8eae6ac041615..d928448b48f01bcfbcf2b726be140b5cf5c4ca6e 100644 --- a/make/common/shared/Defs-windows.gmk +++ b/make/common/shared/Defs-windows.gmk @@ -772,9 +772,20 @@ else BANNED_DLLS=msvcp100[.]dll|msvcr100d[.]dll|msvcrtd[.]dll endif -# Macro to check it's input file for banned dependencies and verify the -# binary was built properly. Relies on process exit code. -define binary_file_verification # binary_file +# Check for /safeseh (only used on 32bit) +define binary_file_safeseh_verification # binary_file +( \ + $(ECHO) "Checking for /SAFESEH usage in: $1" && \ + if [ "`$(DUMPBIN) /loadconfig $1 | $(EGREP) -i 'Safe Exception Handler Table'`" = "" ] ; then \ + $(ECHO) "ERROR: Did not find 'Safe Exception Handler Table' in loadconfig: $1" ; \ + $(DUMPBIN) /loadconfig $1 ; \ + exit 6 ; \ + fi ; \ +) +endef + +# Check for /NXCOMPAT usage +define binary_file_nxcompat_verification # binary_file ( \ $(ECHO) "Checking for /NXCOMPAT usage in: $1" && \ if [ "`$(DUMPBIN) /headers $1 | $(EGREP) -i 'NX compatible'`" = "" ] ; then \ @@ -782,12 +793,24 @@ define binary_file_verification # binary_file $(DUMPBIN) /headers $1 ; \ exit 7 ; \ fi ; \ +) +endef + +# Check for /DYNAMICBASE usage +define binary_file_dynamicbase_verification # binary_file +( \ $(ECHO) "Checking for /DYNAMICBASE usage in: $1" && \ if [ "`$(DUMPBIN) /headers $1 | $(EGREP) -i 'Dynamic base'`" = "" ] ; then \ $(ECHO) "ERROR: Did not find 'Dynamic base' in headers: $1" ; \ $(DUMPBIN) /headers $1 ; \ exit 8 ; \ fi ; \ +) +endef + +# Check for banned dll usage +define binary_file_dll_verification # binary_file +( \ $(ECHO) "Checking for banned dependencies in: $1" && \ if [ "`$(DUMPBIN) /dependents $1 | $(EGREP) -i '$(BANNED_DLLS)'`" != "" ] ; then \ $(ECHO) "ERROR: Found use of $(BANNED_DLLS)"; \ @@ -797,6 +820,27 @@ define binary_file_verification # binary_file ) endef +# Macro to check it's input file for properly built executables. +# Relies on process exit code. Different for 32bit vs 64bit. +ifeq ($(ARCH_DATA_MODEL),32) +define binary_file_verification # binary_file +( \ + $(call binary_file_safeseh_verification,$1); \ + $(call binary_file_nxcompat_verification,$1); \ + $(call binary_file_dynamicbase_verification,$1); \ + $(call binary_file_dll_verification,$1); \ +) +endef +else +define binary_file_verification # binary_file +( \ + $(call binary_file_nxcompat_verification,$1); \ + $(call binary_file_dynamicbase_verification,$1); \ + $(call binary_file_dll_verification,$1); \ +) +endef +endif + else # Macro to check it's input file for banned dependencies and verify the diff --git a/make/java/security/Makefile b/make/java/security/Makefile index 95c56767fae007441a511c7b72725cf4dfc0eff5..1d28b60f2ff0dadac2ec6d35f3e2e1ba1070d73b 100644 --- a/make/java/security/Makefile +++ b/make/java/security/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 1996, 2010 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1996, 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 @@ -65,6 +65,8 @@ CACERTS_BUILD = $(LIBDIR)/security/cacerts ifndef OPENJDK BLACKLIST_SRC = $(CLOSED_SHARE_SRC)/lib/security/blacklist BLACKLIST_BUILD = $(LIBDIR)/security/blacklist + TRUSTEDLIBS_SRC = $(CLOSED_SHARE_SRC)/lib/security/trusted.libraries + TRUSTEDLIBS_BUILD = $(LIBDIR)/security/trusted.libraries endif FILES_class = $(FILES_java:%.java=$(CLASSBINDIR)/%.class) @@ -77,7 +79,7 @@ include $(BUILDDIR)/common/Rules.gmk ifdef OPENJDK build: properties policy cacerts else -build: properties policy cacerts blacklist +build: properties policy cacerts blacklist trustedlibs endif install: all @@ -90,6 +92,8 @@ cacerts: classes $(CACERTS_BUILD) blacklist: classes $(BLACKLIST_BUILD) +trustedlibs: classes $(TRUSTEDLIBS_BUILD) + $(PROPS_BUILD): $(PROPS_SRC) $(install-file) @@ -102,9 +106,12 @@ $(CACERTS_BUILD): $(CACERTS_SRC) $(BLACKLIST_BUILD): $(BLACKLIST_SRC) $(install-file) +$(TRUSTEDLIBS_BUILD): $(TRUSTEDLIBS_SRC) + $(install-file) + clean clobber:: .delete.classlist $(RM) -r $(CLASSBINDIR)/java/security - $(RM) $(PROPS_BUILD) $(POLICY_BUILD) $(CACERTS_BUILD) $(BLACKLIST_BUILD) + $(RM) $(PROPS_BUILD) $(POLICY_BUILD) $(CACERTS_BUILD) $(BLACKLIST_BUILD) $(TRUSTEDLIBS_BUILD) # Additional Rule for building sun.security.util $(CLASSBINDIR)/%.class: $(SHARE_SRC)/sun/%.java diff --git a/make/sun/javazic/tzdata/VERSION b/make/sun/javazic/tzdata/VERSION index 5d158c9e6db90ba0601e1d5eb0a91af6938573e8..b82ee890a30866aa38dd80e72491336ba437a28c 100644 --- a/make/sun/javazic/tzdata/VERSION +++ b/make/sun/javazic/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2010o +tzdata2011b diff --git a/make/sun/javazic/tzdata/australasia b/make/sun/javazic/tzdata/australasia index 35ab9cfc9b8e48c037bd2e08eaca09f08606d126..430a6f146f5db8454c46ed950a1d85defb58112b 100644 --- a/make/sun/javazic/tzdata/australasia +++ b/make/sun/javazic/tzdata/australasia @@ -106,14 +106,13 @@ Rule AS 1986 only - Oct 19 2:00s 1:00 - Rule AS 1987 2007 - Oct lastSun 2:00s 1:00 - Rule AS 1972 only - Feb 27 2:00s 0 - Rule AS 1973 1985 - Mar Sun>=1 2:00s 0 - -Rule AS 1986 1989 - Mar Sun>=15 2:00s 0 - -Rule AS 1990 only - Mar Sun>=18 2:00s 0 - -Rule AS 1991 only - Mar Sun>=1 2:00s 0 - -Rule AS 1992 only - Mar Sun>=18 2:00s 0 - -Rule AS 1993 only - Mar Sun>=1 2:00s 0 - -Rule AS 1994 only - Mar Sun>=18 2:00s 0 - +Rule AS 1986 1990 - Mar Sun>=15 2:00s 0 - +Rule AS 1991 only - Mar 3 2:00s 0 - +Rule AS 1992 only - Mar 22 2:00s 0 - +Rule AS 1993 only - Mar 7 2:00s 0 - +Rule AS 1994 only - Mar 20 2:00s 0 - Rule AS 1995 2005 - Mar lastSun 2:00s 0 - -Rule AS 2006 only - Apr Sun>=1 2:00s 0 - +Rule AS 2006 only - Apr 2 2:00s 0 - Rule AS 2007 only - Mar lastSun 2:00s 0 - Rule AS 2008 max - Apr Sun>=1 2:00s 0 - Rule AS 2008 max - Oct Sun>=1 2:00s 1:00 - diff --git a/make/sun/javazic/tzdata/northamerica b/make/sun/javazic/tzdata/northamerica index d07c69c9b45979a79d1a88567d1ae7c3eec963ca..281ab4f6748312dfed8840ef86d06d6e2ddf8d28 100644 --- a/make/sun/javazic/tzdata/northamerica +++ b/make/sun/javazic/tzdata/northamerica @@ -368,6 +368,27 @@ Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 12:14:21 -7:00 US M%sT 2003 Oct 26 02:00 -6:00 US C%sT +# From Josh Findley (2011-01-21): +# ...it appears that Mercer County, North Dakota, changed from the +# mountain time zone to the central time zone at the last transition from +# daylight-saving to standard time (on Nov. 7, 2010): +# +# http://www.gpo.gov/fdsys/pkg/FR-2010-09-29/html/2010-24376.htm +# +# +# http://www.bismarcktribune.com/news/local/article_1eb1b588-c758-11df-b472-001cc4c03286.html +# + +# From Andy Lipscomb (2011-01-24): +# ...according to the Census Bureau, the largest city is Beulah (although +# it's commonly referred to as Beulah-Hazen, with Hazen being the next +# largest city in Mercer County). Google Maps places Beulah's city hall +# at 4715'51" north, 10146'40" west, which yields an offset of 6h47'07". + +Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 12:12:53 + -7:00 US M%sT 2010 Nov 7 2:00 + -6:00 US C%sT + # US mountain time, represented by Denver # # Colorado, far western Kansas, Montana, western @@ -493,20 +514,50 @@ Zone America/Adak 12:13:21 - LMT 1867 Oct 18 # three votes for and one against." # Hawaii -# -# From Arthur David Olson: -# And then there's Hawaii. -# DST was observed for one day in 1933; -# standard time was changed by half an hour in 1947; -# it's always standard as of 1986. -# -# From Paul Eggert: -# Shanks says the 1933 experiment lasted for three weeks. Go with Shanks. -# -Zone Pacific/Honolulu -10:31:26 - LMT 1900 Jan 1 12:00 - -10:30 - HST 1933 Apr 30 2:00 - -10:30 1:00 HDT 1933 May 21 2:00 - -10:30 US H%sT 1947 Jun 8 2:00 + +# From Arthur David Olson (2010-12-09): +# "Hawaiian Time" by Robert C. Schmitt and Doak C. Cox appears on pages 207-225 +# of volume 26 of The Hawaiian Journal of History (1992). As of 2010-12-09, +# the article is available at +# +# http://evols.library.manoa.hawaii.edu/bitstream/10524/239/2/JL26215.pdf +# +# and indicates that standard time was adopted effective noon, January +# 13, 1896 (page 218), that in "1933, the Legislature decreed daylight +# saving for the period between the last Sunday of each April and the +# last Sunday of each September, but less than a month later repealed the +# act," (page 220), that year-round daylight saving time was in effect +# from 1942-02-09 to 1945-09-30 (page 221, with no time of day given for +# when clocks changed) and that clocks were changed by 30 minutes +# effective the second Sunday of June, 1947 (page 219, with no time of +# day given for when clocks changed). A footnote for the 1933 changes +# cites Session Laws of Hawaii 1933, "Act. 90 (approved 26 Apr. 1933) +# and Act 163 (approved 21 May 1933)." + +# From Arthur David Olson (2011-01-19): +# The following is from "Laws of the Territory of Hawaii Passed by the +# Seventeenth Legislature: Regular Session 1933," available (as of +# 2011-01-19) at American University's Pence Law Library. Page 85: "Act +# 90...At 2 o'clock ante meridian of the last Sunday in April of each +# year, the standard time of this Territory shall be advanced one +# hour...This Act shall take effect upon its approval. Approved this 26th +# day of April, A. D. 1933. LAWRENCE M JUDD, Governor of the Territory of +# Hawaii." Page 172: "Act 163...Act 90 of the Session Laws of 1933 is +# hereby repealed...This Act shall take effect upon its approval, upon +# which date the standard time of this Territory shall be restored to +# that existing immediately prior to the taking effect of said Act 90. +# Approved this 21st day of May, A. D. 1933. LAWRENCE M. JUDD, Governor +# of the Territory of Hawaii." +# +# Note that 1933-05-21 was a Sunday. +# We're left to guess the time of day when Act 163 was approved; guess noon. + +Zone Pacific/Honolulu -10:31:26 - LMT 1896 Jan 13 12:00 #Schmitt&Cox + -10:30 - HST 1933 Apr 30 2:00 #Laws 1933 + -10:30 1:00 HDT 1933 May 21 12:00 #Laws 1933+12 + -10:30 - HST 1942 Feb 09 2:00 #Schmitt&Cox+2 + -10:30 1:00 HDT 1945 Sep 30 2:00 #Schmitt&Fox+2 + -10:30 US H%sT 1947 Jun 8 2:00 #Schmitt&Fox+2 -10:00 - HST # Now we turn to US areas that have diverged from the consensus since 1970. diff --git a/make/sun/javazic/tzdata/zone.tab b/make/sun/javazic/tzdata/zone.tab index c1b3e0707eb62fdb1df064852f859586fa754160..cddfedae509a15e0f650c03e29f68168a2924873 100644 --- a/make/sun/javazic/tzdata/zone.tab +++ b/make/sun/javazic/tzdata/zone.tab @@ -233,8 +233,8 @@ HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest ID -0610+10648 Asia/Jakarta Java & Sumatra ID -0002+10920 Asia/Pontianak west & central Borneo -ID -0507+11924 Asia/Makassar east & south Borneo, Celebes, Bali, Nusa Tengarra, west Timor -ID -0232+14042 Asia/Jayapura Irian Jaya & the Moluccas +ID -0507+11924 Asia/Makassar east & south Borneo, Sulawesi (Celebes), Bali, Nusa Tengarra, west Timor +ID -0232+14042 Asia/Jayapura west New Guinea (Irian Jaya) & Malukus (Moluccas) IE +5320-00615 Europe/Dublin IL +3146+03514 Asia/Jerusalem IM +5409-00428 Europe/Isle_of_Man @@ -426,6 +426,7 @@ US +411745-0863730 America/Indiana/Knox Central Time - Indiana - Starke County US +450628-0873651 America/Menominee Central Time - Michigan - Dickinson, Gogebic, Iron & Menominee Counties US +470659-1011757 America/North_Dakota/Center Central Time - North Dakota - Oliver County US +465042-1012439 America/North_Dakota/New_Salem Central Time - North Dakota - Morton County (except Mandan area) +US +471551-1014640 America/North_Dakota/Beulah Central Time - North Dakota - Mercer County US +394421-1045903 America/Denver Mountain Time US +433649-1161209 America/Boise Mountain Time - south Idaho & east Oregon US +364708-1084111 America/Shiprock Mountain Time - Navajo diff --git a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider index 2ea4f8b80407786c26f161f02507c05a6d4c9f3c..a92a6020d50bf21c1d5731b4dbccea03182c41bd 100644 --- a/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider +++ b/src/share/classes/com/sun/media/sound/services/javax.sound.sampled.spi.FormatConversionProvider @@ -1,5 +1,5 @@ # Providers for FormatConversion +com.sun.media.sound.AudioFloatFormatConverter com.sun.media.sound.UlawCodec com.sun.media.sound.AlawCodec com.sun.media.sound.PCMtoPCMCodec -com.sun.media.sound.AudioFloatFormatConverter diff --git a/src/share/classes/java/dyn/CallSite.java b/src/share/classes/java/dyn/CallSite.java index 2290aa84e2b4cf23f94067d97ea570f4684d4511..42af08a729b02c56181154c2a1968146aae48733 100644 --- a/src/share/classes/java/dyn/CallSite.java +++ b/src/share/classes/java/dyn/CallSite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -78,7 +78,7 @@ static { } private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) { // ignore caller and name, but match the type: - return new ConstantCallSite(MethodHandles.collectArguments(printArgs, type)); + return new ConstantCallSite(printArgs.asType(type)); } * @author John Rose, JSR 292 EG @@ -86,6 +86,7 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam abstract public class CallSite { private static final Access IMPL_TOKEN = Access.getToken(); + static { MethodHandleImpl.initStatics(); } // Fields used only by the JVM. Do not use or change. private MemberName vmmethod; // supplied by the JVM (ref. to calling method) @@ -125,8 +126,8 @@ public class CallSite { } /** - * Report the type of this call site's target. - * Although targets may change, the call site's type can never change. + * Returns the type of this call site's target. + * Although targets may change, any call site's type is permanent, and can never change to an unequal type. * The {@code setTarget} method enforces this invariant by refusing any new target that does * not have the previous target's type. * @return the type of the current target, which is also the type of any future target @@ -154,73 +155,40 @@ public class CallSite { } /** - * Report the current linkage state of the call site, a value which may change over time. - *

- * If a {@code CallSite} object is returned - * from the bootstrap method of the {@code invokedynamic} instruction, - * the {@code CallSite} is permanently bound to that instruction. - * When the {@code invokedynamic} instruction is executed, the target method - * of its associated call site object is invoked directly. - * It is as if the instruction calls {@code getTarget} and then - * calls {@link MethodHandle#invokeExact invokeExact} on the result. - *

- * Unless specified differently by a subclass, - * the interactions of {@code getTarget} with memory are the same - * as of a read from an ordinary variable, such as an array element or a - * non-volatile, non-final field. - *

- * In particular, the current thread may choose to reuse the result - * of a previous read of the target from memory, and may fail to see - * a recent update to the target by another thread. - *

- * In a {@linkplain ConstantCallSite constant call site}, the {@code getTarget} method behaves - * like a read from a {@code final} field of the {@code CallSite}. - *

- * In a {@linkplain VolatileCallSite volatile call site}, the {@code getTarget} method behaves - * like a read from a {@code volatile} field of the {@code CallSite}. - *

- * This method may not be overridden by application code. + * Returns the target method of the call site, according to the + * behavior defined by this call site's specific class. + * The immediate subclasses of {@code CallSite} document the + * class-specific behaviors of this method. + * * @return the current linkage state of the call site, its target method handle * @see ConstantCallSite * @see VolatileCallSite * @see #setTarget + * @see ConstantCallSite#getTarget + * @see MutableCallSite#getTarget + * @see VolatileCallSite#getTarget */ - public final MethodHandle getTarget() { - return getTarget0(); - } - - /** - * Privileged implementations can override this to force final or volatile semantics on getTarget. - */ - /*package-private*/ - MethodHandle getTarget0() { - return target; - } + public abstract MethodHandle getTarget(); /** - * Set the target method of this call site. + * Updates the target method of this call site, according to the + * behavior defined by this call site's specific class. + * The immediate subclasses of {@code CallSite} document the + * class-specific behaviors of this method. *

- * Unless a subclass of CallSite documents otherwise, - * the interactions of {@code setTarget} with memory are the same - * as of a write to an ordinary variable, such as an array element or a - * non-volatile, non-final field. - *

- * In particular, unrelated threads may fail to see the updated target - * until they perform a read from memory. - * Stronger guarantees can be created by putting appropriate operations - * into the bootstrap method and/or the target methods used - * at any given call site. + * The type of the new target must be {@linkplain MethodType#equals equal to} + * the type of the old target. + * * @param newTarget the new target * @throws NullPointerException if the proposed new target is null * @throws WrongMethodTypeException if the proposed new target * has a method type that differs from the previous target - * @throws UnsupportedOperationException if the call site is - * in fact a {@link ConstantCallSite} + * @see CallSite#getTarget + * @see ConstantCallSite#setTarget + * @see MutableCallSite#setTarget + * @see VolatileCallSite#setTarget */ - public void setTarget(MethodHandle newTarget) { - checkTargetChange(this.target, newTarget); - setTargetNormal(newTarget); - } + public abstract void setTarget(MethodHandle newTarget); void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) { MethodType oldType = oldTarget.type(); @@ -236,31 +204,31 @@ public class CallSite { /** * Produce a method handle equivalent to an invokedynamic instruction * which has been linked to this call site. - *

If this call site is a {@linkplain ConstantCallSite constant call site}, - * this method simply returns the call site's target, since that will never change. - *

Otherwise, this method is equivalent to the following code: - *

+     * 

+ * This method is equivalent to the following code: + *

      * MethodHandle getTarget, invoker, result;
-     * getTarget = MethodHandles.lookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
+     * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
      * invoker = MethodHandles.exactInvoker(this.type());
      * result = MethodHandles.foldArguments(invoker, getTarget)
      * 
+ * * @return a method handle which always invokes this call site's current target */ - public final MethodHandle dynamicInvoker() { - if (this instanceof ConstantCallSite) { - return getTarget0(); // will not change dynamically - } + public abstract MethodHandle dynamicInvoker(); + + /*non-public*/ MethodHandle makeDynamicInvoker() { MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this); MethodHandle invoker = MethodHandles.exactInvoker(this.type()); return MethodHandles.foldArguments(invoker, getTarget); } + private static final MethodHandle GET_TARGET; static { try { GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP. findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); - } catch (NoAccessException ignore) { + } catch (ReflectiveOperationException ignore) { throw new InternalError(); } } diff --git a/src/share/classes/java/dyn/ClassValue.java b/src/share/classes/java/dyn/ClassValue.java index 7b00a727d07091a45de63bbf434d8eaff32b5b32..597dd951eb691471f770b2c6ea30b2cbfc4925f0 100644 --- a/src/share/classes/java/dyn/ClassValue.java +++ b/src/share/classes/java/dyn/ClassValue.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 @@ -31,10 +31,14 @@ import java.util.concurrent.atomic.AtomicReference; import java.lang.reflect.UndeclaredThrowableException; /** - * Lazily associate a computed value with (potentially) every class. + * Lazily associate a computed value with (potentially) every type. + * For example, if a dynamic language needs to construct a message dispatch + * table for each class encountered at a message send call site, + * it can use a {@code ClassValue} to cache information needed to + * perform the message send quickly, for each class encountered. * @author John Rose, JSR 292 EG */ -public class ClassValue { +public abstract class ClassValue { /** * Compute the given class's derived value for this {@code ClassValue}. *

@@ -45,61 +49,41 @@ public class ClassValue { * but it may be invoked again if there has been a call to * {@link #remove remove}. *

- * If there is no override from a subclass, this method returns - * the result of applying the {@code ClassValue}'s {@code computeValue} - * method handle, which was supplied at construction time. + * If this method throws an exception, the corresponding call to {@code get} + * will terminate abnormally with that exception, and no class value will be recorded. * + * @param type the type whose class value must be computed * @return the newly computed value associated with this {@code ClassValue}, for the given class or interface - * @throws UndeclaredThrowableException if the {@code computeValue} method handle invocation throws something other than a {@code RuntimeException} or {@code Error} - * @throws UnsupportedOperationException if the {@code computeValue} method handle is null (subclasses must override) + * @see #get + * @see #remove */ - protected T computeValue(Class type) { - if (computeValue == null) - return null; - try { - return (T) (Object) computeValue.invokeGeneric(type); - } catch (Throwable ex) { - if (ex instanceof Error) throw (Error) ex; - if (ex instanceof RuntimeException) throw (RuntimeException) ex; - throw new UndeclaredThrowableException(ex); - } - } - - private final MethodHandle computeValue; - - /** - * Creates a new class value. - * Subclasses which use this constructor must override - * the {@link #computeValue computeValue} method, - * since the default {@code computeValue} method requires a method handle, - * which this constructor does not provide. - */ - protected ClassValue() { - this.computeValue = null; - } - - /** - * Creates a new class value, whose {@link #computeValue computeValue} method - * will return the result of {@code computeValue.invokeGeneric(type)}. - * @throws NullPointerException if the method handle parameter is null - */ - public ClassValue(MethodHandle computeValue) { - computeValue.getClass(); // trigger NPE if null - this.computeValue = computeValue; - } + protected abstract T computeValue(Class type); /** * Returns the value for the given class. * If no value has yet been computed, it is obtained by - * by an invocation of the {@link #computeValue computeValue} method. + * an invocation of the {@link #computeValue computeValue} method. *

* The actual installation of the value on the class * is performed atomically. - * At that point, if racing threads have + * At that point, if several racing threads have * computed values, one is chosen, and returned to * all the racing threads. + *

+ * The {@code type} parameter is typically a class, but it may be any type, + * such as an interface, a primitive type (like {@code int.class}), or {@code void.class}. + *

+ * In the absence of {@code remove} calls, a class value has a simple + * state diagram: uninitialized and initialized. + * When {@code remove} calls are made, + * the rules for value observation are more complex. + * See the documentation for {@link #remove remove} for more information. * + * @param type the type whose class value must be computed or retrieved * @return the current value associated with this {@code ClassValue}, for the given class or interface + * @throws NullPointerException if the argument is null + * @see #remove + * @see #computeValue */ public T get(Class type) { ClassValueMap map = getMap(type); @@ -119,12 +103,51 @@ public class ClassValue { * This may result in an additional invocation of the * {@code computeValue computeValue} method for the given class. *

- * If racing threads perform a combination of {@code get} and {@code remove} calls, - * the calls are serialized. - * A value produced by a call to {@code computeValue} will be discarded, if - * the corresponding {@code get} call was followed by a {@code remove} call - * before the {@code computeValue} could complete. - * In such a case, the {@code get} call will re-invoke {@code computeValue}. + * In order to explain the interaction between {@code get} and {@code remove} calls, + * we must model the state transitions of a class value to take into account + * the alternation between uninitialized and initialized states. + * To do this, number these states sequentially from zero, and note that + * uninitialized (or removed) states are numbered with even numbers, + * while initialized (or re-initialized) states have odd numbers. + *

+ * When a thread {@code T} removes a class value in state {@code 2N}, + * nothing happens, since the class value is already uninitialized. + * Otherwise, the state is advanced atomically to {@code 2N+1}. + *

+ * When a thread {@code T} queries a class value in state {@code 2N}, + * the thread first attempts to initialize the class value to state {@code 2N+1} + * by invoking {@code computeValue} and installing the resulting value. + *

+ * When {@code T} attempts to install the newly computed value, + * if the state is still at {@code 2N}, the class value will be initialized + * with the computed value, advancing it to state {@code 2N+1}. + *

+ * Otherwise, whether the new state is even or odd, + * {@code T} will discard the newly computed value + * and retry the {@code get} operation. + *

+ * Discarding and retrying is an important proviso, + * since otherwise {@code T} could potentially install + * a disastrously stale value. For example: + *

    + *
  • {@code T} calls {@code CV.get(C)} and sees state {@code 2N} + *
  • {@code T} quickly computes a time-dependent value {@code V0} and gets ready to install it + *
  • {@code T} is hit by an unlucky paging or scheduling event, and goes to sleep for a long time + *
  • ...meanwhile, {@code T2} also calls {@code CV.get(C)} and sees state {@code 2N} + *
  • {@code T2} quickly computes a similar time-dependent value {@code V1} and installs it on {@code CV.get(C)} + *
  • {@code T2} (or a third thread) then calls {@code CV.remove(C)}, undoing {@code T2}'s work + *
  • the previous actions of {@code T2} are repeated several times + *
  • also, the relevant computed values change over time: {@code V1}, {@code V2}, ... + *
  • ...meanwhile, {@code T} wakes up and attempts to install {@code V0}; this must fail + *
+ * We can assume in the above scenario that {@code CV.computeValue} uses locks to properly + * observe the time-dependent states as it computes {@code V1}, etc. + * This does not remove the threat of a stale value, since there is a window of time + * between the return of {@code computeValue} in {@code T} and the installation + * of the the new value. No user synchronization is possible during this time. + * + * @param type the type whose class value must be removed + * @throws NullPointerException if the argument is null */ public void remove(Class type) { ClassValueMap map = getMap(type); @@ -137,9 +160,9 @@ public class ClassValue { /// Implementation... - /** The hash code for this type is based on the identity of the object, - * and is well-dispersed for power-of-two tables. - */ + // The hash code for this type is based on the identity of the object, + // and is well-dispersed for power-of-two tables. + /** @deprecated This override, which is implementation-specific, will be removed for PFD. */ public final int hashCode() { return hashCode; } private final int hashCode = HASH_CODES.getAndAdd(0x61c88647); private static final AtomicInteger HASH_CODES = new AtomicInteger(); diff --git a/src/share/classes/java/dyn/ConstantCallSite.java b/src/share/classes/java/dyn/ConstantCallSite.java index 585fdc712cef9352ecacff7657f9d89cc68a0d8c..50240a0f50f45e1459fa05e633e83f65560b3375 100644 --- a/src/share/classes/java/dyn/ConstantCallSite.java +++ b/src/share/classes/java/dyn/ConstantCallSite.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 @@ -32,16 +32,46 @@ package java.dyn; * @author John Rose, JSR 292 EG */ public class ConstantCallSite extends CallSite { - /** Create a call site with a permanent target. + /** + * Creates a call site with a permanent target. + * @param target the target to be permanently associated with this call site * @throws NullPointerException if the proposed target is null */ public ConstantCallSite(MethodHandle target) { super(target); } + + /** + * Returns the target method of the call site, which behaves + * like a {@code final} field of the {@code ConstantCallSite}. + * That is, the the target is always the original value passed + * to the constructor call which created this instance. + * + * @return the immutable linkage state of this call site, a constant method handle + * @throws UnsupportedOperationException because this kind of call site cannot change its target + */ + @Override public final MethodHandle getTarget() { + return target; + } + /** - * Throw an {@link UnsupportedOperationException}, because this kind of call site cannot change its target. + * Always throws an {@link UnsupportedOperationException}. + * This kind of call site cannot change its target. + * @param ignore a new target proposed for the call site, which is ignored + * @throws UnsupportedOperationException because this kind of call site cannot change its target */ @Override public final void setTarget(MethodHandle ignore) { throw new UnsupportedOperationException("ConstantCallSite"); } + + /** + * Returns this call site's permanent target. + * Since that target will never change, this is a correct implementation + * of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}. + * @return the immutable linkage state of this call site, a constant method handle + */ + @Override + public final MethodHandle dynamicInvoker() { + return getTarget(); + } } diff --git a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java index 1fe1af02b45901516845354e05dc9811174f12be..76e795e2f0e8ebaf160f0e6d46b309c4764b76ba 100644 --- a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java +++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java @@ -31,8 +31,8 @@ package java.dyn; * {@linkplain BootstrapMethod bootstrap method}, * or the bootstrap method has * failed to provide a - * {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target} - * of the correct {@linkplain MethodType method type}. + * {@linkplain CallSite call site} with a {@linkplain CallSite#getTarget target} + * of the correct {@linkplain MethodHandle#type method type}. * * @author John Rose, JSR 292 EG * @since 1.7 diff --git a/src/share/classes/java/dyn/Linkage.java b/src/share/classes/java/dyn/Linkage.java index aad9409abdf1b5b29d915ce770641ac210242dbb..4ddda0a19902740d362d1a44be290700dfa703d1 100644 --- a/src/share/classes/java/dyn/Linkage.java +++ b/src/share/classes/java/dyn/Linkage.java @@ -88,7 +88,7 @@ public class Linkage { MethodHandle bootstrapMethod; try { bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex); } MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod); @@ -101,8 +101,9 @@ public class Linkage { /** * METHOD WILL BE REMOVED FOR PFD: * Invalidate all invokedynamic call sites everywhere. - * @deprecated Use {@linkplain CallSite#setTarget call site target setting} - * and {@link VolatileCallSite#invalidateAll call site invalidation} instead. + * @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting}, + * {@link MutableCallSite#syncAll call site update pushing}, + * and {@link SwitchPoint#guardWithTest target switching} instead. */ public static Object invalidateAll() { @@ -113,8 +114,9 @@ public class Linkage { * METHOD WILL BE REMOVED FOR PFD: * Invalidate all {@code invokedynamic} call sites in the bytecodes * of any methods of the given class. - * @deprecated Use {@linkplain CallSite#setTarget call site target setting} - * and {@link VolatileCallSite#invalidateAll call site invalidation} instead. + * @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting}, + * {@link MutableCallSite#syncAll call site update pushing}, + * and {@link SwitchPoint#guardWithTest target switching} instead. */ public static Object invalidateCallerClass(Class callerClass) { diff --git a/src/share/classes/java/dyn/MethodHandle.java b/src/share/classes/java/dyn/MethodHandle.java index 25d0f807488c5688c3c7cd46c0748fdc3e075d02..b78b40248053f521214048a6b52230b42698a454 100644 --- a/src/share/classes/java/dyn/MethodHandle.java +++ b/src/share/classes/java/dyn/MethodHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -34,7 +34,7 @@ import static java.dyn.MethodHandles.invokers; // package-private API import static sun.dyn.MemberName.newIllegalArgumentException; // utility /** - * A method handle is a typed, directly executable reference to a method, + * A method handle is a typed, directly executable reference to an underlying method, * constructor, field, or similar low-level operation, with optional * transformations of arguments or return values. * These transformations are quite general, and include such patterns as @@ -48,99 +48,182 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility * will be removed before the Proposed Final Draft. * Also, the final version will not include any public or * protected constructors. - *

- * Method handles are strongly typed according to signature. - * They are not distinguished by method name or enclosing class. - * A method handle must be invoked under a signature which matches - * the method handle's own {@linkplain MethodType method type}. + * + *

Method handle contents

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

* Every method handle reports its type via the {@link #type type} accessor. - * The structure of this type is a series of classes, one of which is + * This type descriptor is a {@link java.dyn.MethodType MethodType} object, + * whose structure is a series of classes, one of which is * the return type of the method (or {@code void.class} if none). *

- * Every method handle appears as an object containing a method named - * {@link #invokeExact invokeExact}, whose signature exactly matches - * the method handle's type. - * A Java method call expression, which compiles to an - * {@code invokevirtual} instruction, - * can invoke this method from Java source code. - *

- * Every call to a method handle specifies an intended method type, - * which must exactly match the type of the method handle. - * (The type is specified in the {@code invokevirtual} instruction, - * via a {@code CONSTANT_NameAndType} constant pool entry.) - * The call looks within the receiver object for a method - * named {@code invokeExact} of the intended method type. - * The call fails with a {@link WrongMethodTypeException} - * if the method does not exist, even if there is an {@code invokeExact} - * method of a closely similar signature. - * As with other kinds - * of methods in the JVM, signature matching during method linkage - * is exact, and does not allow for language-level implicit conversions - * such as {@code String} to {@code Object} or {@code short} to {@code int}. - *

- * Each individual method handle also contains a method named - * {@link #invokeGeneric invokeGeneric}, whose type is the same - * as {@code invokeExact}, and is therefore also reported by - * the {@link #type type} accessor. + * A method handle's type controls the types of invocations it accepts, + * 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}. + * 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. + *

+ * Method handles are immutable and have no visible state. + * Of course, they can be bound to underlying methods or data which exhibit state. + * With respect to the Java Memory Model, any method handle will behave + * as if all of its (internal) fields are final variables. This means that any method + * handle made visible to the application will always be fully formed. + * This is true even if the method handle is published through a shared + * variable in a data race. + *

+ * Method handles cannot be subclassed by the user. + * Implementations may (or may not) create internal subclasses of {@code MethodHandle} + * which may be visible via the {@link java.lang.Object#getClass Object.getClass} + * operation. The programmer should not draw conclusions about a method handle + * from its specific class, as the method handle class hierarchy (if any) + * 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} + * 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. + * Formally this is accomplished by giving the invoker methods + * {@code Object} return types and variable-arity {@code Object} arguments, + * but they have an additional quality called signature polymorphism + * which connects this freedom of invocation directly to the JVM execution stack. + *

+ * As is usual with virtual methods, source-level calls to {@code invokeExact} + * and {@code invokeGeneric} 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. + * The method handle object itself is pushed on the stack before the arguments. + * The compiler then calls the method handle with a type descriptor which + * describes the argument and return types. + *

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

+ * As a corner case, an uncasted {@code null} argument is given + * a type descriptor of {@code java.lang.Void}. + * The ambiguity with the type {@code Void} is harmless, since there are no references of type + * {@code Void} except the null reference. + * + *

Method handle invocation

+ * 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}. + * 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 + * a method handle will always link, as long + * as the type descriptor is syntactically well-formed + * and the types exist. + *

+ * When the {@code invokevirtual} is executed after linking, + * the receiving method handle's type is first checked by the JVM + * to ensure that it matches the descriptor. + * If the type match fails, it means that the method which the + * caller is invoking is not present on the individual + * method handle being invoked. + *

+ * 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 + * must be a valid argument to the receiver's {@link #asType asType} method. + * Thus, {@code invokeGeneric} 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 - * {@code invokeExact}, if the signature specified by the caller + * {@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 - * to adjust the type of the target method handle - * (as if by a call to {@link #asType asType}) - * to obtain an exactly invokable target. + * 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}. * This allows a more powerful negotiation of method type * between caller and callee. *

- * A method handle is an unrestricted capability to call a method. - * A method handle can be formed on a non-public method by a class - * that has access to that method; the resulting handle can be used - * in any place by any caller who receives a reference to it. Thus, access - * checking is performed when the method handle is created, not - * (as in reflection) every time it is called. Handles to non-public - * methods, or in non-public classes, should generally be kept secret. - * They should not be passed to untrusted code unless their use from - * the untrusted code would be harmless. + * (Note: The adjusted method handle {@code M2} is not directly observable, + * and implementations are therefore not required to materialize it.) + * + *

Invocation checking

+ * 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}). *

- * Bytecode in the JVM can directly call a method handle's - * {@code invokeExact} method from an {@code invokevirtual} instruction. - * The receiver class type must be {@code MethodHandle} and the method name - * must be {@code invokeExact}. The signature of the invocation - * (after resolving symbolic type names) must exactly match the method type - * of the target method. - * Similarly, bytecode can directly call a method handle's {@code invokeGeneric} - * method. The signature of the invocation (after resolving symbolic type names) - * must either exactly match the method type or be a valid argument to - * the target's {@link #asType asType} method. - *

- * Every {@code invokeExact} and {@code invokeGeneric} method always - * throws {@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 - * and unchecked exceptions (other than by their class, of course), - * there is no particular effect on bytecode shape from ascribing - * checked exceptions to method handle invocations. But in Java source - * code, methods which perform method handle calls must either explicitly - * throw {@code java.lang.Throwable Throwable}, or else must catch all - * throwables locally, rethrowing only those which are legal in the context, - * and wrapping ones which are illegal. + * Thus, a method type mismatch which might show up as a linkage error + * in a statically typed program can show up as + * a dynamic {@code WrongMethodTypeException} + * in a program which uses method handles. *

- * Bytecode in the JVM can directly obtain a method handle - * for any accessible method from a {@code ldc} instruction - * which refers to a {@code CONSTANT_MethodHandle} constant pool entry. - * (Each such entry refers directly to a {@code CONSTANT_Methodref}, - * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref} - * constant pool entry. - * For more details, see the package summary.) + * Because method types contain "live" {@code Class} objects, + * method type matching takes into account both types names and class loaders. + * Thus, even if a method handle {@code M} is created in one + * class loader {@code L1} and used in another {@code L2}, + * method handle calls are type-safe, because the caller's type + * descriptor, as resolved in {@code L2}, + * is matched against the original callee method's type descriptor, + * as resolved in {@code L1}. + * The resolution in {@code L1} happens when {@code M} is created + * and its type is assigned, while the resolution in {@code L2} happens + * when the {@code invokevirtual} instruction is linked. *

- * Java code can also use a reflective API called + * Apart from the checking of type descriptors, + * a method handle's capability to call its underlying method is unrestricted. + * If a method handle is formed on a non-public method by a class + * that has access to that method, the resulting handle can be used + * in any place by any caller who receives a reference to it. + *

+ * Unlike with the Core Reflection API, where access is checked every time + * a reflective method is invoked, + * method handle access checking is performed + * when the method handle is created. + * In the case of {@code ldc} (see below), access checking is performed as part of linking + * the constant pool entry underlying the constant method handle. + *

+ * Thus, handles to non-public methods, or to methods in non-public classes, + * should generally be kept secret. + * They should not be passed to untrusted code unless their use from + * the untrusted code would be harmless. + * + *

Method handle creation

+ * Java code can create a method handle that directly accesses + * any method, constructor, or field that is accessible to that code. + * This is done via a reflective, capability-based API called * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup} - * for creating and calling method handles. * For example, a static method handle can be obtained * from {@link java.dyn.MethodHandles.Lookup#findStatic Lookup.findStatic}. - * There are also bridge methods from Core Reflection API objects, - * such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.ureflect}. + * There are also conversion methods from Core Reflection API objects, + * such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}. + *

+ * Like classes and strings, method handles that correspond to accessible + * fields, methods, and constructors can also be represented directly + * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. + * A new type of constant pool entry, {@code CONSTANT_MethodHandle}, + * refers directly to an associated {@code CONSTANT_Methodref}, + * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref} + * constant pool entry. + * (For more details on method handle constants, + * see the package summary.) + *

+ * Method handles produced by lookups or constant loads from methods or + * constructors with the variable arity modifier bit ({@code 0x0080}) + * have a corresponding variable arity, as if they were defined with + * the help of {@link #asVarargsCollector asVarargsCollector}. *

* A method reference may refer either to a static or non-static method. * In the non-static case, the method handle type includes an explicit @@ -153,61 +236,191 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility * When a method handle to a virtual method is invoked, the method is * always looked up in the receiver (that is, the first argument). *

- * A non-virtual method handles to a specific virtual method implementation + * A non-virtual method handle to a specific virtual method implementation * can also be created. These do not perform virtual lookup based on * receiver type. Such a method handle simulates the effect of * an {@code invokespecial} instruction to the same method. - *

+ * + *

Usage examples

* Here are some examples of usage: *

 Object x, y; String s; int i;
 MethodType mt; MethodHandle mh;
 MethodHandles.Lookup lookup = MethodHandles.lookup();
-// mt is {(char,char) => String}
+// mt is (char,char)String
 mt = MethodType.methodType(String.class, char.class, char.class);
 mh = lookup.findVirtual(String.class, "replace", mt);
-// (Ljava/lang/String;CC)Ljava/lang/String;
 s = (String) mh.invokeExact("daddy",'d','n');
+// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
 assert(s.equals("nanny"));
 // weakly typed invocation (using MHs.invoke)
 s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
 assert(s.equals("savvy"));
-// mt is {Object[] => List}
+// mt is (Object[])List
 mt = MethodType.methodType(java.util.List.class, Object[].class);
 mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
-// mt is {(Object,Object,Object) => Object}
+assert(mh.isVarargsCollector());
+x = mh.invokeGeneric("one", "two");
+// invokeGeneric(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);
-mh = MethodHandles.collectArguments(mh, mt);
-// mt is {(Object,Object,Object) => Object}
-// (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+mh = mh.asType(mt);
 x = mh.invokeExact((Object)1, (Object)2, (Object)3);
+// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
 assert(x.equals(java.util.Arrays.asList(1,2,3)));
 // mt is { => int}
 mt = MethodType.methodType(int.class);
 mh = lookup.findVirtual(java.util.List.class, "size", mt);
-// (Ljava/util/List;)I
 i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
+// invokeExact(Ljava/util/List;)I
 assert(i == 3);
 mt = MethodType.methodType(void.class, String.class);
 mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
 mh.invokeExact(System.out, "Hello, world.");
-// (Ljava/io/PrintStream;Ljava/lang/String;)V
+// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
  * 
- * Each of the above calls generates a single invokevirtual instruction - * with the name {@code invoke} and the type descriptors indicated in the comments. - * The argument types are taken directly from the actual arguments, - * while the return type is taken from the cast immediately applied to the call. - * This cast may be to a primitive. - * If it is missing, the type defaults to {@code Object} if the call - * occurs in a context which uses the return value. - * If the call occurs as a statement, a cast is impossible, - * and there is no return type; the call is {@code void}. - *

- * A note on generic typing: Method handles do not represent - * their function types in terms of Java parameterized (generic) types, - * because there are three mismatches between function types and parameterized + * Each of the above calls to {@code invokeExact} or {@code invokeGeneric} + * generates a single invokevirtual instruction with + * the type descriptor indicated in the following comment. + * + *

Exceptions

+ * The methods {@code invokeExact} and {@code invokeGeneric} 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 + * and unchecked exceptions (other than by their class, of course), + * there is no particular effect on bytecode shape from ascribing + * checked exceptions to method handle invocations. But in Java source + * code, methods which perform method handle calls must either explicitly + * throw {@code java.lang.Throwable Throwable}, or else must catch all + * throwables locally, rethrowing only those which are legal in the context, + * and wrapping ones which are illegal. + * + *

Signature polymorphism

+ * The unusual compilation and linkage behavior of + * {@code invokeExact} and {@code invokeGeneric} + * 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. + * In order to make this work, both the Java compiler and the JVM must + * give special treatment to signature polymorphic methods. + *

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

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

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

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

Formal rules for processing signature polymorphic methods

+ *

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

    + *
  • {@link java.dyn.MethodHandle#invokeExact MethodHandle.invokeExact} + *
  • {@link java.dyn.MethodHandle#invokeGeneric MethodHandle.invokeGeneric} + *
+ *

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

    + *
  • It must be native. + *
  • It must take a single varargs parameter of the form {@code Object...}. + *
  • It must produce a return value of type {@code Object}. + *
  • It must be contained within the {@code java.dyn} package. + *
+ * Because of these requirements, a signature polymorphic method is able to accept + * any number and type of actual arguments, and can, with a cast, produce a value of any type. + * However, the JVM will treat these declaration features as a documentation convention, + * rather than a description of the actual structure of the methods as executed. + *

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

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

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

    + *
  • If the method invocation expression is an expression statement, the method is {@code void}. + *
  • Otherwise, if the method invocation expression is the immediate operand of a cast, + * the return type is the erasure of the cast type. + *
  • Otherwise, the return type is the method's nominal return type, {@code Object}. + *
+ * (Programmers are encouraged to use explicit casts unless it is clear that a signature polymorphic + * call will be used as a plain {@code Object} expression.) + *

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

Interoperation between method handles and the Core Reflection API

+ * Using factory methods in the {@link java.dyn.MethodHandles.Lookup Lookup} API, + * any class member represented by a Core Reflection API object + * can be converted to a behaviorally equivalent method handle. + * For example, a reflective {@link java.lang.reflect.Method Method} can + * be converted to a method handle using + * {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}. + * The resulting method handles generally provide more direct and efficient + * access to the underlying class members. + *

+ * 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. + * 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}. + *

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

Interoperation between method handles and Java generics

+ * A method handle can be obtained on a method, constructor, or field + * which is declared with Java generic types. + * As with the Core Reflection API, the type of the method handle + * will constructed from the erasure of the source-level type. + * When a method handle is invoked, the types of its arguments + * or the return value cast type may be generic types or type instances. + * If this occurs, the compiler will replace those + * types by their erasures when when it constructs the type descriptor + * for the {@code invokevirtual} instruction. + *

+ * Method handles do not represent + * their function-like types in terms of Java parameterized (generic) types, + * because there are three mismatches between function-like types and parameterized * Java types. - *

    + *
      *
    • Method types range over all possible arities, * from no arguments to up to 255 of arguments (a limit imposed by the JVM). * Generics are not variadic, and so cannot represent this.
    • @@ -217,29 +430,7 @@ mh.invokeExact(System.out, "Hello, world."); * often generic across a wide range of function types, including * those of multiple arities. It is impossible to represent such * genericity with a Java type parameter. - *
- * Signature polymorphic methods in this class appear to be documented - * as having type parameters for return types and a parameter, but that is - * merely a documentation convention. These type parameters do - * not play a role in type-checking method handle invocations. - *

- * Like classes and strings, method handles that correspond to accessible - * fields, methods, and constructors can be represented directly - * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. - * Loading such a constant causes the component classes of its type to be loaded as necessary. - *

- * Method handles cannot be subclassed by the user. - * Implementations may (or may not) create internal subclasses of {@code MethodHandle} - * which may be visible via the {@code java.lang.Object#getClass Object.getClass} - * operation. The programmer should not draw conclusions about a method handle - * from its specific class, as the method handle class hierarchy (if any) - * may change from time to time or across implementations from different vendors. - *

- * With respect to the Java Memory Model, any method handle will behave - * as if all of its fields are final variables. This means that any method - * handle made visible to the application will always be fully formed. - * This is true even if the method handle is published through a shared - * variables in a data race. + * * * @see MethodType * @see MethodHandles @@ -251,15 +442,16 @@ public abstract class MethodHandle extends MethodHandleImpl { private static Access IMPL_TOKEN = Access.getToken(); + static { MethodHandleImpl.initStatics(); } // interface MethodHandle // { MethodType type(); public R invokeExact(A...) throws X; } /** * Internal marker interface which distinguishes (to the Java compiler) - * those methods which are signature polymorphic. + * those methods which are signature polymorphic. */ - @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.TYPE}) + @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @interface PolymorphicSignature { } @@ -270,7 +462,7 @@ public abstract class MethodHandle * Every invocation of this method handle via {@code invokeExact} must exactly match this type. * @return the method handle type */ - public final MethodType type() { + public MethodType type() { return type; } @@ -307,40 +499,27 @@ public abstract class MethodHandle } /** - * Returns a string representation of the method handle, - * starting with the string {@code "MethodHandle"} and - * ending with the string representation of the method handle's type. - * In other words, this method returns a string equal to the value of: - *

-     * "MethodHandle" + type().toString()
-     * 
- *

- * Note: Future releases of this API may add further information - * to the string representation. - * Therefore, the present syntax should not be parsed by applications. - * - * @return a string representation of the method handle - */ - @Override - public String toString() { - return MethodHandleImpl.getNameString(IMPL_TOKEN, this); - } - - /** - * Invoke the method handle, allowing any caller signature, but requiring an exact signature match. - * The signature at the call site of {@code invokeExact} must + * Invoke the method handle, allowing any caller type descriptor, but requiring an exact type match. + * The type descriptor at the call site of {@code invokeExact} must * exactly match this method handle's {@link #type type}. * No conversions are allowed on arguments or return values. - * @throws WrongMethodTypeException if the target's type is not identical with the caller's type signature + *

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

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

* Otherwise, the call proceeds as if this method handle were first @@ -353,14 +532,20 @@ public abstract class MethodHandle * adaptations directly on the caller's arguments, * and call the target method handle according to its own exact type. *

- * If the method handle is equipped with a - * {@linkplain #withTypeHandler type handler}, the handler must produce - * an entry point of the call site's exact type. - * Otherwise, the signature at the call site of {@code invokeGeneric} must - * be a valid argument to the standard {@code asType} method. + * The type descriptor at the call site of {@code invokeGeneric} 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. - * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type signature + * as the callee's type, + * if the callee is not a {@linkplain #asVarargsCollector variable arity collector}. + *

+ * When this method is observed via the Core Reflection API, + * it will appear as a single native method, taking an object array and returning an object. + * If this native method is invoked directly via + * {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI, + * or indirectly via {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}, + * it will throw an {@code UnsupportedOperationException}. + * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type descriptor + * @throws 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 invokeGeneric(Object... args) throws Throwable; @@ -400,16 +585,22 @@ public abstract class MethodHandle *

* This call is equivalent to the following code: *

-     * MethodHandle invoker = MethodHandles.varargsInvoker(this.type(), 0);
+     * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
      * Object result = invoker.invokeExact(this, arguments);
      * 
+ *

+ * Unlike the signature polymorphic methods {@code invokeExact} and {@code invokeGeneric}, + * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI. + * It can therefore be used as a bridge between native or reflective code and method handles. + * * @param arguments the arguments to pass to the target * @return the result returned by the target - * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments + * @throws ClassCastException if an argument cannot be converted by reference casting + * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments * @throws Throwable anything thrown by the target method invocation - * @see MethodHandles#varargsInvoker + * @see MethodHandles#spreadInvoker */ - public final Object invokeWithArguments(Object... arguments) throws Throwable { + public Object invokeWithArguments(Object... arguments) throws Throwable { int argc = arguments == null ? 0 : arguments.length; MethodType type = type(); if (type.parameterCount() != argc) { @@ -417,7 +608,7 @@ public abstract class MethodHandle return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments); } if (argc <= 10) { - MethodHandle invoker = MethodHandles.invokers(type).genericInvoker(); + MethodHandle invoker = invokers(type).genericInvoker(); switch (argc) { case 0: return invoker.invokeExact(this); case 1: return invoker.invokeExact(this, @@ -456,19 +647,11 @@ public abstract class MethodHandle } // more than ten arguments get boxed in a varargs list: - MethodHandle invoker = MethodHandles.invokers(type).varargsInvoker(0); + MethodHandle invoker = invokers(type).spreadInvoker(0); return invoker.invokeExact(this, arguments); } /** Equivalent to {@code invokeWithArguments(arguments.toArray())}. */ - public final Object invokeWithArguments(java.util.List arguments) throws Throwable { - return invokeWithArguments(arguments.toArray()); - } - @Deprecated - public final Object invokeVarargs(Object... arguments) throws Throwable { - return invokeWithArguments(arguments); - } - @Deprecated - public final Object invokeVarargs(java.util.List arguments) throws Throwable { + public Object invokeWithArguments(java.util.List arguments) throws Throwable { return invokeWithArguments(arguments.toArray()); } @@ -488,13 +671,7 @@ public abstract class MethodHandle * to match up the caller's and callee's types. *

* This method is equivalent to {@link MethodHandles#convertArguments convertArguments}, - * except for method handles produced by {@link #withTypeHandler withTypeHandler}, - * in which case the specified type handler is used for calls to {@code asType}. - *

- * Note that the default behavior of {@code asType} only performs - * pairwise argument conversion and return value conversion. - * Because of this, unless the method handle has a type handler, - * the original type and new type must have the same number of arguments. + * except for variable arity method handles produced by {@link #asVarargsCollector asVarargsCollector}. * * @param newType the expected type of the new method handle * @return a method handle which delegates to {@code this} after performing @@ -508,14 +685,16 @@ public abstract class MethodHandle } /** - * Produce a method handle which adapts, as its target, + * Make an adapter which accepts a trailing array argument + * and spreads its elements as positional arguments. + * The new method handle adapts, as its target, * the current method handle. The type of the adapter will be * the same as the type of the target, except that the final * {@code arrayLength} parameters of the target's type are replaced * by a single array parameter of type {@code arrayType}. *

* If the array element type differs from any of the corresponding - * argument types on original target, + * argument types on the original target, * the original target is adapted to take the array elements directly, * as if by a call to {@link #asType asType}. *

@@ -539,8 +718,9 @@ public abstract class MethodHandle * @throws IllegalArgumentException if target does not have at least * {@code arrayLength} parameter types * @throws WrongMethodTypeException if the implied {@code asType} call fails + * @see #asCollector */ - public final MethodHandle asSpreader(Class arrayType, int arrayLength) { + public MethodHandle asSpreader(Class arrayType, int arrayLength) { Class arrayElement = arrayType.getComponentType(); if (arrayElement == null) throw newIllegalArgumentException("not an array type"); MethodType oldType = type(); @@ -553,13 +733,15 @@ public abstract class MethodHandle } /** - * Produce a method handle which adapts, as its target, + * Make an adapter which accepts a given number of trailing + * positional arguments and collects them into an array argument. + * The new method handle adapts, as its target, * the current method handle. The type of the adapter will be * the same as the type of the target, except that a single trailing * parameter (usually of type {@code arrayType}) is replaced by * {@code arrayLength} parameters whose type is element type of {@code arrayType}. *

- * If the array type differs from the final argument type on original target, + * If the array type differs from the final argument type on the original target, * the original target is adapted to take the array type directly, * as if by a call to {@link #asType asType}. *

@@ -570,21 +752,31 @@ public abstract class MethodHandle * What the target eventually returns is returned unchanged by the adapter. *

* (The array may also be a shared constant when {@code arrayLength} is zero.) - * @param arrayType usually {@code Object[]}, the type of the array argument which will collect the arguments + *

+ * (Note: The {@code arrayType} is often identical to the last + * parameter type of the original target. + * It is an explicit argument for symmetry with {@code asSpreader}, and also + * to allow the target to use a simple {@code Object} as its last parameter type.) + *

+ * In order to create a collecting adapter which is not restricted to a particular + * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead. + * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments * @param arrayLength the number of arguments to collect into a new array argument * @return a new method handle which collects some trailing argument * into an array, before calling the original method handle * @throws IllegalArgumentException if {@code arrayType} is not an array type - or {@code arrayType} is not assignable to this method handle's trailing parameter type - * @throws IllegalArgumentException if {@code arrayLength} is not - * a legal array size + * or {@code arrayType} is not assignable to this method handle's trailing parameter type, + * or {@code arrayLength} is not a legal array size * @throws WrongMethodTypeException if the implied {@code asType} call fails + * @see #asSpreader + * @see #asVarargsCollector */ - public final MethodHandle asCollector(Class arrayType, int arrayLength) { + public MethodHandle asCollector(Class arrayType, int arrayLength) { Class arrayElement = arrayType.getComponentType(); if (arrayElement == null) throw newIllegalArgumentException("not an array type"); MethodType oldType = type(); int nargs = oldType.parameterCount(); + if (nargs == 0) throw newIllegalArgumentException("no trailing argument"); MethodType newType = oldType.dropParameterTypes(nargs-1, nargs); newType = newType.insertParameterTypes(nargs-1, java.util.Collections.>nCopies(arrayLength, arrayElement)); @@ -592,8 +784,185 @@ public abstract class MethodHandle } /** - * Produce a method handle which binds the given argument - * to the current method handle as target. + * Make a variable arity adapter which is able to accept + * any number of trailing positional arguments and collect them + * into an array argument. + *

+ * The type and behavior of the adapter will be the same as + * the type and behavior of the target, except that certain + * {@code invokeGeneric} and {@code asType} requests can lead to + * trailing positional arguments being collected into target's + * trailing parameter. + * Also, the last parameter type of the adapter will be + * {@code arrayType}, even if the target has a different + * last parameter type. + *

+ * When called with {@link #invokeExact invokeExact}, the adapter invokes + * the target with no argument changes. + * (Note: This behavior is different from a + * {@linkplain #asCollector fixed arity collector}, + * since it accepts a whole array of indeterminate length, + * rather than a fixed number of arguments.) + *

+ * When called with {@link #invokeGeneric invokeGeneric}, if the caller + * type is the same as the adapter, the adapter invokes the target as with + * {@code invokeExact}. + * (This is the normal behavior for {@code invokeGeneric} when types match.) + *

+ * Otherwise, if the caller and adapter arity are the same, and the + * trailing parameter type of the caller is a reference type identical to + * or assignable to the trailing parameter type of the adapter, + * the arguments and return values are converted pairwise, + * as if by {@link MethodHandles#convertArguments convertArguments}. + * (This is also normal behavior for {@code invokeGeneric} in such a case.) + *

+ * Otherwise, the arities differ, or the adapter's trailing parameter + * type is not assignable from the corresponding caller type. + * In this case, the adapter replaces all trailing arguments from + * the original trailing argument position onward, by + * a new array of type {@code arrayType}, whose elements + * comprise (in order) the replaced arguments. + *

+ * The caller type must provides as least enough arguments, + * and of the correct type, to satisfy the target's requirement for + * positional arguments before the trailing array argument. + * Thus, the caller must supply, at a minimum, {@code N-1} arguments, + * where {@code N} is the arity of the target. + * Also, there must exist conversions from the incoming arguments + * to the target's arguments. + * As with other uses of {@code invokeGeneric}, if these basic + * requirements are not fulfilled, a {@code WrongMethodTypeException} + * may be thrown. + *

+ * In all cases, what the target eventually returns is returned unchanged by the adapter. + *

+ * In the final case, it is exactly as if the target method handle were + * temporarily adapted with a {@linkplain #asCollector fixed arity collector} + * to the arity required by the caller type. + * (As with {@code asCollector}, if the array length is zero, + * a shared constant may be used instead of a new array. + * If the implied call to {@code asCollector} would throw + * an {@code IllegalArgumentException} or {@code WrongMethodTypeException}, + * the call to the variable arity adapter must throw + * {@code WrongMethodTypeException}.) + *

+ * The behavior of {@link #asType asType} is also specialized for + * variable arity adapters, to maintain the invariant that + * {@code invokeGeneric} is always equivalent to an {@code asType} + * call to adjust the target type, followed by {@code invokeExact}. + * Therefore, a variable arity adapter responds + * to an {@code asType} request by building a fixed arity collector, + * if and only if the adapter and requested type differ either + * in arity or trailing argument type. + * The resulting fixed arity collector has its type further adjusted + * (if necessary) to the requested type by pairwise conversion, + * as if by another application of {@code asType}. + *

+ * When a method handle is obtained by executing an {@code ldc} instruction + * of a {@code CONSTANT_MethodHandle} constant, and the target method is marked + * as a variable arity method (with the modifier bit {@code 0x0080}), + * the method handle will accept multiple arities, as if the method handle + * constant were created by means of a call to {@code asVarargsCollector}. + *

+ * In order to create a collecting adapter which collects a predetermined + * number of arguments, and whose type reflects this predetermined number, + * use {@link #asCollector asCollector} instead. + *

+ * No method handle transformations produce new method handles with + * variable arity, unless they are documented as doing so. + * Therefore, besides {@code asVarargsCollector}, + * all methods in {@code MethodHandle} and {@code MethodHandles} + * will return a method handle with fixed arity, + * except in the cases where they are specified to return their original + * operand (e.g., {@code asType} of the method handle's own type). + *

+ * Calling {@code asVarargsCollector} on a method handle which is already + * of variable arity will produce a method handle with the same type and behavior. + * It may (or may not) return the original variable arity method handle. + *

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

+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());
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
+List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals(1, ls.size());
+assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
+     * 
+ *

+ * Discussion: + * These rules are designed as a dynamically-typed variation + * of the Java rules for variable arity methods. + * In both cases, callers to a variable arity method or method handle + * can either pass zero or more positional arguments, or else pass + * pre-collected arrays of any length. Users should be aware of the + * special role of the final argument, and of the effect of a + * type match on that final argument, which determines whether + * or not a single trailing argument is interpreted as a whole + * array or a single element of an array to be collected. + * Note that the dynamic type of the trailing argument has no + * effect on this decision, only a comparison between the static + * type descriptor of the call site and the type of the method handle.) + *

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

+MethodHandle vamh = publicLookup()
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+  .asVarargsCollector(Object[].class);
+MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
+assert(vamh.type().equals(mh.type()));
+assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+boolean failed = false;
+try { mh.invokeGeneric(1,2,3); }
+catch (WrongMethodTypeException ex) { failed = true; }
+assert(failed);
+     * 
+ * This transformation has no behavioral effect if the method handle is + * not of variable arity. + * + * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments + * @return a new method handle which can collect any number of trailing arguments + * into an array, before calling the original method handle + * @throws IllegalArgumentException if {@code arrayType} is not an array type + * or {@code arrayType} is not assignable to this method handle's trailing parameter type + * @see #asCollector + * @see #isVarargsCollector + */ + public MethodHandle asVarargsCollector(Class arrayType) { + Class arrayElement = arrayType.getComponentType(); + if (arrayElement == null) throw newIllegalArgumentException("not an array type"); + return MethodHandles.asVarargsCollector(this, arrayType); + } + + /** + * Determine if this method handle + * supports {@linkplain #asVarargsCollector variable arity} calls. + * Such method handles arise from the following sources: + *
    + *
  • a call to {@linkplain #asVarargsCollector asVarargsCollector} + *
  • a call to a {@linkplain java.dyn.MethodHandles.Lookup lookup method} + * which resolves to a variable arity Java method or constructor + *
  • an {@code ldc} instruction of a {@code CONSTANT_MethodHandle} + * which resolves to a variable arity Java method or constructor + *
+ * @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls + * @see #asVarargsCollector + */ + public boolean isVarargsCollector() { + return false; + } + + /** + * Bind a value {@code x} to the first argument of a method handle, without invoking it. + * The new method handle adapts, as its target, + * to the current method handle. * The type of the bound handle will be * the same as the type of the target, except that a single leading * reference parameter will be omitted. @@ -614,79 +983,27 @@ public abstract class MethodHandle * to the leading parameter type of the target * @see MethodHandles#insertArguments */ - public final MethodHandle bindTo(Object x) { + public MethodHandle bindTo(Object x) { return MethodHandles.insertArguments(this, 0, x); } /** - * PROVISIONAL API, WORK IN PROGRESS: - * Create a new method handle with the same type as this one, - * but whose {@code asType} method invokes the given - * {@code typeHandler} on this method handle, - * instead of the standard {@code MethodHandles.convertArguments}. - *

- * The new method handle will have the same behavior as the - * old one when invoked by {@code invokeExact}. - * For {@code invokeGeneric} calls which exactly match - * the method type, the two method handles will also - * have the same behavior. - * For other {@code invokeGeneric} calls, the {@code typeHandler} - * will control the behavior of the new method handle. - *

- * Thus, a method handle with an {@code asType} handler can - * be configured to accept more than one arity of {@code invokeGeneric} - * call, and potentially every possible arity. - * It can also be configured to supply default values for - * optional arguments, when the caller does not specify them. - *

- * The given method handle must take two arguments and return - * one result. The result it returns must be a method handle - * of exactly the requested type. If the result returned by - * the target is null, a {@link NullPointerException} is thrown, - * else if the type of the target does not exactly match - * the requested type, a {@link WrongMethodTypeException} is thrown. - *

- * A method handle's type handler is not guaranteed to be called every - * time its {@code asType} or {@code invokeGeneric} method is called. - * If the implementation is faced is able to prove that an equivalent - * type handler call has already occurred (on the same two arguments), - * it may substitute the result of that previous invocation, without - * making a new invocation. Thus, type handlers should not (in general) - * perform significant side effects. - *

- * Therefore, the type handler is invoked as if by this code: + * Returns a string representation of the method handle, + * starting with the string {@code "MethodHandle"} and + * ending with the string representation of the method handle's type. + * In other words, this method returns a string equal to the value of: *

-     * MethodHandle target = this;      // original method handle
-     * MethodHandle adapter = ...;      // adapted method handle
-     * MethodType requestedType = ...;  // argument to asType()
-     * if (type().equals(requestedType))
-     *    return adapter;
-     * MethodHandle result = (MethodHandle)
-     *    typeHandler.invokeGeneric(target, requestedType);
-     * if (!result.type().equals(requestedType))
-     *    throw new WrongMethodTypeException();
-     * return result;
+     * "MethodHandle" + type().toString()
      * 
*

- * For example, here is a list-making variable-arity method handle: - *

-MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
-MethodHandle asList = lookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
-  return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
-}
-MethodHandle collectingTypeHandler = lookup()
-  .findStatic(lookup().lookupClass(), "collectingTypeHandler",
-     methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
-MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
-
-assertEquals("[]", makeAnyList.invokeGeneric().toString());
-assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
-assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
-     * 
+ * Note: Future releases of this API may add further information + * to the string representation. + * Therefore, the present syntax should not be parsed by applications. + * + * @return a string representation of the method handle */ - public MethodHandle withTypeHandler(MethodHandle typeHandler) { - return MethodHandles.withTypeHandler(this, typeHandler); + @Override + public String toString() { + return MethodHandleImpl.getNameString(IMPL_TOKEN, this); } } diff --git a/src/share/classes/java/dyn/MethodHandles.java b/src/share/classes/java/dyn/MethodHandles.java index d2de0c24a488f495abe99be6a83d62f4d674fd9c..3bc9bd4a327e9462de0fe5a5c06319d4d23dd2ed 100644 --- a/src/share/classes/java/dyn/MethodHandles.java +++ b/src/share/classes/java/dyn/MethodHandles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -29,6 +29,7 @@ import java.lang.reflect.*; import sun.dyn.Access; import sun.dyn.MemberName; import sun.dyn.MethodHandleImpl; +import sun.dyn.WrapperInstance; import sun.dyn.util.ValueConversions; import sun.dyn.util.VerifyAccess; import sun.dyn.util.Wrapper; @@ -45,10 +46,10 @@ import static sun.dyn.MemberName.newNoAccessException; * This class consists exclusively of static methods that operate on or return * method handles. They fall into several categories: *
    - *
  • Factory methods which create method handles for methods and fields. - *
  • Invoker methods which can invoke method handles on dynamically typed arguments and/or varargs arrays. - *
  • Combinator methods, which combine or transforming pre-existing method handles into new ones. - *
  • Factory methods which create method handles that emulate other common JVM operations or control flow patterns. + *
  • Lookup methods which help create method handles for methods and fields. + *
  • Combinator methods, which combine or transform pre-existing method handles into new ones. + *
  • Other factory methods to create method handles that emulate other common JVM operations or control flow patterns. + *
  • Wrapper methods which can convert between method handles and other function-like "SAM types". *
*

* @author John Rose, JSR 292 EG @@ -97,47 +98,155 @@ public class MethodHandles { * when the creation requires access checking. * Method handles do not perform * access checks when they are called, but rather when they are created. - * (This is a major difference - * from reflective {@link Method}, which performs access checking - * against every caller, on every call.) * Therefore, method handle access * restrictions must be enforced when a method handle is created. * The caller class against which those restrictions are enforced * is known as the {@linkplain #lookupClass lookup class}. - * A lookup object embodies an - * authenticated lookup class, and can be used to create any number + *

+ * A lookup class which needs to create method handles will call + * {@link MethodHandles#lookup MethodHandles.lookup} to create a factory for itself. + * When the {@code Lookup} factory object is created, the identity of the lookup class is + * determined, and securely stored in the {@code Lookup} object. + * The lookup class (or its delegates) may then use factory methods + * on the {@code Lookup} object to create method handles for access-checked members. + * This includes all methods, constructors, and fields which are allowed to the lookup class, + * even private ones. + *

+ * The factory methods on a {@code Lookup} object correspond to all major + * use cases for methods, constructors, and fields. + * Here is a summary of the correspondence between these factory methods and + * the behavior the resulting method handles: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
lookup expressionmemberbehavior
{@linkplain java.dyn.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)}FT f;(T) this.f;
{@linkplain java.dyn.MethodHandles.Lookup#findStaticGetter lookup.findStaticGetter(C.class,"f",FT.class)}static
FT f;
(T) C.f;
{@linkplain java.dyn.MethodHandles.Lookup#findSetter lookup.findSetter(C.class,"f",FT.class)}FT f;this.f = x;
{@linkplain java.dyn.MethodHandles.Lookup#findStaticSetter lookup.findStaticSetter(C.class,"f",FT.class)}static
FT f;
C.f = arg;
{@linkplain java.dyn.MethodHandles.Lookup#findVirtual lookup.findVirtual(C.class,"m",MT)}T m(A*);(T) this.m(arg*);
{@linkplain java.dyn.MethodHandles.Lookup#findStatic lookup.findStatic(C.class,"m",MT)}static
T m(A*);
(T) C.m(arg*);
{@linkplain java.dyn.MethodHandles.Lookup#findSpecial lookup.findSpecial(C.class,"m",MT,this.class)}T m(A*);(T) super.m(arg*);
{@linkplain java.dyn.MethodHandles.Lookup#findConstructor lookup.findConstructor(C.class,MT)}C(A*);(T) new C(arg*);
{@linkplain java.dyn.MethodHandles.Lookup#unreflectGetter lookup.unreflectGetter(aField)}(static)?
FT f;
(FT) aField.get(thisOrNull);
{@linkplain java.dyn.MethodHandles.Lookup#unreflectSetter lookup.unreflectSetter(aField)}(static)?
FT f;
aField.set(thisOrNull, arg);
{@linkplain java.dyn.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}(static)?
T m(A*);
(T) aMethod.invoke(thisOrNull, arg*);
{@linkplain java.dyn.MethodHandles.Lookup#unreflectConstructor lookup.unreflectConstructor(aConstructor)}C(A*);(C) aConstructor.newInstance(arg*);
{@linkplain java.dyn.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}(static)?
T m(A*);
(T) aMethod.invoke(thisOrNull, arg*);
+ *
+ * Here, the type {@code C} is the class or interface being searched for a member, + * documented as a parameter named {@code refc} in the lookup methods. + * The method or constructor type {@code MT} is composed from the return type {@code T} + * and the sequence of argument types {@code A*}. + * Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}. + * The formal parameter {@code this} stands for the self-reference of type {@code C}; + * if it is present, it is always the leading argument to the method handle invocation. + * The name {@code arg} stands for all the other method handle arguments. + * In the code examples for the Core Reflection API, the name {@code thisOrNull} + * stands for a null reference if the accessed method or field is static, + * and {@code this} otherwise. + * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand + * for reflective objects corresponding to the given members. + *

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

    + *
  • If {@code C} is not symbolically accessible from the lookup class's loader, + * the lookup can still succeed, even when there is no equivalent + * Java expression or bytecoded constant. + *
  • Likewise, if {@code T} or {@code MT} + * 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. + *
  • 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 + * security manager checks. + *
+ * + *

Access checking

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

+ * All access checks start from a {@code Lookup} object, which + * compares its recorded lookup class against all requests to + * create method handles. + * A single {@code Lookup} object can be used to create any number * of access-checked method handles, all checked against a single * lookup class. *

- * A class which needs to create method handles will call - * {@link MethodHandles#lookup MethodHandles.lookup} to create a factory for itself. - * It may then use this factory to create method handles on - * all of its methods, including private ones. - * It may also delegate the lookup (e.g., to a metaobject protocol) - * by passing the lookup object to other code. - * If this other code creates method handles, they will be access - * checked against the original lookup class, and not with any higher - * privileges. + * A {@code Lookup} object can be shared with other trusted code, + * such as a metaobject protocol. + * A shared {@code Lookup} object delegates the capability + * to create method handles on private members of the lookup class. + * Even if privileged code uses the {@code Lookup} object, + * the access checking is confined to the privileges of the + * original lookup class. *

- * Access checks only apply to named and reflected methods. - * Other method handle creation methods, such as - * {@link #convertArguments MethodHandles.convertArguments}, - * do not require any access checks, and can be done independently - * of any lookup class. - *

How access errors are handled

* A lookup can fail, because * the containing class is not accessible to the lookup class, or * because the desired class member is missing, or because the * desired class member is not accessible to the lookup class. - * It can also fail if a security manager is installed and refuses - * access. In any of these cases, an exception will be - * thrown from the attempted lookup. + * In any of these cases, a {@code ReflectiveOperationException} will be + * thrown from the attempted lookup. The exact class will be one of + * the following: + *
    + *
  • NoSuchMethodException — if a method is requested but does not exist + *
  • NoSuchFieldException — if a field is requested but does not exist + *
  • IllegalAccessException — if the member exists but an access check fails + *
*

* In general, the conditions under which a method handle may be - * created for a method {@code M} are exactly as restrictive as the conditions - * under which the lookup class could have compiled a call to {@code M}. + * looked up for a method {@code M} are exactly equivalent to the conditions + * under which the lookup class could have compiled and resolved a call to {@code M}. + * And the effect of invoking the method handle resulting from the lookup + * is exactly equivalent to executing the compiled and resolved call to {@code M}. + * The same point is true of fields and constructors. *

- * In some cases, this access is obtained by the Java compiler by creating + * In some cases, access between nested classes is obtained by the Java compiler by creating * an wrapper method to access a private method of another class * in the same top-level declaration. * For example, a nested class {@code C.D} @@ -149,6 +258,61 @@ public class MethodHandles { * A workaround for this limitation is the {@link Lookup#in Lookup.in} method, * which can transform a lookup on {@code C.E} into one on any of those other * classes, without special elevation of privilege. + *

+ * Although bytecode instructions can only refer to classes in + * a related class loader, this API can search for methods in any + * class, as long as a reference to its {@code Class} object is + * available. Such cross-loader references are also possible with the + * Core Reflection API, and are impossible to bytecode instructions + * such as {@code invokestatic} or {@code getfield}. + * There is a {@linkplain java.lang.SecurityManager security manager API} + * to allow applications to check such cross-loader references. + * These checks apply to both the {@code MethodHandles.Lookup} API + * and the Core Reflection API + * (as found on {@link java.lang.Class Class}). + *

+ * Access checks only apply to named and reflected methods, + * constructors, and fields. + * Other method handle creation methods, such as + * {@link #convertArguments MethodHandles.convertArguments}, + * do not require any access checks, and are done + * with static methods of {@link MethodHandles}, + * independently of any {@code Lookup} object. + * + *

Security manager interactions

+ * + * If a security manager is present, member lookups are subject to + * additional checks. + * From one to four calls are made to the security manager. + * Any of these calls can refuse access by throwing a + * {@link java.lang.SecurityException SecurityException}. + * Define {@code smgr} as the security manager, + * {@code refc} as the containing class in which the member + * is being sought, and {@code defc} as the class in which the + * member is actually defined. + * The calls are made according to the following rules: + *
    + *
  • In all cases, {@link SecurityManager#checkMemberAccess + * smgr.checkMemberAccess(refc, Member.PUBLIC)} is called. + *
  • If the class loader of the lookup class is not + * the same as or an ancestor of the class loader of {@code refc}, + * then {@link SecurityManager#checkPackageAccess + * smgr.checkPackageAccess(refcPkg)} is called, + * where {@code refcPkg} is the package of {@code refc}. + *
  • If the retrieved member is not public, + * {@link SecurityManager#checkMemberAccess + * smgr.checkMemberAccess(defc, Member.DECLARED)} is called. + * (Note that {@code defc} might be the same as {@code refc}.) + *
  • If the retrieved member is not public, + * and if {@code defc} and {@code refc} are in different class loaders, + * and if the class loader of the lookup class is not + * the same as or an ancestor of the class loader of {@code defc}, + * then {@link SecurityManager#checkPackageAccess + * smgr.checkPackageAccess(defcPkg)} is called, + * where {@code defcPkg} is the package of {@code defc}. + *
+ * In all cases, the requesting class presented to the security + * manager will be the lookup class from the current {@code Lookup} object. */ public static final class Lookup { @@ -194,12 +358,12 @@ public class MethodHandles { return (mods != 0) ? mods : PACKAGE; } - /** Which class is performing the lookup? It is this class against + /** Tells which class is performing the lookup. It is this class against * which checks are performed for visibility and access permissions. *

* The class implies a maximum level of access permission, * but the permissions may be additionally limited by the bitmask - * {@link #lookupModes}, which controls whether non-public members + * {@link #lookupModes lookupModes}, which controls whether non-public members * can be accessed. */ public Class lookupClass() { @@ -211,7 +375,7 @@ public class MethodHandles { return (allowedModes == TRUSTED) ? null : lookupClass; } - /** Which types of members can this lookup object produce? + /** Tells which access-protection classes of members this lookup object can produce. * The result is a bit-mask of the bits * {@linkplain #PUBLIC PUBLIC (0x01)}, * {@linkplain #PRIVATE PRIVATE (0x02)}, @@ -257,7 +421,7 @@ public class MethodHandles { } /** - * Create a lookup on the specified new lookup class. + * Creates a lookup on the specified new lookup class. * The resulting object will report the specified * class as its own {@link #lookupClass lookupClass}. *

@@ -275,6 +439,10 @@ public class MethodHandles { * then no members, not even public members, will be accessible. * (In all other cases, public members will continue to be accessible.) * + * + * @param requestedLookupClass the desired lookup class for the new lookup object + * @return a lookup object which reports the desired lookup class + * @throws NullPointerException if the argument is null */ public Lookup in(Class requestedLookupClass) { requestedLookupClass.getClass(); // null check @@ -322,11 +490,12 @@ public class MethodHandles { } /** - * Display the name of the class from which lookups are to be made. + * Displays the name of the class from which lookups are to be made. * (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.) * If there are restrictions on the access permitted to this lookup, * this is indicated by adding a suffix to the class name, consisting - * of a slash and a keyword. The keyword is chosen as follows: + * of a slash and a keyword. The keyword represents the strongest + * allowed access, and is chosen as follows: *

    *
  • If no access is allowed, the suffix is "/noaccess". *
  • If only public access is allowed, the suffix is "/public". @@ -337,26 +506,37 @@ public class MethodHandles { * access (public, package, private, and protected) is allowed. * In this case, no suffix is added. * This is true only of an object obtained originally from - * {@link java.dyn.MethodHandles#lookup() MethodHandles.lookup}. - * Objects created by {@link java.dyn.MethodHandles.Lookup#in() Lookup#in} + * {@link java.dyn.MethodHandles#lookup MethodHandles.lookup}. + * Objects created by {@link java.dyn.MethodHandles.Lookup#in Lookup.in} * always have restricted access, and will display a suffix. + *

    + * (It may seem strange that protected access should be + * stronger than private access. Viewed independently from + * package access, protected access is the first to be lost, + * because it requires a direct subclass relationship between + * caller and callee.) + * @see #in */ @Override public String toString() { String cname = lookupClass.getName(); switch (allowedModes) { - case TRUSTED: - return "/trusted"; // internal only + case 0: // no privileges + return cname + "/noaccess"; case PUBLIC: return cname + "/public"; case PUBLIC|PACKAGE: return cname + "/package"; - case 0: // no privileges - return cname + "/noaccess"; + case ALL_MODES & ~PROTECTED: + return cname + "/private"; case ALL_MODES: return cname; - default: - return cname + "/private"; + case TRUSTED: + return "/trusted"; // internal only; not exported + default: // Should not happen, but it's a bitfield... + cname = cname + "/" + Integer.toHexString(allowedModes); + assert(false) : cname; + return cname; } } @@ -371,29 +551,37 @@ public class MethodHandles { } /** - * Produce a method handle for a static method. + * Produces a method handle for a static method. * The type of the method handle will be that of the method. * (Since static methods do not take receivers, there is no * additional receiver argument inserted into the method handle type, - * as there would be with {@link #findVirtual} or {@link #findSpecial}.) + * as there would be with {@link #findVirtual findVirtual} or {@link #findSpecial findSpecial}.) * The method and all its argument types must be accessible to the lookup class. * If the method's class has not yet been initialized, that is done * immediately, before the method handle is returned. + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class from which the method is accessed * @param name the name of the method * @param type the type of the method * @return the desired method handle - * @exception NoAccessException if the method does not exist or access checking fails + * @throws NoSuchMethodException if the method does not exist + * @throws IllegalAccessException if access checking fails, or if the method is not {@code static} + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ public - MethodHandle findStatic(Class refc, String name, MethodType type) throws NoAccessException { + MethodHandle findStatic(Class refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MemberName method = resolveOrFail(refc, name, type, true); checkMethod(refc, method, true); return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClassOrNull()); } /** - * Produce a method handle for a virtual method. + * Produces a method handle for a virtual method. * The type of the method handle will be that of the method, * with the receiver type (usually {@code refc}) prepended. * The method and all its argument types must be accessible to the lookup class. @@ -403,13 +591,31 @@ public class MethodHandles { * implementation to enter. * (The dispatching action is identical with that performed by an * {@code invokevirtual} or {@code invokeinterface} instruction.) + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. + *

    + * 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 + * method handle is equivalent to one produced by + * {@link java.dyn.MethodHandles#exactInvoker MethodHandles.exactInvoker} or + * {@link java.dyn.MethodHandles#genericInvoker MethodHandles.genericInvoker} + * with the same {@code type} argument. + * * @param refc the class or interface from which the method is accessed * @param name the name of the method * @param type the type of the method, with the receiver argument omitted * @return the desired method handle - * @exception NoAccessException if the method does not exist or access checking fails + * @throws NoSuchMethodException if the method does not exist + * @throws IllegalAccessException if access checking fails, or if the method is {@code static} + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle findVirtual(Class refc, String name, MethodType type) throws NoAccessException { + public MethodHandle findVirtual(Class refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MemberName method = resolveOrFail(refc, name, type, false); checkMethod(refc, method, false); MethodHandle mh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClassOrNull()); @@ -417,7 +623,7 @@ public class MethodHandles { } /** - * Produce a method handle which creates an object and initializes it, using + * Produces a method handle which creates an object and initializes it, using * the constructor of the specified type. * The parameter types of the method handle will be those of the constructor, * while the return type will be a reference to the constructor's class. @@ -426,23 +632,47 @@ public class MethodHandles { * immediately, before the method handle is returned. *

    * Note: The requested type must have a return type of {@code void}. - * This is consistent with the JVM's treatment of constructor signatures. + * This is consistent with the JVM's treatment of constructor type descriptors. + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the constructor's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class or interface from which the method is accessed * @param type the type of the method, with the receiver argument omitted, and a void return type * @return the desired method handle - * @exception NoAccessException if the method does not exist or access checking fails + * @throws NoSuchMethodException if the constructor does not exist + * @throws IllegalAccessException if access checking fails + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle findConstructor(Class refc, MethodType type) throws NoAccessException { + public MethodHandle findConstructor(Class refc, MethodType type) throws NoSuchMethodException, IllegalAccessException { String name = ""; MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull()); assert(ctor.isConstructor()); checkAccess(refc, ctor); MethodHandle rawMH = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull()); - return MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawMH); + MethodHandle allocMH = MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawMH); + return fixVarargs(allocMH, rawMH); + } + + /** Return a version of MH which matches matchMH w.r.t. isVarargsCollector. */ + private static MethodHandle fixVarargs(MethodHandle mh, MethodHandle matchMH) { + boolean va1 = mh.isVarargsCollector(); + boolean va2 = matchMH.isVarargsCollector(); + if (va1 == va2) { + return mh; + } else if (va2) { + MethodType type = mh.type(); + int arity = type.parameterCount(); + return mh.asVarargsCollector(type.parameterType(arity-1)); + } else { + throw new InternalError("already varargs, but template is not: "+mh); + } } /** - * Produce an early-bound method handle for a virtual method, + * Produces an early-bound method handle for a virtual method, * as if called from an {@code invokespecial} * instruction from {@code caller}. * The type of the method handle will be that of the method, @@ -458,15 +688,23 @@ public class MethodHandles { * If the explicitly specified caller class is not identical with the * lookup class, or if this lookup object does not have private access * privileges, the access fails. + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class or interface from which the method is accessed * @param name the name of the method (which must not be "<init>") * @param type the type of the method, with the receiver argument omitted * @param specialCaller the proposed calling class to perform the {@code invokespecial} * @return the desired method handle - * @exception NoAccessException if the method does not exist or access checking fails + * @throws NoSuchMethodException if the method does not exist + * @throws IllegalAccessException if access checking fails + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ public MethodHandle findSpecial(Class refc, String name, MethodType type, - Class specialCaller) throws NoAccessException { + Class specialCaller) throws NoSuchMethodException, IllegalAccessException { checkSpecialCaller(specialCaller); MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller); checkMethod(refc, method, false); @@ -475,69 +713,89 @@ public class MethodHandles { } /** - * Produce a method handle giving read access to a non-static field. + * Produces a method handle giving read access to a non-static field. * The type of the method handle will have a return type of the field's * value type. * The method handle's single argument will be the instance containing * the field. * Access checking is performed immediately on behalf of the lookup class. + * @param refc the class or interface from which the method is accessed * @param name the field's name * @param type the field's type * @return a method handle which can load values from the field - * @exception NoAccessException if access checking fails + * @throws NoSuchFieldException if the field does not exist + * @throws IllegalAccessException if access checking fails, or if the field is {@code static} + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle findGetter(Class refc, String name, Class type) throws NoAccessException { + public MethodHandle findGetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { return makeAccessor(refc, name, type, false, false); } /** - * Produce a method handle giving write access to a non-static field. + * Produces a method handle giving write access to a non-static field. * The type of the method handle will have a void return type. * The method handle will take two arguments, the instance containing * the field, and the value to be stored. * The second argument will be of the field's value type. * Access checking is performed immediately on behalf of the lookup class. + * @param refc the class or interface from which the method is accessed * @param name the field's name * @param type the field's type * @return a method handle which can store values into the field - * @exception NoAccessException if access checking fails + * @throws NoSuchFieldException if the field does not exist + * @throws IllegalAccessException if access checking fails, or if the field is {@code static} + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle findSetter(Class refc, String name, Class type) throws NoAccessException { + public MethodHandle findSetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { return makeAccessor(refc, name, type, false, true); } /** - * Produce a method handle giving read access to a static field. + * Produces a method handle giving read access to a static field. * The type of the method handle will have a return type of the field's * value type. * The method handle will take no arguments. * Access checking is performed immediately on behalf of the lookup class. + * @param refc the class or interface from which the method is accessed * @param name the field's name * @param type the field's type * @return a method handle which can load values from the field - * @exception NoAccessException if access checking fails + * @throws NoSuchFieldException if the field does not exist + * @throws IllegalAccessException if access checking fails, or if the field is not {@code static} + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle findStaticGetter(Class refc, String name, Class type) throws NoAccessException { + public MethodHandle findStaticGetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { return makeAccessor(refc, name, type, true, false); } /** - * Produce a method handle giving write access to a static field. + * Produces a method handle giving write access to a static field. * The type of the method handle will have a void return type. * The method handle will take a single * argument, of the field's value type, the value to be stored. * Access checking is performed immediately on behalf of the lookup class. + * @param refc the class or interface from which the method is accessed * @param name the field's name * @param type the field's type * @return a method handle which can store values into the field - * @exception NoAccessException if access checking fails + * @throws NoSuchFieldException if the field does not exist + * @throws IllegalAccessException if access checking fails, or if the field is not {@code static} + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle findStaticSetter(Class refc, String name, Class type) throws NoAccessException { + public MethodHandle findStaticSetter(Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { return makeAccessor(refc, name, type, true, true); } /** - * Produce an early-bound method handle for a non-static method. + * Produces an early-bound method handle for a non-static method. * The receiver must have a supertype {@code defc} in which a method * of the given name and type is accessible to the lookup class. * The method and all its argument types must be accessible to the lookup class. @@ -547,28 +805,47 @@ public class MethodHandles { * so that every call to the method handle will invoke the * requested method on the given receiver. *

    - * This is equivalent to the following expression: - * - * {@link #insertArguments insertArguments}({@link #findVirtual findVirtual}(defc, name, type), receiver) - * + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set + * and the trailing array argument is not the only argument. + * (If the trailing array argument is the only argument, + * the given receiver value will be bound to it.) + *

    + * This is equivalent to the following code: + *

    +MethodHandle mh0 = {@link #findVirtual findVirtual}(defc, name, type);
    +MethodHandle mh1 = mh0.{@link MethodHandle#bindTo bindTo}(receiver);
    +MethodType mt1 = mh1.type();
    +if (mh0.isVarargsCollector() && mt1.parameterCount() > 0) {
    +  mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
    +return mh1;
    +         * 
    * where {@code defc} is either {@code receiver.getClass()} or a super * type of that class, in which the requested method is accessible * to the lookup class. + * (Note that {@code bindTo} does not preserve variable arity.) * @param receiver the object from which the method is accessed * @param name the name of the method * @param type the type of the method, with the receiver argument omitted * @return the desired method handle - * @exception NoAccessException if the method does not exist or access checking fails + * @throws NoSuchMethodException if the method does not exist + * @throws IllegalAccessException if access checking fails + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws NullPointerException if any argument is null */ - public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException { + public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { Class refc = receiver.getClass(); // may get NPE MemberName method = resolveOrFail(refc, name, type, false); checkMethod(refc, method, false); MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClassOrNull()); MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver); if (bmh == null) - throw newNoAccessException(method, lookupClass()); - return bmh; + throw newNoAccessException(method, this); + if (dmh.type().parameterCount() == 0) + return dmh; // bound the trailing parameter; no varargs possible + return fixVarargs(bmh, dmh); } /** @@ -581,11 +858,16 @@ public class MethodHandles { * If the method's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class. * If m is not public, do not share the resulting handle with untrusted parties. + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param m the reflected method * @return a method handle which can invoke the reflected method - * @exception NoAccessException if access checking fails + * @throws IllegalAccessException if access checking fails + * @throws NullPointerException if the argument is null */ - public MethodHandle unreflect(Method m) throws NoAccessException { + public MethodHandle unreflect(Method m) throws IllegalAccessException { MemberName method = new MemberName(m); assert(method.isMethod()); if (!m.isAccessible()) checkMethod(method.getDeclaringClass(), method, method.isStatic()); @@ -595,7 +877,7 @@ public class MethodHandles { } /** - * Produce a method handle for a reflected method. + * Produces a method handle for a reflected method. * It will bypass checks for overriding methods on the receiver, * as if by a {@code invokespecial} instruction from within the {@code specialCaller}. * The type of the method handle will be that of the method, @@ -603,12 +885,17 @@ public class MethodHandles { * If the method's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class, * as if {@code invokespecial} instruction were being linked. + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param m the reflected method * @param specialCaller the class nominally calling the method * @return a method handle which can invoke the reflected method - * @exception NoAccessException if access checking fails + * @throws IllegalAccessException if access checking fails + * @throws NullPointerException if any argument is null */ - public MethodHandle unreflectSpecial(Method m, Class specialCaller) throws NoAccessException { + public MethodHandle unreflectSpecial(Method m, Class specialCaller) throws IllegalAccessException { checkSpecialCaller(specialCaller); MemberName method = new MemberName(m); assert(method.isMethod()); @@ -619,7 +906,7 @@ public class MethodHandles { } /** - * Produce a method handle for a reflected constructor. + * Produces a method handle for a reflected constructor. * The type of the method handle will be that of the constructor, * with the return type changed to the declaring class. * The method handle will perform a {@code newInstance} operation, @@ -628,20 +915,26 @@ public class MethodHandles { *

    * If the constructor's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class. + *

    + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the constructor's variable arity modifier bit ({@code 0x0080}) is set. * @param c the reflected constructor * @return a method handle which can invoke the reflected constructor - * @exception NoAccessException if access checking fails + * @throws IllegalAccessException if access checking fails + * @throws NullPointerException if the argument is null */ - public MethodHandle unreflectConstructor(Constructor c) throws NoAccessException { + public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException { MemberName ctor = new MemberName(c); assert(ctor.isConstructor()); if (!c.isAccessible()) checkAccess(c.getDeclaringClass(), ctor); MethodHandle rawCtor = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull()); - return MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawCtor); + MethodHandle allocator = MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawCtor); + return fixVarargs(allocator, rawCtor); } /** - * Produce a method handle giving read access to a reflected field. + * Produces a method handle giving read access to a reflected field. * The type of the method handle will have a return type of the field's * value type. * If the field is static, the method handle will take no arguments. @@ -651,14 +944,15 @@ public class MethodHandles { * access checking is performed immediately on behalf of the lookup class. * @param f the reflected field * @return a method handle which can load values from the reflected field - * @exception NoAccessException if access checking fails + * @throws IllegalAccessException if access checking fails + * @throws NullPointerException if the argument is null */ - public MethodHandle unreflectGetter(Field f) throws NoAccessException { + public MethodHandle unreflectGetter(Field f) throws IllegalAccessException { return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false); } /** - * Produce a method handle giving write access to a reflected field. + * Produces a method handle giving write access to a reflected field. * The type of the method handle will have a void return type. * If the field is static, the method handle will take a single * argument, of the field's value type, the value to be stored. @@ -668,40 +962,47 @@ public class MethodHandles { * access checking is performed immediately on behalf of the lookup class. * @param f the reflected field * @return a method handle which can store values into the reflected field - * @exception NoAccessException if access checking fails + * @throws IllegalAccessException if access checking fails + * @throws NullPointerException if the argument is null */ - public MethodHandle unreflectSetter(Field f) throws NoAccessException { + public MethodHandle unreflectSetter(Field f) throws IllegalAccessException { return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true); } /// Helper methods, all package-private. - MemberName resolveOrFail(Class refc, String name, Class type, boolean isStatic) throws NoAccessException { + MemberName resolveOrFail(Class refc, String name, Class type, boolean isStatic) throws NoSuchFieldException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve + name.getClass(); type.getClass(); // NPE int mods = (isStatic ? Modifier.STATIC : 0); - return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); + return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(), + NoSuchFieldException.class); } - MemberName resolveOrFail(Class refc, String name, MethodType type, boolean isStatic) throws NoAccessException { + MemberName resolveOrFail(Class refc, String name, MethodType type, boolean isStatic) throws NoSuchMethodException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve + name.getClass(); type.getClass(); // NPE int mods = (isStatic ? Modifier.STATIC : 0); - return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); + return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(), + NoSuchMethodException.class); } MemberName resolveOrFail(Class refc, String name, MethodType type, boolean isStatic, - boolean searchSupers, Class specialCaller) throws NoAccessException { + boolean searchSupers, Class specialCaller) throws NoSuchMethodException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve + name.getClass(); type.getClass(); // NPE int mods = (isStatic ? Modifier.STATIC : 0); - return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller); + return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller, + NoSuchMethodException.class); } - void checkSymbolicClass(Class refc) throws NoAccessException { + void checkSymbolicClass(Class refc) throws IllegalAccessException { Class caller = lookupClassOrNull(); if (caller != null && !VerifyAccess.isClassAccessible(refc, caller)) - throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller); + throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), this); } - void checkMethod(Class refc, MemberName m, boolean wantStatic) throws NoAccessException { + void checkMethod(Class refc, MemberName m, boolean wantStatic) throws IllegalAccessException { String message; if (m.isConstructor()) message = "expected a method, not a constructor"; @@ -711,10 +1012,10 @@ public class MethodHandles { message = wantStatic ? "expected a static method" : "expected a non-static method"; else { checkAccess(refc, m); return; } - throw newNoAccessException(message, m, lookupClass()); + throw newNoAccessException(message, m, this); } - void checkAccess(Class refc, MemberName m) throws NoAccessException { + void checkAccess(Class refc, MemberName m) throws IllegalAccessException { int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; int mods = m.getModifiers(); @@ -729,22 +1030,25 @@ public class MethodHandles { && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass())) // Protected members can also be checked as if they were package-private. return; - throw newNoAccessException(accessFailedMessage(refc, m), m, lookupClass()); + throw newNoAccessException(accessFailedMessage(refc, m), m, this); } String accessFailedMessage(Class refc, MemberName m) { Class defc = m.getDeclaringClass(); int mods = m.getModifiers(); - if (!VerifyAccess.isClassAccessible(defc, lookupClass())) + // check the class first: + boolean classOK = (Modifier.isPublic(defc.getModifiers()) && + (defc == refc || + Modifier.isPublic(refc.getModifiers()))); + if (!classOK && (allowedModes & PACKAGE) != 0) { + classOK = (VerifyAccess.isClassAccessible(defc, lookupClass()) && + (defc == refc || + VerifyAccess.isClassAccessible(refc, lookupClass()))); + } + if (!classOK) return "class is not public"; - if (refc != defc && !VerifyAccess.isClassAccessible(refc, lookupClass())) - return "symbolic reference "+refc.getName()+" is not public"; if (Modifier.isPublic(mods)) return "access to public member failed"; // (how?) - else if (allowedModes == PUBLIC) - return "member is not public"; - else if (allowedModes == 0) - return "attempted member access through a non-public class"; if (Modifier.isPrivate(mods)) return "member is private"; if (Modifier.isProtected(mods)) @@ -754,17 +1058,17 @@ public class MethodHandles { private static final boolean ALLOW_NESTMATE_ACCESS = false; - void checkSpecialCaller(Class specialCaller) throws NoAccessException { + void checkSpecialCaller(Class specialCaller) throws IllegalAccessException { if (allowedModes == TRUSTED) return; if ((allowedModes & PRIVATE) == 0 || (specialCaller != lookupClass() && !(ALLOW_NESTMATE_ACCESS && VerifyAccess.isSamePackageMember(specialCaller, lookupClass())))) throw newNoAccessException("no private access for invokespecial", - new MemberName(specialCaller), lookupClass()); + new MemberName(specialCaller), this); } - MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException { + MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws IllegalAccessException { // The accessing class only has the right to use a protected member // on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc. if (!method.isProtected() || method.isStatic() @@ -776,7 +1080,7 @@ public class MethodHandles { else return restrictReceiver(method, mh, lookupClass()); } - MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class caller) throws NoAccessException { + MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class caller) throws IllegalAccessException { assert(!method.isStatic()); Class defc = method.getDeclaringClass(); // receiver type of mh is too wide if (defc.isInterface() || !defc.isAssignableFrom(caller)) { @@ -785,22 +1089,23 @@ public class MethodHandles { MethodType rawType = mh.type(); if (rawType.parameterType(0) == caller) return mh; MethodType narrowType = rawType.changeParameterType(0, caller); - return MethodHandleImpl.convertArguments(IMPL_TOKEN, mh, narrowType, rawType, null); + MethodHandle narrowMH = MethodHandleImpl.convertArguments(IMPL_TOKEN, mh, narrowType, rawType, null); + return fixVarargs(narrowMH, mh); } MethodHandle makeAccessor(Class refc, String name, Class type, - boolean isStatic, boolean isSetter) throws NoAccessException { + boolean isStatic, boolean isSetter) throws NoSuchFieldException, IllegalAccessException { MemberName field = resolveOrFail(refc, name, type, isStatic); if (isStatic != field.isStatic()) throw newNoAccessException(isStatic ? "expected a static field" : "expected a non-static field", - field, lookupClass()); + field, this); return makeAccessor(refc, field, false, isSetter); } MethodHandle makeAccessor(Class refc, MemberName field, - boolean trusted, boolean isSetter) throws NoAccessException { + boolean trusted, boolean isSetter) throws IllegalAccessException { assert(field.isField()); if (trusted) return MethodHandleImpl.accessField(IMPL_TOKEN, field, isSetter, lookupClassOrNull()); @@ -811,12 +1116,13 @@ public class MethodHandles { } /** - * Produce a method handle giving read access to elements of an array. + * Produces a method handle giving read access to elements of an array. * The type of the method handle will have a return type of the array's * element type. Its first argument will be the array type, * and the second will be {@code int}. * @param arrayClass an array type * @return a method handle which can load values from the given array type + * @throws NullPointerException if the argument is null * @throws IllegalArgumentException if arrayClass is not an array type */ public static @@ -825,11 +1131,12 @@ public class MethodHandles { } /** - * Produce a method handle giving write access to elements of an array. + * Produces a method handle giving write access to elements of an array. * The type of the method handle will have a void return type. * Its last argument will be the array's element type. * The first and second arguments will be the array type and int. * @return a method handle which can store values into the array type + * @throws NullPointerException if the argument is null * @throws IllegalArgumentException if arrayClass is not an array type */ public static @@ -840,47 +1147,7 @@ public class MethodHandles { /// method handle invocation (reflective style) /** - * Produce a method handle which will invoke any method handle of the - * given type on a standard set of {@code Object} type arguments. - * The resulting invoker will be a method handle with the following - * arguments: - *

      - *
    • a single {@code MethodHandle} target - *
    • zero or more {@code Object} values (one for each argument in {@code type}) - *
    - *

    - * The invoker will behave like a call to {@link MethodHandle.invokeGeneric} 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} - * is used to convert the target to the required {@code type}. - *

    - * The type of the returned invoker will not be the given {@code type}, but rather - * will have all parameter and return types replaced by {@code Object}. - *

    - * Before invoking its target, the invoker will apply reference casts as - * necessary and unbox and widen primitive arguments, as if by {@link #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. - *

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

    -     * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type);
    -     * MethodType genericType = type.generic();
    -     * genericType = genericType.insertParameterType(0, MethodHandle.class);
    -     * return invoker.asType(genericType);
    -     * 
    - * @param type the type of target methods which the invoker will apply to - * @return a method handle suitable for invoking any method handle of the given type - */ - static public - MethodHandle genericInvoker(MethodType type) { - return invokers(type).genericInvoker(); - } - - /** - * Produce a method handle which will invoke any method handle of the + * Produces a method handle which will invoke any method handle of the * given {@code type} on a standard set of {@code Object} type arguments * and a single trailing {@code Object[]} array. * The resulting invoker will be a method handle with the following @@ -891,10 +1158,10 @@ public class MethodHandles { *
  • an {@code Object[]} array containing more arguments *
*

- * The invoker will behave like a call to {@link MethodHandle.invokeGeneric} with + * The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} 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} + * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType} * is used to convert the target to the required {@code type}. *

* The type of the returned invoker will not be the given {@code type}, but rather @@ -909,34 +1176,56 @@ public class MethodHandles { *

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

-     * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type);
-     * MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
-     * vaType = vaType.insertParameterType(0, MethodHandle.class);
-     * int spreadArgCount = type.parameterCount - objectArgCount;
-     * invoker = invoker.asSpreader(Object.class, spreadArgCount);
-     * return invoker.asType(vaType);
+MethodHandle invoker = MethodHandles.genericInvoker(type);
+int spreadArgCount = type.parameterCount - objectArgCount;
+invoker = invoker.asSpreader(Object[].class, spreadArgCount);
+return invoker;
      * 
+ *

+ * This method throws no reflective or security exceptions. * @param type the desired target type * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments * @return a method handle suitable for invoking any method handle of the given type */ static public - MethodHandle varargsInvoker(MethodType type, int objectArgCount) { + MethodHandle spreadInvoker(MethodType type, int objectArgCount) { if (objectArgCount < 0 || objectArgCount > type.parameterCount()) throw new IllegalArgumentException("bad argument count "+objectArgCount); - return invokers(type).varargsInvoker(objectArgCount); + return invokers(type).spreadInvoker(objectArgCount); } /** - * Produce a method handle which will take a invoke any method handle of the - * given type. The resulting invoker will have a type which is + * Produces a special invoker method handle which can be used to + * invoke any method handle of the given type, as if by {@code 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}. *

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

-     * lookup().findVirtual(MethodHandle.class, "invokeExact", type);
+publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
      * 
+ * + *

+ * Discussion: + * Invoker method handles can be useful when working with variable method handles + * of unknown types. + * 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...)}. + * (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, + * they can be applied once to the invoker {@code X} and reused on many {@code M} + * method handle values, as long as they are compatible with the type of {@code X}. + *

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

+ * This method throws no reflective or security exceptions. * @param type the desired target type * @return a method handle suitable for invoking any method handle of the given type */ @@ -945,12 +1234,38 @@ public class MethodHandles { return invokers(type).exactInvoker(); } + /** + * Produces a special invoker method handle which can be used to + * invoke any method handle of the given type, as if by {@code invokeGeneric}. + * 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. + *

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

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

+ * This method throws no reflective or security exceptions. + * @param type the desired target type + * @return a method handle suitable for invoking any method handle convertible to the given type + */ + static public + MethodHandle genericInvoker(MethodType type) { + return invokers(type).genericInvoker(); + } + static Invokers invokers(MethodType type) { return MethodTypeImpl.invokers(IMPL_TOKEN, type); } /** - * WORK IN PROGRESS: * Perform value checking, exactly as if for an adapted method handle. * It is assumed that the given value is either null, of type T0, * or (if T0 is primitive) of the wrapper type corresponding to T0. @@ -1021,7 +1336,7 @@ public class MethodHandles { /// method handle modification (creation from other method handles) /** - * Produce a method handle which adapts the type of the + * Produces a method handle which adapts the type of the * given method handle to a new type by pairwise argument conversion. * The original type and new type must have the same number of arguments. * The resulting method handle is guaranteed to report a type @@ -1060,6 +1375,7 @@ public class MethodHandles { * @return a method handle which delegates to {@code target} after performing * any necessary argument conversions, and arranges for any * necessary return value conversions + * @throws NullPointerException if either argument is null * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandle#asType * @see MethodHandles#explicitCastArguments @@ -1081,7 +1397,7 @@ public class MethodHandles { } /** - * Produce a method handle which adapts the type of the + * Produces a method handle which adapts the type of the * given method handle to a new type by pairwise argument conversion. * The original type and new type must have the same number of arguments. * The resulting method handle is guaranteed to report a type @@ -1113,6 +1429,7 @@ public class MethodHandles { * @return a method handle which delegates to {@code target} after performing * any necessary argument conversions, and arranges for any * necessary return value conversions + * @throws NullPointerException if either argument is null * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandle#asType * @see MethodHandles#convertArguments @@ -1160,7 +1477,7 @@ public class MethodHandles { */ /** - * Produce a method handle which adapts the calling sequence of the + * Produces a method handle which adapts the calling sequence of the * given method handle to a new type, by reordering the arguments. * The resulting method handle is guaranteed to report a type * which is equal to the desired new type. @@ -1208,6 +1525,7 @@ assert((int)twice.invokeExact(21) == 42); * @param reorder a string which controls the reordering * @return a method handle which delegates to {@code target} after it * drops unused arguments and moves and/or duplicates the other arguments + * @throws NullPointerException if any argument is null */ public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { @@ -1233,11 +1551,10 @@ assert((int)twice.invokeExact(21) == 42); } /** - * METHOD WILL BE REMOVED FOR PFD: * Equivalent to the following code: *

      * int spreadPos = newType.parameterCount() - 1;
-     * Class spreadType = newType.parameterType(spreadPos);
+     * Class<?> spreadType = newType.parameterType(spreadPos);
      * int spreadCount = target.type().parameterCount() - spreadPos;
      * MethodHandle adapter = target.asSpreader(spreadType, spreadCount);
      * adapter = adapter.asType(newType);
@@ -1247,9 +1564,8 @@ assert((int)twice.invokeExact(21) == 42);
      * @param newType the expected type of the new method handle
      * @return a method handle which spreads its final argument,
      *         before calling the original method handle
-     * @deprecated Use {@link MethodHandle#asSpreader}
      */
-    public static
+    /*non-public*/ static
     MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
         MethodType oldType = target.type();
         int inargs  = newType.parameterCount();
@@ -1267,11 +1583,10 @@ assert((int)twice.invokeExact(21) == 42);
     }
 
     /**
-     * METHOD WILL BE REMOVED FOR PFD:
      * Equivalent to the following code:
      * 

      * int collectPos = target.type().parameterCount() - 1;
-     * Class collectType = target.type().parameterType(collectPos);
+     * Class<?> collectType = target.type().parameterType(collectPos);
      * if (!collectType.isArray())  collectType = Object[].class;
      * int collectCount = newType.parameterCount() - collectPos;
      * MethodHandle adapter = target.asCollector(collectType, collectCount);
@@ -1282,9 +1597,8 @@ assert((int)twice.invokeExact(21) == 42);
      * @param newType the expected type of the new method handle
      * @return a method handle which collects some trailing argument
      *         into an array, before calling the original method handle
-     * @deprecated Use {@link MethodHandle#asCollector} instead.
      */
-    public static
+    /*non-public*/ static
     MethodHandle collectArguments(MethodHandle target, MethodType newType) {
         MethodType oldType = target.type();
         int inargs  = newType.parameterCount();
@@ -1301,7 +1615,7 @@ assert((int)twice.invokeExact(21) == 42);
     }
 
     /**
-     * Produce a method handle of the requested return type which returns the given
+     * Produces a method handle of the requested return type which returns the given
      * constant value every time it is invoked.
      * 

* Before the method handle is returned, the passed-in value is converted to the requested type. @@ -1312,12 +1626,15 @@ assert((int)twice.invokeExact(21) == 42); * @param type the return type of the desired method handle * @param value the value to return * @return a method handle of the given return type and no arguments, which always returns the given value - * @throws WrongMethodTypeException if the value cannot be converted to the required return type + * @throws NullPointerException if the {@code type} argument is null + * @throws ClassCastException if the value cannot be converted to the required return type + * @throws IllegalArgumentException if the given type is {@code void.class} */ public static MethodHandle constant(Class type, Object value) { if (type.isPrimitive()) { - if (type == void.class) return identity(type); + if (type == void.class) + throw newIllegalArgumentException("void type"); Wrapper w = Wrapper.forPrimitiveType(type); return identity(type).bindTo(w.convert(value, type)); } else { @@ -1326,64 +1643,22 @@ assert((int)twice.invokeExact(21) == 42); } /** - * Produce a method handle of the requested type which returns the given - * constant value every time it is invoked. - *

- * Before the method handle is returned, the passed-in value is converted to the requested return type, - * as if by {@link #explicitCastArguments #explicitCastArguments}. - * That is, if the return type is primitive, the value is unboxed, - * and the primitive value is widened and/or narrowed. - * Otherwise, reference conversions are attempted. - * @param type the type of the desired method handle - * @param value the value to return - * @return a method handle of the given return type and no arguments, which always returns the given value - * @throws WrongMethodTypeException if the value cannot be converted to the required return type + * Produces a method handle which returns its sole argument when invoked. + *

The identity function for {@code void} takes no arguments and returns no values. + * @param type the type of the sole parameter and return value of the desired method handle + * @return a unary method handle which accepts and returns the given type + * @throws NullPointerException if the argument is null + * @throws IllegalArgumentException if the given type is {@code void.class} */ public static - MethodHandle constant(MethodType type, Object value) { - MethodHandle target = constant(type.returnType(), value); - int len = type.parameterCount(); - if (len == 0) - return target.asType(type); - target = target.asType(type.dropParameterTypes(0, len)); - return dropArguments(target, 0, type.parameterList().subList(0, len)); - } - - /** - * Produce a method handle which returns its sole argument when invoked. - *

The identity function for {@code void} takes no arguments and returns no values. - * @param type the type of the sole parameter and return value of the desired method handle - * @return a unary method handle which accepts and returns the given type - */ - public static MethodHandle identity(Class type) { + if (type == void.class) + throw newIllegalArgumentException("void type"); return ValueConversions.identity(type); } - /** - * Produce a method handle of the requested type which returns its argument when invoked. - * If the return type differs from the first argument type, the argument will be - * converted as if by {@link #explicitCastArguments explicitCastArguments}. - * If there are additional arguments beyond the first, they are discarded. - *

The identity function for {@code void} discards all its arguments. - * @param type the type of the desired method handle - * @return a method handle of the given type, which always returns its first argument - * @throws WrongMethodTypeException if the first argument cannot be converted to the required return type - */ - public static - MethodHandle identity(MethodType type) { - MethodHandle target = identity(type.returnType()); - int len = type.parameterCount(); - if (len == 1) - return explicitCastArguments(target, type); - if (len == 0) - throw new IllegalArgumentException("not enough arguments"); - target = explicitCastArguments(target, type.dropParameterTypes(1, len)); - return dropArguments(target, 1, type.parameterList().subList(1, len)); - } - /** - * Produce a method handle which calls the original method handle {@code target}, + * Produces a method handle which calls the original method handle {@code target}, * after inserting the given argument(s) at the given position. * The formal parameters to {@code target} which will be supplied by those * arguments are called bound parameters, because the new method @@ -1404,6 +1679,7 @@ assert((int)twice.invokeExact(21) == 42); * @param values the series of arguments to insert * @return a method handle which inserts an additional argument, * before calling the original method handle + * @throws NullPointerException if the {@code target} argument or the {@code values} array is null * @see MethodHandle#bindTo */ public static @@ -1438,7 +1714,7 @@ assert((int)twice.invokeExact(21) == 42); } /** - * Produce a method handle which calls the original method handle, + * Produces a method handle which calls the original method handle, * after dropping the given argument(s) at the given position. * The type of the new method handle will insert the given argument * type(s), at that position, into the original handle's type. @@ -1470,6 +1746,9 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); * @param pos position of first argument to drop (zero for the leftmost) * @return a method handle which drops arguments of the given types, * before calling the original method handle + * @throws NullPointerException if the {@code target} argument is null, + * or if the {@code valueTypes} list or any of its elements is null + * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class} */ public static MethodHandle dropArguments(MethodHandle target, int pos, List> valueTypes) { @@ -1487,7 +1766,7 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); } /** - * Produce a method handle which calls the original method handle, + * Produces a method handle which calls the original method handle, * after dropping the given argument(s) at the given position. * The type of the new method handle will insert the given argument * type(s), at that position, into the original handle's type. @@ -1500,6 +1779,9 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); * @param pos position of first argument to drop (zero for the leftmost) * @return a method handle which drops arguments of the given types, * before calling the original method handle + * @throws NullPointerException if the {@code target} argument is null, + * or if the {@code valueTypes} array or any of its elements is null + * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class} */ public static MethodHandle dropArguments(MethodHandle target, int pos, Class... valueTypes) { @@ -1514,7 +1796,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); *

* The pre-processing is performed by one or more method handles, * specified in the elements of the {@code filters} array. - * (If there are no elements in the array, the original target is returned.) + * Null arguments in the array are ignored, and the corresponding arguments left unchanged. + * (If there are no non-null elements in the array, the original target is returned.) * Each filter is applied to the corresponding argument of the adapter. *

* If a filter {@code F} applies to the {@code N}th argument of @@ -1544,12 +1827,16 @@ assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY MethodHandle f2 = filterArguments(cat, 0, upcase, upcase); assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY *

+ * * @param target the method handle to invoke after arguments are filtered * @param pos the position of the first argument to filter * @param filters method handles to call initially on filtered arguments * @return method handle which incorporates the specified argument filtering logic - * @throws IllegalArgumentException if an element of {@code filters} is null or - * does not match a corresponding argument type of {@code target} as described above + * @throws NullPointerException if the {@code target} argument is null + * or if the {@code filters} array is null + * @throws IllegalArgumentException if a non-null element of {@code filters} + * does not match a corresponding argument type of {@code target} as described above, + * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()} */ public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { @@ -1557,17 +1844,18 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY MethodHandle adapter = target; MethodType adapterType = targetType; int maxPos = targetType.parameterCount(); - int curPos = pos; + if (pos + filters.length > maxPos) + throw newIllegalArgumentException("too many filters"); + int curPos = pos-1; // pre-incremented for (MethodHandle filter : filters) { - if (curPos >= maxPos) - throw newIllegalArgumentException("too many 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(IMPL_TOKEN, adapter, curPos, filter); - curPos += 1; } MethodType midType = adapter.type(); if (midType != adapterType) @@ -1602,7 +1890,8 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * @param target the method handle to invoke before filtering the return value * @param filter method handle to call on the return value * @return method handle which incorporates the specified return value filtering logic - * @throws IllegalArgumentException if {@code filter} is null or + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if {@code filter} * does not match the return type of {@code target} as described above */ public static @@ -1612,9 +1901,11 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 if (filterType.parameterCount() != 1 || filterType.parameterType(0) != targetType.returnType()) throw newIllegalArgumentException("target and filter types do not match"); + // result = fold( lambda(retval, arg...) { filter(retval) }, + // lambda( arg...) { target(arg...) } ) // FIXME: Too many nodes here. - MethodHandle returner = dropArguments(filter, 0, targetType.parameterList()); - return foldArguments(returner, exactInvoker(target.type()).bindTo(target)); + MethodHandle returner = dropArguments(filter, 1, targetType.parameterList()); + return foldArguments(returner, target); } /** @@ -1637,7 +1928,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * (Note that {@link #dropArguments(MethodHandle,int,List) dropArguments} can be used to remove any arguments * that either the {@code combiner} or {@code target} does not wish to receive. * If some of the incoming arguments are destined only for the combiner, - * consider using {@link MethodHandle#asCollector} instead, since those + * consider using {@link MethodHandle#asCollector asCollector} instead, since those * arguments will not need to be live on the stack on entry to the * target.) *

@@ -1656,6 +1947,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * @param target the method handle to invoke after arguments are combined * @param combiner method handle to call initially on the incoming arguments * @return method handle which incorporates the specified argument folding logic + * @throws NullPointerException if either argument is null * @throws IllegalArgumentException if the first argument type of * {@code target} is not the same as {@code combiner}'s return type, * or if the following argument types of {@code target} @@ -1704,6 +1996,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * @param target method handle to call if test passes * @param fallback method handle to call if test fails * @return method handle which incorporates the specified if/then/else logic + * @throws NullPointerException if any argument is null * @throws IllegalArgumentException if {@code test} does not return boolean, * or if all three method types do not match (with the return * type of {@code test} changed to match that of {@code target}). @@ -1772,6 +2065,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * @param exType the type of exception which the handler will catch * @param handler method handle to call if a matching exception is thrown * @return method handle which incorporates the specified try/catch logic + * @throws NullPointerException if any argument is null * @throws IllegalArgumentException if {@code handler} does not accept * the given exception type, or if the method handle types do * not match in their return types and their @@ -1802,12 +2096,14 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 } /** - * Produce a method handle which will throw exceptions of the given {@code exType}. + * Produces a method handle which will throw exceptions of the given {@code exType}. * The method handle will accept a single argument of {@code exType}, * and immediately throw it as an exception. * The method type will nominally specify a return of {@code returnType}. * The return type may be anything convenient: It doesn't matter to the * method handle's behavior, since it will never return normally. + * @return method handle which can throw the given exceptions + * @throws NullPointerException if either argument is null */ public static MethodHandle throwException(Class returnType, Class exType) { @@ -1815,18 +2111,43 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 } /** - * PROVISIONAL API, WORK IN PROGRESS: - * Produce a wrapper instance of the given "SAM" interface which redirects + * Produces an instance of the given "SAM" interface which redirects * its calls to the given method handle. + *

* A SAM interface is an interface which declares a single abstract method. - * The type must be public. (No additional access checks are performed.) + * When determining the unique abstract method of a SAM interface, + * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) + * are disregarded. For example, {@link java.util.Comparator} is a SAM interface, + * even though it re-declares the {@code Object.equals} method. + * Also, if the SAM interface has a supertype, + * the SAM interface may override an inherited method. + * Any such overrides are respected, and the method handle will be accessible + * by either the inherited method or the SAM method. + * In particular, a {@linkplain java.lang.reflect.Method#isBridge bridge method} + * may be created if the methods have different return types. + *

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

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

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

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

* The method handle may throw an undeclared exception, * which means any checked exception (or other checked throwable) @@ -1835,54 +2156,46 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} * and thrown in that wrapped form. *

- * The wrapper instance is guaranteed to be of a non-public - * implementation class C in a package containing no classes - * or methods except system-defined classes and methods. - * The implementation class C will have no public supertypes - * or public methods beyond the following: - *

    - *
  • the SAM type itself and any methods in the SAM type - *
  • the supertypes of the SAM type (if any) and their methods - *
  • {@link Object} and its methods - *
  • {@link java.dyn.AsInstanceObject AsInstanceObject} and its methods
  • - *
- *

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

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

- * This method is not guaranteed to return a distinct - * wrapper object for each separate call. If the implementation is able - * to prove that a wrapper of the required SAM type - * has already been created for a given - * method handle, or for another method handle with the - * same behavior, the implementation may return that wrapper in place of - * a new wrapper. + * Like {@link java.lang.Integer#valueOf Integer.valueOf}, + * {@code asInstance} is a factory method whose results are defined + * by their behavior. + * It is not guaranteed to return a new instance for every call. *

- * This method is designed to apply to common use cases - * where a single method handle must interoperate with - * a type (class or interface) that implements a function-like - * API. Additional variations, such as SAM classes with - * private constructors, or interfaces with multiple but related - * entry points, must be covered by hand-written or automatically - * generated adapter classes. In those cases, consider implementing - * {@link java.dyn.MethodHandles.AsInstanceObject AsInstanceObject} - * in the adapters, so that generic code can extract the underlying - * method handle without knowing where the SAM adapter came from. + * Future versions of this API may accept additional types, + * such as abstract classes with single abstract methods. + * Future versions of this API may also equip wrapper instances + * with one or more additional public "marker" interfaces. + * * @param target the method handle to invoke from the wrapper * @param samType the desired type of the wrapper, a SAM type * @return a correctly-typed wrapper for the given {@code target} - * @throws IllegalArgumentException if the {@code target} throws - * an undeclared exception + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if the {@code samType} is not a + * valid argument to this method + * @throws WrongMethodTypeException if the {@code target} cannot + * be converted to the type required by the SAM type */ - // ISSUE: Should we delegate equals/hashCode to the targets? - // Not useful unless there is a stable equals/hashCode behavior - // for MethodHandle, but there isn't. + // Other notes to implementors: + //

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

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

+ // This method is designed to apply to common use cases + // where a single method handle must interoperate with + // an interface that implements a function-like + // API. Additional variations, such as SAM classes with + // private constructors, or interfaces with multiple but related + // entry points, must be covered by hand-written or automatically + // generated adapter classes. + // public static T asInstance(final MethodHandle target, final Class samType) { // POC implementation only; violates the above contract several ways @@ -1890,22 +2203,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 if (sam == null) throw new IllegalArgumentException("not a SAM type: "+samType.getName()); MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes()); - if (!samMT.equals(target.type())) - throw new IllegalArgumentException("wrong method type: "+target+" should match "+sam); + MethodHandle checkTarget = target.asType(samMT); // make throw WMT + checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class)); + final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, samMT.parameterCount()); return samType.cast(Proxy.newProxyInstance( samType.getClassLoader(), - new Class[]{ samType, AsInstanceObject.class }, + new Class[]{ samType, WrapperInstance.class }, new InvocationHandler() { private Object getArg(String name) { - if ((Object)name == "getAsInstanceTarget") return target; - if ((Object)name == "getAsInstanceType") return samType; + if ((Object)name == "getWrapperInstanceTarget") return target; + if ((Object)name == "getWrapperInstanceType") return samType; throw new AssertionError(); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getDeclaringClass() == AsInstanceObject.class) + if (method.getDeclaringClass() == WrapperInstance.class) return getArg(method.getName()); if (method.equals(sam)) - return target.invokeVarargs(args); + return vaTarget.invokeExact(args); if (isObjectMethod(method)) return callObjectMethod(this, method, args); throw new InternalError(); @@ -1914,21 +2228,49 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 } /** - * PROVISIONAL API, WORK IN PROGRESS: - * Interface implemented by every object which is produced by {@link #asInstance asInstance}. - * The methods of this interface allow a caller to recover the parameters - * to {@code asInstance}. - * This allows applications to repeatedly convert between method handles - * and SAM objects, without the risk of creating unbounded delegation chains. + * Determine if the given object was produced by a call to {@link #asInstance asInstance}. + * @param x any reference + * @return true if the reference is not null and points to an object produced by {@code asInstance} */ - public interface AsInstanceObject { - /** Produce or recover a target method handle which is behaviorally - * equivalent to the SAM method of this object. - */ - public MethodHandle getAsInstanceTarget(); - /** Recover the SAM type for which this object was created. - */ - public Class getAsInstanceType(); + public static + boolean isWrapperInstance(Object x) { + return x instanceof WrapperInstance; + } + + private static WrapperInstance asWrapperInstance(Object x) { + try { + if (x != null) + return (WrapperInstance) x; + } catch (ClassCastException ex) { + } + throw new IllegalArgumentException("not a wrapper instance"); + } + + /** + * Produces or recovers a target method handle which is behaviorally + * equivalent to the SAM method of this wrapper instance. + * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}. + * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. + * @param x any reference + * @return a method handle implementing the SAM method + * @throws IllegalArgumentException if the reference x is not to a wrapper instance + */ + public static + MethodHandle wrapperInstanceTarget(Object x) { + return asWrapperInstance(x).getWrapperInstanceTarget(); + } + + /** + * Recover the SAM type for which this wrapper instance was created. + * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}. + * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. + * @param x any reference + * @return the SAM type for which the wrapper was created + * @throws IllegalArgumentException if the reference x is not to a wrapper instance + */ + public static + Class wrapperInstanceType(Object x) { + return asWrapperInstance(x).getWrapperInstanceType(); } private static @@ -1991,7 +2333,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 } /*non-public*/ - static MethodHandle withTypeHandler(MethodHandle target, MethodHandle typeHandler) { - return MethodHandleImpl.withTypeHandler(IMPL_TOKEN, target, typeHandler); + static MethodHandle asVarargsCollector(MethodHandle target, Class arrayType) { + return MethodHandleImpl.asVarargsCollector(IMPL_TOKEN, target, arrayType); } } diff --git a/src/share/classes/java/dyn/MethodType.java b/src/share/classes/java/dyn/MethodType.java index 32f3a196b537dec067511a2491e1ea50641b6c8d..a7baf7c634e7892233bb45e2d8ec9a7205872f90 100644 --- a/src/share/classes/java/dyn/MethodType.java +++ b/src/share/classes/java/dyn/MethodType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.List; import sun.dyn.Access; import sun.dyn.Invokers; +import sun.dyn.MethodHandleImpl; import sun.dyn.MethodTypeImpl; import sun.dyn.util.BytecodeDescriptor; import static sun.dyn.MemberName.newIllegalArgumentException; @@ -41,8 +42,8 @@ import static sun.dyn.MemberName.newIllegalArgumentException; * and expected by a method handle caller. Method types must be properly * 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} - * and {@link MethodHandle#invokeGeneric}, and during execution + * during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact} + * and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution * of {@code invokedynamic} instructions. *

* The structure is a return type accompanied by any number of parameter types. @@ -70,8 +71,9 @@ import static sun.dyn.MemberName.newIllegalArgumentException; * with the instructions in a class file's constant pool. *

* Like classes and strings, method types can also be represented directly - * in a class file's constant pool as constants. The may be loaded by an {@code ldc} - * instruction which refers to a suitable {@code CONSTANT_MethodType} constant pool entry. + * in a class file's constant pool as constants. + * A method type may be loaded by an {@code ldc} instruction which refers + * to a suitable {@code CONSTANT_MethodType} constant pool entry. * The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string. * For more details, see the package summary. *

@@ -82,9 +84,14 @@ import static sun.dyn.MemberName.newIllegalArgumentException; * @author John Rose, JSR 292 EG */ public final -class MethodType { +class MethodType implements java.io.Serializable { + private static final long serialVersionUID = 292L; // {rtype, {ptype...}} + + // The rtype and ptypes fields define the structural identity of the method type: private final Class rtype; private final Class[] ptypes; + + // The remaining fields are caches of various sorts: private MethodTypeForm form; // erased form, plus cached data about primitives private MethodType wrapAlt; // alternative wrapped/unwrapped version private Invokers invokers; // cache of handy higher-order adapters @@ -117,6 +124,9 @@ class MethodType { }); } + /** + * Check the given parameters for validity and store them into the final fields. + */ private MethodType(Class rtype, Class[] ptypes) { checkRtype(rtype); checkPtypes(ptypes); @@ -124,15 +134,32 @@ class MethodType { this.ptypes = ptypes; } - private void checkRtype(Class rtype) { + private static void checkRtype(Class rtype) { rtype.equals(rtype); // null check } - private void checkPtypes(Class[] ptypes) { + private static int checkPtype(Class ptype) { + ptype.getClass(); //NPE + if (ptype == void.class) + throw newIllegalArgumentException("parameter type cannot be void"); + if (ptype == double.class || ptype == long.class) return 1; + return 0; + } + /** Return number of extra slots (count of long/double args). */ + private static int checkPtypes(Class[] ptypes) { + int slots = 0; for (Class ptype : ptypes) { - ptype.equals(ptype); // null check - if (ptype == void.class) - throw newIllegalArgumentException("parameter type cannot be void"); + slots += checkPtype(ptype); } + checkSlotCount(ptypes.length + slots); + return slots; + } + private static void checkSlotCount(int count) { + if ((count & 0xFF) != count) + throw newIllegalArgumentException("bad parameter count "+count); + } + private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) { + if (num instanceof Integer) num = "bad index: "+num; + return new IndexOutOfBoundsException(num.toString()); } static final HashMap internTable @@ -140,27 +167,39 @@ class MethodType { static final Class[] NO_PTYPES = {}; - /** Find or create an instance of the given method type. + /** + * Find or create an instance of the given method type. * @param rtype the return type * @param ptypes the parameter types - * @return a method type with the given parts - * @throws NullPointerException if rtype or any ptype is null - * @throws IllegalArgumentException if any of the ptypes is void + * @return a method type with the given components + * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null + * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class} */ public static MethodType methodType(Class rtype, Class[] ptypes) { return makeImpl(rtype, ptypes, false); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */ + /** + * Finds or creates a method type with the given components. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * @return a method type with the given components + * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null + * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class} + */ public static - MethodType methodType(Class rtype, List> ptypes) { + MethodType methodType(Class rtype, List> ptypes) { boolean notrust = false; // random List impl. could return evil ptypes array return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * The leading parameter type is prepended to the remaining array. + /** + * Finds or creates a method type with the given components. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * The leading parameter type is prepended to the remaining array. + * @return a method type with the given components + * @throws NullPointerException if {@code rtype} or {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is null + * @throws IllegalArgumentException if {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is {@code void.class} */ public static MethodType methodType(Class rtype, Class ptype0, Class... ptypes) { @@ -170,25 +209,37 @@ class MethodType { return makeImpl(rtype, ptypes1, true); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * The resulting method has no parameter types. + /** + * Finds or creates a method type with the given components. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * The resulting method has no parameter types. + * @return a method type with the given return value + * @throws NullPointerException if {@code rtype} is null */ public static MethodType methodType(Class rtype) { return makeImpl(rtype, NO_PTYPES, true); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * The resulting method has the single given parameter type. + /** + * Finds or creates a method type with the given components. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * The resulting method has the single given parameter type. + * @return a method type with the given return value and parameter type + * @throws NullPointerException if {@code rtype} or {@code ptype0} is null + * @throws IllegalArgumentException if {@code ptype0} is {@code void.class} */ public static MethodType methodType(Class rtype, Class ptype0) { return makeImpl(rtype, new Class[]{ ptype0 }, true); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * The resulting method has the same parameter types as {@code ptypes}, - * and the specified return type. + /** + * Finds or creates a method type with the given components. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * The resulting method has the same parameter types as {@code ptypes}, + * and the specified return type. + * @throws NullPointerException if {@code rtype} or {@code ptypes} is null */ public static MethodType methodType(Class rtype, MethodType ptypes) { @@ -237,17 +288,20 @@ class MethodType { private static final MethodType[] objectOnlyTypes = new MethodType[20]; /** - * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + * 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 * @see #genericMethodType(int) */ public static MethodType genericMethodType(int objectArgCount, boolean varargs) { MethodType mt; + checkSlotCount(objectArgCount); int ivarargs = (!varargs ? 0 : 1); int ootIndex = objectArgCount*2 + ivarargs; if (ootIndex < objectOnlyTypes.length) { @@ -265,9 +319,12 @@ class MethodType { } /** + * Finds or creates a method type whose components are all {@code Object}. + * 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 + * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 * @see #genericMethodType(int, boolean) */ public static @@ -275,27 +332,41 @@ class MethodType { return genericMethodType(objectArgCount, false); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + /** + * Finds or creates a method type with a single different parameter type. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * @param num the index (zero-based) of the parameter type to change * @param nptype a new parameter type to replace the old one with * @return the same type, except with the selected parameter changed + * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()} + * @throws IllegalArgumentException if {@code nptype} is {@code void.class} + * @throws NullPointerException if {@code nptype} is null */ public MethodType changeParameterType(int num, Class nptype) { if (parameterType(num) == nptype) return this; + checkPtype(nptype); Class[] nptypes = ptypes.clone(); nptypes[num] = nptype; return makeImpl(rtype, nptypes, true); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + /** + * Finds or creates a method type with additional parameter types. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * @param num the position (zero-based) of the inserted parameter type(s) - * @param ptypesToInsert zero or more a new parameter types to insert into the parameter list + * @param ptypesToInsert zero or more new parameter types to insert into the parameter list * @return the same type, except with the selected parameter(s) inserted + * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()} + * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class} + * or if the resulting method type would have more than 255 parameter slots + * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null */ public MethodType insertParameterTypes(int num, Class... ptypesToInsert) { int len = ptypes.length; if (num < 0 || num > len) - throw newIllegalArgumentException("num="+num); //SPECME + throw newIndexOutOfBoundsException(num); + int ins = checkPtypes(ptypesToInsert); + checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins); int ilen = ptypesToInsert.length; if (ilen == 0) return this; Class[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen); @@ -304,40 +375,61 @@ class MethodType { return makeImpl(rtype, nptypes, true); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list + /** + * Finds or creates a method type with additional parameter types. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list * @return the same type, except with the selected parameter(s) appended + * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class} + * or if the resulting method type would have more than 255 parameter slots + * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null */ public MethodType appendParameterTypes(Class... ptypesToInsert) { return insertParameterTypes(parameterCount(), ptypesToInsert); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list - * @return the same type, except with the selected parameter(s) appended - */ - public MethodType appendParameterTypes(List> ptypesToInsert) { - return insertParameterTypes(parameterCount(), ptypesToInsert); - } - - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + /** + * Finds or creates a method type with additional parameter types. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * @param num the position (zero-based) of the inserted parameter type(s) - * @param ptypesToInsert zero or more a new parameter types to insert into the parameter list + * @param ptypesToInsert zero or more new parameter types to insert into the parameter list * @return the same type, except with the selected parameter(s) inserted + * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()} + * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class} + * or if the resulting method type would have more than 255 parameter slots + * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null */ public MethodType insertParameterTypes(int num, List> ptypesToInsert) { return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES)); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + /** + * Finds or creates a method type with additional parameter types. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. + * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list + * @return the same type, except with the selected parameter(s) appended + * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class} + * or if the resulting method type would have more than 255 parameter slots + * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null + */ + public MethodType appendParameterTypes(List> ptypesToInsert) { + return insertParameterTypes(parameterCount(), ptypesToInsert); + } + + /** + * Finds or creates a method type with some parameter types omitted. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * @param start the index (zero-based) of the first parameter type to remove * @param end the index (greater than {@code start}) of the first parameter type after not to remove * @return the same type, except with the selected parameter(s) removed + * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()} + * or if {@code end} is negative or greater than {@code parameterCount()} + * or if {@code start} is greater than {@code end} */ public MethodType dropParameterTypes(int start, int end) { int len = ptypes.length; if (!(0 <= start && start <= end && end <= len)) - throw newIllegalArgumentException("start="+start+" end="+end); //SPECME + throw newIndexOutOfBoundsException("start="+start+" end="+end); if (start == end) return this; Class[] nptypes; if (start == 0) { @@ -361,17 +453,20 @@ class MethodType { return makeImpl(rtype, nptypes, true); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + /** + * Finds or creates a method type with a different return type. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * @param nrtype a return parameter type to replace the old one with * @return the same type, except with the return type change + * @throws NullPointerException if {@code nrtype} is null */ public MethodType changeReturnType(Class nrtype) { if (returnType() == nrtype) return this; return makeImpl(nrtype, ptypes, true); } - /** Convenience method. - * Report if this type contains a primitive argument or return value. + /** + * Reports if this type contains a primitive argument or return value. * The return type {@code void} counts as a primitive. * @return true if any of the types are primitives */ @@ -379,8 +474,8 @@ class MethodType { return form.hasPrimitives(); } - /** Convenience method. - * Report if this type contains a wrapper argument or return value. + /** + * Reports if this type contains a wrapper argument or return value. * Wrappers are types which box primitive values, such as {@link Integer}. * The reference type {@code java.lang.Void} counts as a wrapper. * @return true if any of the types are wrappers @@ -389,8 +484,9 @@ class MethodType { return unwrap() != this; } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * Erase all reference types to {@code Object}. + /** + * Erases all reference types to {@code Object}. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All primitive types (including {@code void}) will remain unchanged. * @return a version of the original type with all reference types replaced */ @@ -398,8 +494,9 @@ class MethodType { return form.erasedType(); } - /** Convenience method for {@link #genericMethodType(int)}. - * Convert all types, both reference and primitive, to {@code Object}. + /** + * Converts all types, both reference and primitive, to {@code Object}. + * Convenience method for {@link #genericMethodType(int) genericMethodType}. * The expression {@code type.wrap().erase()} produces the same value * as {@code type.generic()}. * @return a version of the original type with all types replaced @@ -408,8 +505,9 @@ class MethodType { return genericMethodType(parameterCount()); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * Convert all primitive types to their corresponding wrapper types. + /** + * Converts all primitive types to their corresponding wrapper types. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All reference types (including wrapper types) will remain unchanged. * A {@code void} return type is changed to the type {@code java.lang.Void}. * The expression {@code type.wrap().erase()} produces the same value @@ -420,8 +518,9 @@ class MethodType { return hasPrimitives() ? wrapWithPrims(this) : this; } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + /** * Convert all wrapper types to their corresponding primitive types. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * All primitive types (including {@code void}) will remain unchanged. * A return type of {@code java.lang.Void} is changed to {@code void}. * @return a version of the original type with all wrapper types replaced @@ -456,23 +555,33 @@ class MethodType { return uwt; } - /** @param num the index (zero-based) of the desired parameter type - * @return the selected parameter type + /** + * Returns the parameter type at the specified index, within this method type. + * @param num the index (zero-based) of the desired parameter type + * @return the selected parameter type + * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()} */ public Class parameterType(int num) { return ptypes[num]; } - /** @return the number of parameter types */ + /** + * Returns the number of parameter types in this method type. + * @return the number of parameter types + */ public int parameterCount() { return ptypes.length; } - /** @return the return type */ + /** + * Returns the return type of this method type. + * @return the return type + */ public Class returnType() { return rtype; } /** - * Convenience method to present the arguments as a list. + * Presents the parameter types as a list (a convenience method). + * The list will be immutable. * @return the parameter types (as an immutable list) */ public List> parameterList() { @@ -480,7 +589,7 @@ class MethodType { } /** - * Convenience method to present the arguments as an array. + * Presents the parameter types as an array (a convenience method). * Changes to the array will not result in changes to the type. * @return the parameter types (as a fresh copy if necessary) */ @@ -524,14 +633,14 @@ class MethodType { } /** + * Returns a string representation of the method type, + * of the form {@code "(PT0,PT1...)RT"}. * The string representation of a method type is a * parenthesis enclosed, comma separated list of type names, * followed immediately by the return type. *

* Each type is represented by its * {@link java.lang.Class#getSimpleName simple name}. - * If a type name name is array, it the base type followed - * by [], rather than the Class.getName of the array type. */ @Override public String toString() { @@ -548,21 +657,22 @@ class MethodType { /// Queries which have to do with the bytecode architecture - /** The number of JVM stack slots required to invoke a method + /** Reports the number of JVM stack slots required to invoke a method * of this type. Note that (for historic reasons) the JVM requires * a second stack slot to pass long and double arguments. - * So this method returns {@link #parameterCount()} plus the + * So this method returns {@link #parameterCount() parameterCount} plus the * number of long and double parameters (if any). *

* This method is included for the benfit of applications that must * generate bytecodes that process method handles and invokedynamic. * @return the number of JVM stack slots for this type's parameters + * @deprecated Will be removed for PFD. */ public int parameterSlotCount() { return form.parameterSlotCount(); } - /** Number of JVM stack slots which carry all parameters including and after + /** Reports the number of JVM stack slots which carry all parameters including and after * the given position, which must be in the range of 0 to * {@code parameterCount} inclusive. Successive parameters are * more shallowly stacked, and parameters are indexed in the bytecodes @@ -583,6 +693,8 @@ class MethodType { * @param num an index (zero-based, inclusive) within the parameter types * @return the index of the (shallowest) JVM stack slot transmitting the * given parameter + * @throws IllegalArgumentException if {@code num} is negative or greater than {@code parameterCount()} + * @deprecated Will be removed for PFD. */ public int parameterSlotDepth(int num) { if (num < 0 || num > ptypes.length) @@ -590,7 +702,7 @@ class MethodType { return form.parameterToArgSlot(num-1); } - /** The number of JVM stack slots required to receive a return value + /** Reports the number of JVM stack slots required to receive a return value * from a method of this type. * If the {@link #returnType() return type} is void, it will be zero, * else if the return type is long or double, it will be two, else one. @@ -598,13 +710,15 @@ class MethodType { * This method is included for the benfit of applications that must * generate bytecodes that process method handles and invokedynamic. * @return the number of JVM stack slots (0, 1, or 2) for this type's return value + * @deprecated Will be removed for PFD. */ public int returnSlotCount() { return form.returnSlotCount(); } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. - * Find or create an instance of the given method type. + /** + * Find or create an instance of a method type, given the spelling of its bytecode descriptor. + * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * Any class or interface name embedded in the descriptor string * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)} * on the given loader (or if it is null, on the system class loader). @@ -614,10 +728,10 @@ class MethodType { * not all reachable from a common class loader. *

* This method is included for the benfit of applications that must - * generate bytecodes that process method handles and invokedynamic. - * @param descriptor a bytecode-level signature string "(T...)T" + * generate bytecodes that process method handles and {@code invokedynamic}. + * @param descriptor a bytecode-level type descriptor string "(T...)T" * @param loader the class loader in which to look up the types - * @return a method type matching the bytecode-level signature + * @return a method type matching the bytecode-level type descriptor * @throws IllegalArgumentException if the string is not well-formed * @throws TypeNotPresentException if a named type cannot be found */ @@ -631,19 +745,121 @@ class MethodType { } /** - * Create a bytecode descriptor representation of the method type. + * Produces a bytecode descriptor representation of the method type. *

- * Note that this is not a strict inverse of {@link #fromMethodDescriptorString}. + * Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}. * Two distinct classes which share a common name but have different class loaders * will appear identical when viewed within descriptor strings. *

* This method is included for the benfit of applications that must - * generate bytecodes that process method handles and invokedynamic. - * {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader)}, + * generate bytecodes that process method handles and {@code invokedynamic}. + * {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString}, * because the latter requires a suitable class loader argument. - * @return the bytecode signature representation + * @return the bytecode type descriptor representation */ public String toMethodDescriptorString() { return BytecodeDescriptor.unparse(this); } + + /// Serialization. + + /** + * There are no serializable fields for {@code MethodType}. + */ + private static final java.io.ObjectStreamField[] serialPersistentFields = { }; + + /** + * Save the {@code MethodType} instance to a stream. + * + * @serialData + * For portability, the serialized format does not refer to named fields. + * Instead, the return type and parameter type arrays are written directly + * from the {@code writeObject} method, using two calls to {@code s.writeObject} + * as follows: + *

+s.writeObject(this.returnType());
+s.writeObject(this.parameterArray());
+     * 
+ *

+ * The deserialized field values are checked as if they were + * provided to the factory method {@link #methodType(Class,Class[]) methodType}. + * For example, null values, or {@code void} parameter types, + * will lead to exceptions during deserialization. + * @param the stream to write the object to + */ + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + s.defaultWriteObject(); // requires serialPersistentFields to be an empty array + s.writeObject(returnType()); + s.writeObject(parameterArray()); + } + + /** + * Reconstitute the {@code MethodType} instance from a stream (that is, + * deserialize it). + * This instance is a scratch object with bogus final fields. + * It provides the parameters to the factory method called by + * {@link #readResolve readResolve}. + * After that call it is discarded. + * @param the stream to read the object from + * @see #MethodType() + * @see #readResolve + * @see #writeObject + */ + private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); // requires serialPersistentFields to be an empty array + + Class returnType = (Class) s.readObject(); + Class[] parameterArray = (Class[]) s.readObject(); + + // Probably this object will never escape, but let's check + // the field values now, just to be sure. + checkRtype(returnType); + checkPtypes(parameterArray); + + parameterArray = parameterArray.clone(); // make sure it is unshared + MethodType_init(returnType, parameterArray); + } + + /** + * For serialization only. + * Sets the final fields to null, pending {@code Unsafe.putObject}. + */ + private MethodType() { + this.rtype = null; + this.ptypes = null; + } + private void MethodType_init(Class rtype, Class[] ptypes) { + // In order to communicate these values to readResolve, we must + // store them into the implementation-specific final fields. + checkRtype(rtype); + checkPtypes(ptypes); + unsafe.putObject(this, rtypeOffset, rtype); + unsafe.putObject(this, ptypesOffset, ptypes); + } + + // Support for resetting final fields while deserializing + private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + private static final long rtypeOffset, ptypesOffset; + static { + try { + rtypeOffset = unsafe.objectFieldOffset + (MethodType.class.getDeclaredField("rtype")); + ptypesOffset = unsafe.objectFieldOffset + (MethodType.class.getDeclaredField("ptypes")); + } catch (Exception ex) { + throw new Error(ex); + } + } + + /** + * Resolves and initializes a {@code MethodType} object + * after serialization. + * @return the fully initialized {@code MethodType} object + */ + private Object readResolve() { + // Do not use a trusted path for deserialization: + //return makeImpl(rtype, ptypes, true); + // Verify all operands, and make sure ptypes is unshared: + return methodType(rtype, ptypes); + } } diff --git a/src/share/classes/java/dyn/MutableCallSite.java b/src/share/classes/java/dyn/MutableCallSite.java index b33c41c1a5ad6bd059e2ccdb2ff8a4b7b303da55..95df7a6a6d1b5428dcba527a5426e008bf09ee9c 100644 --- a/src/share/classes/java/dyn/MutableCallSite.java +++ b/src/share/classes/java/dyn/MutableCallSite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -73,7 +73,7 @@ assertEquals("Wilma, dear?", (String) worker2.invokeExact()); * (This is a normal consequence of the Java Memory Model as applied * to object fields.) *

- * The {@link #sync sync} operation provides a way to force threads + * The {@link #syncAll syncAll} operation provides a way to force threads * to accept a new target value, even if there is no other synchronization. *

* For target values which will be frequently updated, consider using @@ -82,13 +82,17 @@ assertEquals("Wilma, dear?", (String) worker2.invokeExact()); */ public class MutableCallSite extends CallSite { /** - * Make a blank call site object with the given method type. - * An initial target method is supplied which will throw - * an {@link IllegalStateException} if called. + * Creates a blank call site object with the given method type. + * The initial target is set to a method handle of the given type + * which will throw an {@link IllegalStateException} if called. + *

+ * The type of the call site is permanently set to the given type. *

* Before this {@code CallSite} object is returned from a bootstrap method, + * or invoked in some other manner, * it is usually provided with a more useful target method, * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}. + * @param type the method type that this call site will have * @throws NullPointerException if the proposed type is null */ public MutableCallSite(MethodType type) { @@ -96,8 +100,9 @@ public class MutableCallSite extends CallSite { } /** - * Make a blank call site object, possibly equipped with an initial target method handle. - * @param target the method handle which will be the initial target of the call site + * Creates a call site object with an initial target method handle. + * The type of the call site is permanently set to the initial target's type. + * @param target the method handle that will be the initial target of the call site * @throws NullPointerException if the proposed target is null */ public MutableCallSite(MethodHandle target) { @@ -105,7 +110,59 @@ public class MutableCallSite extends CallSite { } /** - * Perform a synchronization operation on each call site in the given array, + * Returns the target method of the call site, which behaves + * like a normal field of the {@code MutableCallSite}. + *

+ * The interactions of {@code getTarget} with memory are the same + * as of a read from an ordinary variable, such as an array element or a + * non-volatile, non-final field. + *

+ * In particular, the current thread may choose to reuse the result + * of a previous read of the target from memory, and may fail to see + * a recent update to the target by another thread. + * + * @return the linkage state of this call site, a method handle which can change over time + * @see #setTarget + */ + @Override public final MethodHandle getTarget() { + return target; + } + + /** + * Updates the target method of this call site, as a normal variable. + * The type of the new target must agree with the type of the old target. + *

+ * The interactions with memory are the same + * as of a write to an ordinary variable, such as an array element or a + * non-volatile, non-final field. + *

+ * In particular, unrelated threads may fail to see the updated target + * until they perform a read from memory. + * Stronger guarantees can be created by putting appropriate operations + * into the bootstrap method and/or the target methods used + * at any given call site. + * + * @param newTarget the new target + * @throws NullPointerException if the proposed new target is null + * @throws WrongMethodTypeException if the proposed new target + * has a method type that differs from the previous target + * @see #getTarget + */ + @Override public void setTarget(MethodHandle newTarget) { + checkTargetChange(this.target, newTarget); + setTargetNormal(newTarget); + } + + /** + * {@inheritDoc} + */ + @Override + public final MethodHandle dynamicInvoker() { + return makeDynamicInvoker(); + } + + /** + * Performs a synchronization operation on each call site in the given array, * forcing all other threads to throw away any cached values previously * loaded from the target of any of the call sites. *

@@ -115,19 +172,29 @@ public class MutableCallSite extends CallSite { *

* The overall effect is to force all future readers of each call site's target * to accept the most recently stored value. - * ("Most recently" is reckoned relative to the {@code sync} itself.) - * Conversely, the {@code sync} call may block until all readers have + * ("Most recently" is reckoned relative to the {@code syncAll} itself.) + * Conversely, the {@code syncAll} call may block until all readers have * (somehow) decached all previous versions of each call site's target. *

- * To avoid race conditions, calls to {@code setTarget} and {@code sync} + * To avoid race conditions, calls to {@code setTarget} and {@code syncAll} * should generally be performed under some sort of mutual exclusion. * Note that reader threads may observe an updated target as early * as the {@code setTarget} call that install the value - * (and before the {@code sync} that confirms the value). + * (and before the {@code syncAll} that confirms the value). * On the other hand, reader threads may observe previous versions of - * the target until the {@code sync} call returns + * the target until the {@code syncAll} call returns * (and after the {@code setTarget} that attempts to convey the updated version). *

+ * This operation is likely to be expensive and should be used sparingly. + * If possible, it should be buffered for batch processing on sets of call sites. + *

+ * If {@code sites} contains a null element, + * a {@code NullPointerException} will be raised. + * In this case, some non-null elements in the array may be + * processed before the method returns abnormally. + * Which elements these are (if any) is implementation-dependent. + * + *

Java Memory Model details

* In terms of the Java Memory Model, this operation performs a synchronization * action which is comparable in effect to the writing of a volatile variable * by the current thread, and an eventual volatile read by every other thread @@ -171,18 +238,17 @@ public class MutableCallSite extends CallSite { * thereby ensuring communication of the new target value. *

* As long as the constraints of the Java Memory Model are obeyed, - * implementations may delay the completion of a {@code sync} + * implementations may delay the completion of a {@code syncAll} * operation while other threads ({@code T} above) continue to * use previous values of {@code S}'s target. * However, implementations are (as always) encouraged to avoid * livelock, and to eventually require all threads to take account * of the updated target. - *

- * This operation is likely to be expensive and should be used sparingly. - * If possible, it should be buffered for batch processing on sets of call sites. + * *

- * (This is a static method on a set of call sites, not a - * virtual method on a single call site, for performance reasons. + * Discussion: + * For performance reasons, {@code syncAll} is not a virtual method + * on a single call site, but rather applies to a set of call sites. * Some implementations may incur a large fixed overhead cost * for processing one or more synchronization operations, * but a small incremental cost for each additional call site. @@ -191,15 +257,25 @@ public class MutableCallSite extends CallSite { * in order to make them notice the updated target value. * However, it may be observed that a single call to synchronize * several sites has the same formal effect as many calls, - * each on just one of the sites.) - *

+ * each on just one of the sites. + * + *

+ * Implementation Note: * Simple implementations of {@code MutableCallSite} may use * a volatile variable for the target of a mutable call site. - * In such an implementation, the {@code sync} method can be a no-op, + * In such an implementation, the {@code syncAll} method can be a no-op, * and yet it will conform to the JMM behavior documented above. + * + * @param sites an array of call sites to be synchronized + * @throws NullPointerException if the {@code sites} array reference is null + * or the array contains a null */ - public static void sync(MutableCallSite[] sites) { + public static void syncAll(MutableCallSite[] sites) { + if (sites.length == 0) return; STORE_BARRIER.lazySet(0); + for (int i = 0; i < sites.length; i++) { + sites[i].getClass(); // trigger NPE on first null + } // FIXME: NYI } private static final AtomicInteger STORE_BARRIER = new AtomicInteger(); diff --git a/src/share/classes/java/dyn/NoAccessException.java b/src/share/classes/java/dyn/NoAccessException.java deleted file mode 100644 index 89c1a52af2a6ae061f8ab7acb1fb96d1490bc6a5..0000000000000000000000000000000000000000 --- a/src/share/classes/java/dyn/NoAccessException.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.dyn; - -/** - * Thrown to indicate that a caller has attempted to create a method handle - * which accesses a field, method, or class to which the caller does not have access. - * This unchecked exception is analogous to {@link IllegalAccessException}, - * which is a checked exception thrown when reflective invocation fails - * because of an access check. With method handles, this same access - * checking is performed by the {@link MethodHandles.Lookup lookup object} - * on behalf of the method handle creator, - * at the time of creation. - * @author John Rose, JSR 292 EG - * @since 1.7 - */ -public class NoAccessException extends ReflectiveOperationException { - private static final long serialVersionUID = 292L; - - /** - * Constructs a {@code NoAccessException} with no detail message. - */ - public NoAccessException() { - super(); - } - - /** - * Constructs a {@code NoAccessException} with the specified - * detail message. - * - * @param s the detail message - */ - public NoAccessException(String s) { - super(s); - } - - /** - * Constructs a {@code NoAccessException} with the specified cause. - * - * @param cause the underlying cause of the exception - */ - public NoAccessException(Throwable cause) { - super(cause); - } - - /** - * Constructs a {@code NoAccessException} with the specified - * detail message and cause. - * - * @param s the detail message - * @param cause the underlying cause of the exception - */ - public NoAccessException(String s, Throwable cause) { - super(s, cause); - } -} diff --git a/src/share/classes/java/dyn/SwitchPoint.java b/src/share/classes/java/dyn/SwitchPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..642e194b863614ef7041dd2a9e3a95775c1fb137 --- /dev/null +++ b/src/share/classes/java/dyn/SwitchPoint.java @@ -0,0 +1,195 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.dyn; + +/** + *

+ * A {@code SwitchPoint} is an object which can publish state transitions to other threads. + * A switch point is initially in the valid state, but may at any time be + * changed to the invalid state. Invalidation cannot be reversed. + * A switch point can combine a guarded pair of method handles into a + * guarded delegator. + * The guarded delegator is a method handle which delegates to one of the old method handles. + * The state of the switch point determines which of the two gets the delegation. + *

+ * A single switch point may be used to control any number of method handles. + * (Indirectly, therefore, it can control any number of call sites.) + * This is done by using the single switch point as a factory for combining + * any number of guarded method handle pairs into guarded delegators. + *

+ * When a guarded delegator is created from a guarded pair, the pair + * is wrapped in a new method handle {@code M}, + * which is permanently associated with the switch point that created it. + * Each pair consists of a target {@code T} and a fallback {@code F}. + * While the switch point is valid, invocations to {@code M} are delegated to {@code T}. + * After it is invalidated, invocations are delegated to {@code F}. + *

+ * Invalidation is global and immediate, as if the switch point contained a + * volatile boolean variable consulted on every call to {@code M}. + * The invalidation is also permanent, which means the switch point + * can change state only once. + * The switch point will always delegate to {@code F} after being invalidated. + * At that point {@code guardWithTest} may ignore {@code T} and return {@code F}. + *

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

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

+ * Discussion: + * Switch points are useful without subclassing. They may also be subclassed. + * This may be useful in order to associate application-specific invalidation logic + * with the switch point. + *

+ * Implementation Note: + * A switch point behaves as if implemented on top of {@link MutableCallSite}, + * approximately as follows: + *

+public class SwitchPoint {
+  private static final MethodHandle
+    K_true  = MethodHandles.constant(boolean.class, true),
+    K_false = MethodHandles.constant(boolean.class, false);
+  private final MutableCallSite mcs;
+  private final MethodHandle mcsInvoker;
+  public SwitchPoint() {
+    this.mcs = new MutableCallSite(K_true);
+    this.mcsInvoker = mcs.dynamicInvoker();
+  }
+  public MethodHandle guardWithTest(
+                MethodHandle target, MethodHandle fallback) {
+    // Note:  mcsInvoker is of type ()boolean.
+    // Target and fallback may take any arguments, but must have the same type.
+    return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
+  }
+  public static void invalidateAll(SwitchPoint[] spts) {
+    List<MutableCallSite> mcss = new ArrayList<>();
+    for (SwitchPoint spt : spts)  mcss.add(spt.mcs);
+    for (MutableCallSite mcs : mcss)  mcs.setTarget(K_false);
+    MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
+  }
+}
+ * 
+ * @author Remi Forax, JSR 292 EG + */ +public class SwitchPoint { + private static final MethodHandle + K_true = MethodHandles.constant(boolean.class, true), + K_false = MethodHandles.constant(boolean.class, false); + + private final MutableCallSite mcs; + private final MethodHandle mcsInvoker; + + /** + * Creates a new switch point. + */ + public SwitchPoint() { + this.mcs = new MutableCallSite(K_true); + this.mcsInvoker = mcs.dynamicInvoker(); + } + + /** + * Returns a method handle which always delegates either to the target or the fallback. + * The method handle will delegate to the target exactly as long as the switch point is valid. + * After that, it will permanently delegate to the fallback. + *

+ * The target and fallback must be of exactly the same method type, + * and the resulting combined method handle will also be of this type. + * + * @param target the method handle selected by the switch point as long as it is valid + * @param fallback the method handle selected by the switch point after it is invalidated + * @return a combined method handle which always calls either the target or fallback + * @throws NullPointerException if either argument is null + * @see MethodHandles#guardWithTest + */ + public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) { + if (mcs.getTarget() == K_false) + return fallback; // already invalid + return MethodHandles.guardWithTest(mcsInvoker, target, fallback); + } + + /** + * Sets all of the given switch points into the invalid state. + * After this call executes, no thread will observe any of the + * switch points to be in a valid state. + *

+ * This operation is likely to be expensive and should be used sparingly. + * If possible, it should be buffered for batch processing on sets of switch points. + *

+ * If {@code switchPoints} contains a null element, + * a {@code NullPointerException} will be raised. + * In this case, some non-null elements in the array may be + * processed before the method returns abnormally. + * Which elements these are (if any) is implementation-dependent. + * + *

+ * Discussion: + * For performance reasons, {@code invalidateAll} is not a virtual method + * on a single switch point, but rather applies to a set of switch points. + * Some implementations may incur a large fixed overhead cost + * for processing one or more invalidation operations, + * but a small incremental cost for each additional invalidation. + * In any case, this operation is likely to be costly, since + * other threads may have to be somehow interrupted + * in order to make them notice the updated switch point state. + * However, it may be observed that a single call to invalidate + * several switch points has the same formal effect as many calls, + * each on just one of the switch points. + * + *

+ * Implementation Note: + * Simple implementations of {@code SwitchPoint} may use + * a private {@link MutableCallSite} to publish the state of a switch point. + * In such an implementation, the {@code invalidateAll} method can + * simply change the call site's target, and issue one call to + * {@linkplain MutableCallSite#syncAll synchronize} all the + * private call sites. + * + * @param switchPoints an array of call sites to be synchronized + * @throws NullPointerException if the {@code switchPoints} array reference is null + * or the array contains a null + */ + public static void invalidateAll(SwitchPoint[] switchPoints) { + if (switchPoints.length == 0) return; + MutableCallSite[] sites = new MutableCallSite[switchPoints.length]; + for (int i = 0; i < switchPoints.length; i++) { + SwitchPoint spt = switchPoints[i]; + if (spt == null) break; // MSC.syncAll will trigger a NPE + sites[i] = spt.mcs; + spt.mcs.setTarget(K_false); + } + MutableCallSite.syncAll(sites); + } +} diff --git a/src/share/classes/java/dyn/Switcher.java b/src/share/classes/java/dyn/Switcher.java deleted file mode 100644 index b0838764b4417a68bda5dbde1d2418feceb600ce..0000000000000000000000000000000000000000 --- a/src/share/classes/java/dyn/Switcher.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.dyn; - -/** - *

- * A {@code Switcher} is an object which can publish state transitions to other threads. - * A switcher is initially in the valid state, but may at any time be - * changed to the invalid state. Invalidation cannot be reversed. - *

- * A single switcher may be used to create any number of guarded method handle pairs. - * Each guarded pair is wrapped in a new method handle {@code M}, - * which is permanently associated with the switcher that created it. - * Each pair consists of a target {@code T} and a fallback {@code F}. - * While the switcher is valid, invocations to {@code M} are delegated to {@code T}. - * After it is invalidated, invocations are delegated to {@code F}. - *

- * Invalidation is global and immediate, as if the switcher contained a - * volatile boolean variable consulted on every call to {@code M}. - * The invalidation is also permanent, which means the switcher - * can change state only once. - *

- * Here is an example of a switcher in action: - *

-MethodType MT_str2 = MethodType.methodType(String.class, String.class);
-MethodHandle MH_strcat = MethodHandles.lookup()
-    .findVirtual(String.class, "concat", MT_str2);
-Switcher switcher = new Switcher();
-// the following steps may be repeated to re-use the same switcher:
-MethodHandle worker1 = strcat;
-MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
-MethodHandle worker = switcher.guardWithTest(worker1, worker2);
-assertEquals("method", (String) worker.invokeExact("met", "hod"));
-switcher.invalidate();
-assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
- * 
- *

- * Implementation Note: - * A switcher behaves as if implemented on top of {@link MutableCallSite}, - * approximately as follows: - *

-public class Switcher {
-  private static final MethodHandle
-    K_true  = MethodHandles.constant(boolean.class, true),
-    K_false = MethodHandles.constant(boolean.class, false);
-  private final MutableCallSite mcs;
-  private final MethodHandle mcsInvoker;
-  public Switcher() {
-    this.mcs = new MutableCallSite(K_true);
-    this.mcsInvoker = mcs.dynamicInvoker();
-  }
-  public MethodHandle guardWithTest(
-                MethodHandle target, MethodHandle fallback) {
-    // Note:  mcsInvoker is of type boolean().
-    // Target and fallback may take any arguments, but must have the same type.
-    return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
-  }
-  public static void invalidateAll(Switcher[] switchers) {
-    List mcss = new ArrayList<>();
-    for (Switcher s : switchers)  mcss.add(s.mcs);
-    for (MutableCallSite mcs : mcss)  mcs.setTarget(K_false);
-    MutableCallSite.sync(mcss.toArray(new MutableCallSite[0]));
-  }
-}
- * 
- * @author Remi Forax, JSR 292 EG - */ -public class Switcher { - private static final MethodHandle - K_true = MethodHandles.constant(boolean.class, true), - K_false = MethodHandles.constant(boolean.class, false); - - private final MutableCallSite mcs; - private final MethodHandle mcsInvoker; - - /** Create a switcher. */ - public Switcher() { - this.mcs = new MutableCallSite(K_true); - this.mcsInvoker = mcs.dynamicInvoker(); - } - - /** - * Return a method handle which always delegates either to the target or the fallback. - * The method handle will delegate to the target exactly as long as the switcher is valid. - * After that, it will permanently delegate to the fallback. - *

- * The target and fallback must be of exactly the same method type, - * and the resulting combined method handle will also be of this type. - * @see MethodHandles#guardWithTest - */ - public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) { - if (mcs.getTarget() == K_false) - return fallback; // already invalid - return MethodHandles.guardWithTest(mcsInvoker, target, fallback); - } - - /** Set all of the given switchers into the invalid state. */ - public static void invalidateAll(Switcher[] switchers) { - MutableCallSite[] sites = new MutableCallSite[switchers.length]; - int fillp = 0; - for (Switcher switcher : switchers) { - sites[fillp++] = switcher.mcs; - switcher.mcs.setTarget(K_false); - } - MutableCallSite.sync(sites); - } -} diff --git a/src/share/classes/java/dyn/VolatileCallSite.java b/src/share/classes/java/dyn/VolatileCallSite.java index 8c603b9191593991b7b0c9fd911d8f8a8d43056e..616813ce2f5a55f40b629262758b1212aaecb3ae 100644 --- a/src/share/classes/java/dyn/VolatileCallSite.java +++ b/src/share/classes/java/dyn/VolatileCallSite.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 @@ -34,7 +34,7 @@ import java.util.List; * There may be a performance penalty for such tight coupling between threads. *

* Unlike {@code MutableCallSite}, there is no - * {@linkplain MutableCallSite#sync sync operation} on volatile + * {@linkplain MutableCallSite#syncAll syncAll operation} on volatile * call sites, since every write to a volatile variable is implicitly * synchronized with reader threads. *

@@ -44,36 +44,68 @@ import java.util.List; * @author John Rose, JSR 292 EG */ public class VolatileCallSite extends CallSite { - /** Create a call site with a volatile target. - * The initial target is set to a method handle - * of the given type which will throw {@code IllegalStateException}. + /** + * Creates a call site with a volatile binding to its target. + * The initial target is set to a method handle + * of the given type which will throw an {@code IllegalStateException} if called. + * @param type the method type that this call site will have * @throws NullPointerException if the proposed type is null */ public VolatileCallSite(MethodType type) { super(type); } - /** Create a call site with a volatile target. - * The target is set to the given value. + /** + * Creates a call site with a volatile binding to its target. + * The target is set to the given value. + * @param target the method handle that will be the initial target of the call site * @throws NullPointerException if the proposed target is null */ public VolatileCallSite(MethodHandle target) { super(target); } - /** Internal override to nominally final getTarget. */ - @Override - MethodHandle getTarget0() { + /** + * Returns the target method of the call site, which behaves + * like a {@code volatile} field of the {@code VolatileCallSite}. + *

+ * The interactions of {@code getTarget} with memory are the same + * as of a read from a {@code volatile} field. + *

+ * In particular, the current thread is required to issue a fresh + * read of the target from memory, and must not fail to see + * a recent update to the target by another thread. + * + * @return the linkage state of this call site, a method handle which can change over time + * @see #setTarget + */ + @Override public final MethodHandle getTarget() { return getTargetVolatile(); } /** - * Set the target method of this call site, as a volatile variable. - * Has the same effect as {@link CallSite#setTarget CallSite.setTarget}, with the additional - * effects associated with volatiles, in the Java Memory Model. + * Updates the target method of this call site, as a volatile variable. + * The type of the new target must agree with the type of the old target. + *

+ * The interactions with memory are the same as of a write to a volatile field. + * In particular, any threads is guaranteed to see the updated target + * the next time it calls {@code getTarget}. + * @param newTarget the new target + * @throws NullPointerException if the proposed new target is null + * @throws WrongMethodTypeException if the proposed new target + * has a method type that differs from the previous target + * @see #getTarget */ @Override public void setTarget(MethodHandle newTarget) { checkTargetChange(getTargetVolatile(), newTarget); setTargetVolatile(newTarget); } + + /** + * {@inheritDoc} + */ + @Override + public final MethodHandle dynamicInvoker() { + return makeDynamicInvoker(); + } } diff --git a/src/share/classes/java/dyn/WrongMethodTypeException.java b/src/share/classes/java/dyn/WrongMethodTypeException.java index 4c4c99817ffec82b37b265639887a57023d9672b..2455432e858be4721fe36e8938b441cc2d0af770 100644 --- a/src/share/classes/java/dyn/WrongMethodTypeException.java +++ b/src/share/classes/java/dyn/WrongMethodTypeException.java @@ -29,7 +29,7 @@ package java.dyn; * Thrown to indicate that code has attempted to call a method handle * via the wrong method type. As with the bytecode representation of * normal Java method calls, method handle calls are strongly typed - * to a specific signature associated with a call site. + * to a specific type descriptor associated with a call site. *

* This exception may also be thrown when two method handles are * composed, and the system detects that their types cannot be diff --git a/src/share/classes/java/dyn/package-info.java b/src/share/classes/java/dyn/package-info.java index d2fa14ffc9ffe13d0da94c625968d3e579dfa1f0..d01c644f312959b7351f3b1ab09ae0c9235a9dd2 100644 --- a/src/share/classes/java/dyn/package-info.java +++ b/src/share/classes/java/dyn/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -24,21 +24,20 @@ */ /** - * This package contains dynamic language support provided directly by + * The {@code java.lang.invoke} package contains dynamic language support provided directly by * the Java core class libraries and virtual machine. + * + *

+ * Historic Note: In some early versions of Java SE 7, + * the name of this package is {@code java.dyn}. *

* Certain types in this package have special relations to dynamic * language support in the virtual machine: *

    - *
  • In source code, a call to - * {@link java.dyn.MethodHandle#invokeExact MethodHandle.invokeExact} or - * {@link java.dyn.MethodHandle#invokeGeneric MethodHandle.invokeGeneric} - * will compile and link, regardless of the requested type signature. - * As usual, the Java compiler emits an {@code invokevirtual} - * instruction with the given signature against the named method. - * The JVM links any such call (regardless of signature) to a dynamically - * typed method handle invocation. In the case of {@code invokeGeneric}, - * argument and return value conversions are applied. + *
  • The class {@link java.dyn.MethodHandle MethodHandle} contains + * signature polymorphic methods + * which can be linked regardless of their type descriptor. + * Normally, method linkage requires exact matching of type descriptors. *
  • * *
  • The JVM bytecode format supports immediate constants of @@ -58,12 +57,11 @@ * The final two bytes are reserved for future use and required to be zero. * The constant pool reference of an {@code invokedynamic} instruction is to a entry * with tag {@code CONSTANT_InvokeDynamic} (decimal 18). See below for its format. - * (The tag value 17 is also temporarily allowed. See below.) * The entry specifies the following information: *
      *
    • a bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant)
    • *
    • the dynamic invocation name (a UTF8 string)
    • - *
    • the argument and return types of the call (encoded as a signature in a UTF8 string)
    • + *
    • the argument and return types of the call (encoded as a type descriptor in a UTF8 string)
    • *
    • optionally, a sequence of additional static arguments to the bootstrap method ({@code ldc}-type constants)
    • *
    *

    @@ -78,9 +76,9 @@ * as described below. * *

    - * (Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType} + * Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType} * instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the - * bootstrap method was specified dynamically, in a per-class basis, during class initialization.) + * bootstrap method was specified dynamically, in a per-class basis, during class initialization. * *

    constant pool entries for {@code invokedynamic} instructions

    * If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18), @@ -90,33 +88,21 @@ * bootstrap method table, which is stored in the {@code BootstrapMethods} * attribute as described below. * The second pair of bytes must be an index to a {@code CONSTANT_NameAndType}. - * This table is not part of the constant pool. Instead, it is stored - * in a class attribute named {@code BootstrapMethods}, described below. *

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

    - * Some older JVMs may allow an older constant pool entry tag of decimal 17. - * The format and behavior of a constant pool entry with this tag is identical to - * an entry with a tag of decimal 18, except that the first index refers directly - * to a {@code CONSTANT_MethodHandle} to use as the bootstrap method. - * This format does not require the bootstrap method table. - * - *

    - * (Note: The Proposed Final Draft of this specification is likely to support - * only the tag 18, not the tag 17.) * *

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

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

    * The JVM will ensure that on first * execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType MethodType} - * will be created which represents the signature. + * will be created which represents the type descriptor. * Any classes mentioned in the {@code MethodType} will be loaded if necessary, * but not initialized. * Access checking and error reporting is performed exactly as it is for @@ -141,38 +127,75 @@ * type is created. Any classes mentioned in this reification will be loaded if necessary, * but not initialized, and access checking and error reporting performed as usual. *

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

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

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

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

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

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

    * A constant may refer to a method or constructor with the {@code varargs} - * bit (hexadecimal {@code 80}) set in its modifier bitmask. - * The method handle constant produced for such a method behaves the same + * bit (hexadecimal {@code 0x0080}) set in its modifier bitmask. + * The method handle constant produced for such a method behaves as if + * it were created by {@link java.dyn.MethodHandle#asVarargsCollector asVarargsCollector}. + * In other words, the constant method handle will exhibit variable arity, + * when invoked via {@code invokeGeneric}. + * On the other hand, its behavior with respect to {@code invokeExact} will be the same * as if the {@code varargs} bit were not set. - * The argument-collecting behavior of {@code varargs} can be emulated by - * adapting the method handle constant with - * {@link java.dyn.MethodHandle#asCollector asCollector}. - * There is no provision for doing this automatically. *

    * Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types * resolve class names, they do not force class initialization. @@ -186,14 +209,14 @@ * by the execution of {@code invokedynamic} and {@code ldc} instructions. * (Roughly speaking, this means that every use of a constant pool entry * must lead to the same outcome. - * If the resoultion succeeds, the same object reference is produced + * If the resolution succeeds, the same object reference is produced * by every subsequent execution of the same instruction. * If the resolution of the constant causes an error to occur, * the same error will be re-thrown on every subsequent attempt * to use this particular constant.) *

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

    * Each {@code invokedynamic} instruction statically specifies its own * bootstrap method as a constant pool reference. - * The constant pool reference also specifies the call site's name and type signature, + * The constant pool reference also specifies the call site's name and type descriptor, * just like {@code invokevirtual} and the other invoke instructions. *

    * Linking starts with resolving the constant pool entry for the * bootstrap method, and resolving a {@link java.dyn.MethodType MethodType} object for - * the type signature of the dynamic call site. + * the type descriptor of the dynamic call site. * This resolution process may trigger class loading. * It may therefore throw an error if a class fails to load. * This error becomes the abnormal termination of the dynamic * call site execution. * Linkage does not trigger class initialization. *

    - * Next, the bootstrap method call is started, with four or five values being stacked: + * Next, the bootstrap method call is started, with at least four values being stacked: *

      *
    • a {@code MethodHandle}, the resolved bootstrap method itself
    • *
    • a {@code MethodHandles.Lookup}, a lookup object on the caller class in which dynamic call site occurs
    • *
    • a {@code String}, the method name mentioned in the call site
    • - *
    • a {@code MethodType}, the resolved type signature of the call
    • - *
    • optionally, a single object representing one or more additional static arguments
    • + *
    • a {@code MethodType}, the resolved type descriptor of the call
    • + *
    • optionally, one or more additional static arguments
    • *
    * The method handle is then applied to the other values as if by * {@link java.dyn.MethodHandle#invokeGeneric invokeGeneric}. * The returned result must be a {@link java.dyn.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 signature and passed to + * derived from the dynamic call site's type descriptor and passed to * the bootstrap method. * The call site then becomes permanently linked to the dynamic call site. *

    @@ -299,11 +322,11 @@ * chosen target object. * *

    - * (Historic Note: Unlike some previous versions of this specification, + * Historic Note: Unlike some previous versions of this specification, * these rules do not enable the JVM to duplicate dynamic call sites, * or to issue “causeless” bootstrap method calls. * Every dynamic call site transitions at most once from unlinked to linked, - * just before its first invocation.) + * just before its first invocation. * *

    the {@code BootstrapMethods} attribute

    * Each {@code CONSTANT_InvokeDynamic} entry contains an index which references @@ -349,7 +372,6 @@ * Static arguments are specified constant pool indexes stored in the {@code BootstrapMethods} attribute. * Before the bootstrap method is invoked, each index is used to compute an {@code Object} * reference to the indexed value in the constant pool. - * If the value is a primitive type, it is converted to a reference by boxing conversion. * The valid constant pool entries are listed in this table: * * @@ -369,21 +391,43 @@ * the instruction's bootstrap method will be invoked on three arguments, * conveying the instruction's caller class, name, and method type. * If the {@code invokedynamic} instruction specifies one or more static arguments, - * a fourth argument will be passed to the bootstrap argument, - * either an {@code Object} reference to the sole extra argument (if there is one) - * or an {@code Object} array of references to all the arguments (if there are two or more), - * as if the bootstrap method is a variable-arity method. + * 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} + * or {@code invokeWithArguments}. (There is no way to tell the difference.) + *

    + * The normal argument conversion rules for {@code invokeGeneric} 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. + * (This is not a special rule, but rather a useful consequence of the interaction + * between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods, + * and the {@code java.dyn.MethodHandle#asVarargsCollector asVarargsCollector} transformation.) + *

    + * Given these rules, here are examples of legal bootstrap method declarations, + * given various numbers {@code N} of extra arguments. + * The first rows (marked {@code *}) will work for any number of extra arguments. * *

    * + * + * + * * + * * * + * + * *
    Nsample bootstrap method
    *CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
    *CallSite bootstrap(Object... args)
    *CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)
    0CallSite bootstrap(Lookup caller, String name, MethodType type)
    0CallSite bootstrap(Lookup caller, Object... nameAndType)
    1CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
    2CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
    2CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)
    2CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)
    * + * The last example assumes that the extra arguments are of type + * {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively. + * The second-to-last example assumes that all extra arguments are of type + * {@code CONSTANT_String}. + * The other examples work with all types of extra arguments. *

    - * The argument and return types listed here are used by the {@code invokeGeneric} - * call to the bootstrap method. * As noted above, the actual method type of the bootstrap method can vary. * For example, the fourth argument could be {@code MethodHandle}, * if that is the type of the corresponding constant in @@ -391,14 +435,8 @@ * 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} * 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.) - *

    - * If the fourth argument is an array, the array element type must be {@code Object}, - * since object arrays (as produced by the JVM at this point) cannot be converted - * to other array types. - *

    - * If an array is provided, it will appear to be freshly allocated. - * That is, the same array will not appear to two bootstrap method calls. + * (If a string constant were passed instead, by badly generated code, that cast would then fail, + * resulting in an {@code InvokeDynamicBootstrapError}.) *

    * Extra bootstrap method arguments are intended to allow language implementors * to safely and compactly encode metadata. @@ -406,24 +444,6 @@ * since each call site could be given its own unique bootstrap method. * Such a practice is likely to produce large class files and constant pools. * - *

    - * PROVISIONAL API, WORK IN PROGRESS: - * (Usage Note: There is no mechanism for specifying five or more positional arguments to the bootstrap method. - * If there are two or more arguments, the Java code of the bootstrap method is required to extract them from - * a varargs-style object array. - * This design uses varargs because it anticipates some use cases where bootstrap arguments - * contribute components of variable-length structures, such as virtual function tables - * or interpreter token streams. - * Such parameters would be awkward or impossible to manage if represented - * as normal positional method arguments, - * since there would need to be one Java method per length. - * On balance, leaving out the varargs feature would cause more trouble to users than keeping it. - * Also, this design allows bootstrap methods to be called in a limited JVM stack depth. - * At both the user and JVM level, the difference between varargs and non-varargs - * calling sequences can easily be bridged via the - * {@link java.dyn.MethodHandle#asSpreader asSpreader} - * and {@link java.dyn.MethodHandle#asSpreader asCollector} methods.) - * *

    Structure Summary

    *
    // summary of constant and attribute structures
     struct CONSTANT_MethodHandle_info {
    @@ -435,11 +455,6 @@ struct CONSTANT_MethodType_info {
       u1 tag = 16;
       u2 descriptor_index;    // index to CONSTANT_Utf8, as in NameAndType
     }
    -struct CONSTANT_InvokeDynamic_17_info {
    -  u1 tag = 17;
    -  u2 bootstrap_method_index;   // index to CONSTANT_MethodHandle
    -  u2 name_and_type_index;      // same as for CONSTANT_Methodref, etc.
    -}
     struct CONSTANT_InvokeDynamic_info {
       u1 tag = 18;
       u2 bootstrap_method_attr_index;  // index into BootstrapMethods_attr
    @@ -456,9 +471,6 @@ struct BootstrapMethods_attr {
      } bootstrap_methods[bootstrap_method_count];
     }
      * 
    - *

    - * Note: The Proposed Final Draft of JSR 292 may remove the constant tag 17, - * for the sake of simplicity. * * @author John Rose, JSR 292 EG */ diff --git a/src/share/classes/java/io/Console.java b/src/share/classes/java/io/Console.java index 783dafd28e4ff5f463cd730076afd79803a28173..9bfbaf00497cd129e420bb9f933e6635ff4e4a3e 100644 --- a/src/share/classes/java/io/Console.java +++ b/src/share/classes/java/io/Console.java @@ -308,17 +308,29 @@ public final class Console implements Flushable char[] passwd = null; synchronized (writeLock) { synchronized(readLock) { - if (fmt.length() != 0) - pw.format(fmt, args); try { echoOff = echo(false); - passwd = readline(true); } catch (IOException x) { throw new IOError(x); + } + IOError ioe = null; + try { + if (fmt.length() != 0) + pw.format(fmt, args); + passwd = readline(true); + } catch (IOException x) { + ioe = new IOError(x); } finally { try { echoOff = echo(true); - } catch (IOException xx) {} + } catch (IOException x) { + if (ioe == null) + ioe = new IOError(x); + else + ioe.addSuppressed(x); + } + if (ioe != null) + throw ioe; } pw.println(); } diff --git a/src/share/classes/java/lang/System.java b/src/share/classes/java/lang/System.java index 59e15ee1d043b0acbafbc782464677907ff84b46..3eafd1b3cbb9d7fe6d29e0d04d5dea8328471627 100644 --- a/src/share/classes/java/lang/System.java +++ b/src/share/classes/java/lang/System.java @@ -1102,6 +1102,18 @@ public final class System { * Initialize the system class. Called after thread initialization. */ private static void initializeSystemClass() { + + // VM might invoke JNU_NewStringPlatform() to set those encoding + // sensitive properties (user.home, user.name, boot.class.path, etc.) + // during "props" initialization, in which it may need access, via + // System.getProperty(), to the related system encoding property that + // have been initialized (put into "props") at early stage of the + // initialization. So make sure the "props" is available at the + // very beginning of the initialization and all system properties to + // be put into it directly. + props = new Properties(); + initProperties(props); // initialized by the VM + // There are certain system configurations that may be controlled by // VM options such as the maximum amount of direct memory and // Integer cache size used to support the object identity semantics @@ -1112,7 +1124,12 @@ public final class System { // // See java.lang.Integer.IntegerCache and the // sun.misc.VM.saveAndRemoveProperties method for example. - props = initSystemProperties(); + // + // Save a private copy of the system properties object that + // can only be accessed by the internal implementation. Remove + // certain system properties that are not intended for public access. + sun.misc.VM.saveAndRemoveProperties(props); + lineSeparator = props.getProperty("line.separator"); sun.misc.Version.init(); @@ -1123,7 +1140,6 @@ public final class System { setIn0(new BufferedInputStream(fdIn)); setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true)); setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true)); - // Load the zip library now in order to keep java.util.zip.ZipFile // from trying to use itself to load this library later. loadLibrary("zip"); @@ -1151,18 +1167,6 @@ public final class System { setJavaLangAccess(); } - private static Properties initSystemProperties() { - Properties props = new Properties(); - initProperties(props); // initialized by the VM - - // Save a private copy of the system properties object that - // can only be accessed by the internal implementation. Remove - // certain system properties that are not intended for public access. - sun.misc.VM.saveAndRemoveProperties(props); - - return props; - } - private static void setJavaLangAccess() { // Allow privileged classes outside of java.lang sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){ diff --git a/src/share/classes/java/lang/Thread.java b/src/share/classes/java/lang/Thread.java index d35e082b84468ab308dea20254944cd15e0ce5f5..d64d55b740410bd72621d4cba5ab8139628b7524 100644 --- a/src/share/classes/java/lang/Thread.java +++ b/src/share/classes/java/lang/Thread.java @@ -254,12 +254,6 @@ class Thread implements Runnable { */ public final static int MAX_PRIORITY = 10; - /* If stop was called before start */ - private boolean stopBeforeStart; - - /* Remembered Throwable from stop before start */ - private Throwable throwableFromStop; - /** * Returns a reference to the currently executing thread object. * @@ -706,10 +700,6 @@ class Thread implements Runnable { it will be passed up the call stack */ } } - - if (stopBeforeStart) { - stop0(throwableFromStop); - } } private native void start0(); @@ -820,12 +810,7 @@ class Thread implements Runnable { */ @Deprecated public final void stop() { - // If the thread is already dead, return. - // A zero status value corresponds to "NEW". - if ((threadStatus != 0) && !isAlive()) { - return; - } - stop1(new ThreadDeath()); + stop(new ThreadDeath()); } /** @@ -879,36 +864,25 @@ class Thread implements Runnable { */ @Deprecated public final synchronized void stop(Throwable obj) { - stop1(obj); - } + if (obj == null) + throw new NullPointerException(); - /** - * Common impl for stop() and stop(Throwable). - */ - private final synchronized void stop1(Throwable th) { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if ((this != Thread.currentThread()) || - (!(th instanceof ThreadDeath))) { + (!(obj instanceof ThreadDeath))) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } - // A zero status value corresponds to "NEW" + // A zero status value corresponds to "NEW", it can't change to + // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise - stop0(th); - } else { - - // Must do the null arg check that the VM would do with stop0 - if (th == null) { - throw new NullPointerException(); - } - - // Remember this stop attempt for if/when start is used - stopBeforeStart = true; - throwableFromStop = th; } + + // The VM can handle all thread states + stop0(obj); } /** diff --git a/src/share/classes/java/util/Hashtable.java b/src/share/classes/java/util/Hashtable.java index 63af5581cf7e95f1b76018eddef63d773049a9c5..b354bbc50f166bacd5ceafc7fc01c7fc2ad4cbc2 100644 --- a/src/share/classes/java/util/Hashtable.java +++ b/src/share/classes/java/util/Hashtable.java @@ -845,24 +845,36 @@ public class Hashtable * for each key-value mapping represented by the Hashtable * The key-value mappings are emitted in no particular order. */ - private synchronized void writeObject(java.io.ObjectOutputStream s) - throws IOException - { - // Write out the length, threshold, loadfactor - s.defaultWriteObject(); - - // Write out length, count of elements and then the key/value objects - s.writeInt(table.length); - s.writeInt(count); - for (int index = table.length-1; index >= 0; index--) { - Entry entry = table[index]; - - while (entry != null) { - s.writeObject(entry.key); - s.writeObject(entry.value); - entry = entry.next; + private void writeObject(java.io.ObjectOutputStream s) + throws IOException { + Entry entryStack = null; + + synchronized (this) { + // Write out the length, threshold, loadfactor + s.defaultWriteObject(); + + // Write out length, count of elements + s.writeInt(table.length); + s.writeInt(count); + + // Stack copies of the entries in the table + for (int index = 0; index < table.length; index++) { + Entry entry = table[index]; + + while (entry != null) { + entryStack = + new Entry<>(0, entry.key, entry.value, entryStack); + entry = entry.next; + } } } + + // Write out the key/value objects from the stacked entries + while (entryStack != null) { + s.writeObject(entryStack.key); + s.writeObject(entryStack.value); + entryStack = entryStack.next; + } } /** diff --git a/src/share/classes/java/util/SimpleTimeZone.java b/src/share/classes/java/util/SimpleTimeZone.java index 8642773329066ba30fb7d2dead5b9e93cb607e39..ef70b960ee00e97ac3ae2f249179764d34648cfc 100644 --- a/src/share/classes/java/util/SimpleTimeZone.java +++ b/src/share/classes/java/util/SimpleTimeZone.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -825,10 +825,7 @@ public class SimpleTimeZone extends TimeZone { * @since 1.2 */ public int getDSTSavings() { - if (useDaylight) { - return dstSavings; - } - return 0; + return useDaylight ? dstSavings : 0; } /** @@ -841,6 +838,20 @@ public class SimpleTimeZone extends TimeZone { return useDaylight; } + /** + * Returns {@code true} if this {@code SimpleTimeZone} observes + * Daylight Saving Time. This method is equivalent to {@link + * #useDaylightTime()}. + * + * @return {@code true} if this {@code SimpleTimeZone} observes + * Daylight Saving Time; {@code false} otherwise. + * @since 1.7 + */ + @Override + public boolean observesDaylightTime() { + return useDaylightTime(); + } + /** * Queries if the given date is in daylight saving time. * @return true if daylight saving time is in effective at the diff --git a/src/share/classes/java/util/TimeZone.java b/src/share/classes/java/util/TimeZone.java index ca62e0ed7dab2f28e27e726e3273871c368dfe61..9799be601fef0f5beb862be0d0051e0210516b7e 100644 --- a/src/share/classes/java/util/TimeZone.java +++ b/src/share/classes/java/util/TimeZone.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -455,17 +455,28 @@ abstract public class TimeZone implements Serializable, Cloneable { /** * Returns the amount of time to be added to local standard time * to get local wall clock time. - *

    - * The default implementation always returns 3600000 milliseconds - * (i.e., one hour) if this time zone observes Daylight Saving - * Time. Otherwise, 0 (zero) is returned. - *

    - * If an underlying TimeZone implementation subclass supports - * historical Daylight Saving Time changes, this method returns - * the known latest daylight saving value. + * + *

    The default implementation returns 3600000 milliseconds + * (i.e., one hour) if a call to {@link #useDaylightTime()} + * returns {@code true}. Otherwise, 0 (zero) is returned. + * + *

    If an underlying {@code TimeZone} implementation subclass + * supports historical and future Daylight Saving Time schedule + * changes, this method returns the amount of saving time of the + * last known Daylight Saving Time rule that can be a future + * prediction. + * + *

    If the amount of saving time at any given time stamp is + * required, construct a {@link Calendar} with this {@code + * TimeZone} and the time stamp, and call {@link Calendar#get(int) + * Calendar.get}{@code (}{@link Calendar#DST_OFFSET}{@code )}. * * @return the amount of saving time in milliseconds * @since 1.4 + * @see #inDaylightTime(Date) + * @see #getOffset(long) + * @see #getOffset(int,int,int,int,int,int) + * @see Calendar#ZONE_OFFSET */ public int getDSTSavings() { if (useDaylightTime()) { @@ -475,24 +486,51 @@ abstract public class TimeZone implements Serializable, Cloneable { } /** - * Queries if this time zone uses daylight savings time. - *

    - * If an underlying TimeZone implementation subclass - * supports historical Daylight Saving Time schedule changes, the - * method refers to the latest Daylight Saving Time schedule - * information. + * Queries if this {@code TimeZone} uses Daylight Saving Time. + * + *

    If an underlying {@code TimeZone} implementation subclass + * supports historical and future Daylight Saving Time schedule + * changes, this method refers to the last known Daylight Saving Time + * rule that can be a future prediction and may not be the same as + * the current rule. Consider calling {@link #observesDaylightTime()} + * if the current rule should also be taken into account. * - * @return true if this time zone uses daylight savings time, - * false, otherwise. + * @return {@code true} if this {@code TimeZone} uses Daylight Saving Time, + * {@code false}, otherwise. + * @see #inDaylightTime(Date) + * @see Calendar#DST_OFFSET */ public abstract boolean useDaylightTime(); /** - * Queries if the given date is in daylight savings time in - * this time zone. - * @param date the given Date. - * @return true if the given date is in daylight savings time, - * false, otherwise. + * Returns {@code true} if this {@code TimeZone} is currently in + * Daylight Saving Time, or if a transition from Standard Time to + * Daylight Saving Time occurs at any future time. + * + *

    The default implementation returns {@code true} if + * {@code useDaylightTime()} or {@code inDaylightTime(new Date())} + * returns {@code true}. + * + * @return {@code true} if this {@code TimeZone} is currently in + * Daylight Saving Time, or if a transition from Standard Time to + * Daylight Saving Time occurs at any future time; {@code false} + * otherwise. + * @since 1.7 + * @see #useDaylightTime() + * @see #inDaylightTime(Date) + * @see Calendar#DST_OFFSET + */ + public boolean observesDaylightTime() { + return useDaylightTime() || inDaylightTime(new Date()); + } + + /** + * Queries if the given {@code date} is in Daylight Saving Time in + * this {@code TimeZone}. + * + * @param date the given {@code Date}. + * @return {@code true} if the given {@code date} is in Daylight Saving Time, + * {@code false}, otherwise. */ abstract public boolean inDaylightTime(Date date); diff --git a/src/share/classes/java/util/Vector.java b/src/share/classes/java/util/Vector.java index 1ce0abf86f30824ce8b7ae68a44d9c32fd243b37..11c0c60a48153e756d996eb5e0a3275d2c7af6bc 100644 --- a/src/share/classes/java/util/Vector.java +++ b/src/share/classes/java/util/Vector.java @@ -1050,13 +1050,21 @@ public class Vector /** * Save the state of the {@code Vector} instance to a stream (that - * is, serialize it). This method is present merely for synchronization. - * It just calls the default writeObject method. + * is, serialize it). + * This method performs synchronization to ensure the consistency + * of the serialized data. */ - private synchronized void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException - { - s.defaultWriteObject(); + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + final java.io.ObjectOutputStream.PutField fields = s.putFields(); + final Object[] data; + synchronized (this) { + fields.put("capacityIncrement", capacityIncrement); + fields.put("elementCount", elementCount); + data = elementData.clone(); + } + fields.put("elementData", data); + s.writeFields(); } /** diff --git a/src/share/classes/java/util/jar/JarFile.java b/src/share/classes/java/util/jar/JarFile.java index e42d41fb79992898dd20c6eff9d3b133633f5940..a5795c72763c81e820034b4429548c736028062a 100644 --- a/src/share/classes/java/util/jar/JarFile.java +++ b/src/share/classes/java/util/jar/JarFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -27,11 +27,13 @@ package java.util.jar; import java.io.*; import java.lang.ref.SoftReference; +import java.net.URL; import java.util.*; import java.util.zip.*; import java.security.CodeSigner; import java.security.cert.Certificate; import java.security.AccessController; +import java.security.CodeSource; import sun.security.action.GetPropertyAction; import sun.security.util.ManifestEntryVerifier; import sun.misc.SharedSecrets; @@ -262,7 +264,7 @@ class JarFile extends ZipFile { throw new RuntimeException(e); } if (certs == null && jv != null) { - certs = jv.getCerts(getName()); + certs = jv.getCerts(JarFile.this, this); } return certs == null ? null : certs.clone(); } @@ -273,7 +275,7 @@ class JarFile extends ZipFile { throw new RuntimeException(e); } if (signers == null && jv != null) { - signers = jv.getCodeSigners(getName()); + signers = jv.getCodeSigners(JarFile.this, this); } return signers == null ? null : signers.clone(); } @@ -544,4 +546,191 @@ class JarFile extends ZipFile { } return false; } + + private synchronized void ensureInitialization() { + try { + maybeInstantiateVerifier(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (jv != null && !jvInitialized) { + initializeVerifier(); + jvInitialized = true; + } + } + + JarEntry newEntry(ZipEntry ze) { + return new JarFileEntry(ze); + } + + Enumeration entryNames(CodeSource[] cs) { + ensureInitialization(); + if (jv != null) { + return jv.entryNames(this, cs); + } + + /* + * JAR file has no signed content. Is there a non-signing + * code source? + */ + boolean includeUnsigned = false; + for (int i = 0; i < cs.length; i++) { + if (cs[i].getCodeSigners() == null) { + includeUnsigned = true; + break; + } + } + if (includeUnsigned) { + return unsignedEntryNames(); + } else { + return new Enumeration() { + + public boolean hasMoreElements() { + return false; + } + + public String nextElement() { + throw new NoSuchElementException(); + } + }; + } + } + + /** + * Returns an enumeration of the zip file entries + * excluding internal JAR mechanism entries and including + * signed entries missing from the ZIP directory. + */ + Enumeration entries2() { + ensureInitialization(); + if (jv != null) { + return jv.entries2(this, super.entries()); + } + + // screen out entries which are never signed + final Enumeration enum_ = super.entries(); + return new Enumeration() { + + ZipEntry entry; + + public boolean hasMoreElements() { + if (entry != null) { + return true; + } + while (enum_.hasMoreElements()) { + ZipEntry ze = (ZipEntry) enum_.nextElement(); + if (JarVerifier.isSigningRelated(ze.getName())) { + continue; + } + entry = ze; + return true; + } + return false; + } + + public JarFileEntry nextElement() { + if (hasMoreElements()) { + ZipEntry ze = entry; + entry = null; + return new JarFileEntry(ze); + } + throw new NoSuchElementException(); + } + }; + } + + CodeSource[] getCodeSources(URL url) { + ensureInitialization(); + if (jv != null) { + return jv.getCodeSources(this, url); + } + + /* + * JAR file has no signed content. Is there a non-signing + * code source? + */ + Enumeration unsigned = unsignedEntryNames(); + if (unsigned.hasMoreElements()) { + return new CodeSource[]{JarVerifier.getUnsignedCS(url)}; + } else { + return null; + } + } + + private Enumeration unsignedEntryNames() { + final Enumeration entries = entries(); + return new Enumeration() { + + String name; + + /* + * Grab entries from ZIP directory but screen out + * metadata. + */ + public boolean hasMoreElements() { + if (name != null) { + return true; + } + while (entries.hasMoreElements()) { + String value; + ZipEntry e = (ZipEntry) entries.nextElement(); + value = e.getName(); + if (e.isDirectory() || JarVerifier.isSigningRelated(value)) { + continue; + } + name = value; + return true; + } + return false; + } + + public String nextElement() { + if (hasMoreElements()) { + String value = name; + name = null; + return value; + } + throw new NoSuchElementException(); + } + }; + } + + CodeSource getCodeSource(URL url, String name) { + ensureInitialization(); + if (jv != null) { + if (jv.eagerValidation) { + CodeSource cs = null; + JarEntry je = getJarEntry(name); + if (je != null) { + cs = jv.getCodeSource(url, this, je); + } else { + cs = jv.getCodeSource(url, name); + } + return cs; + } else { + return jv.getCodeSource(url, name); + } + } + + return JarVerifier.getUnsignedCS(url); + } + + void setEagerValidation(boolean eager) { + try { + maybeInstantiateVerifier(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (jv != null) { + jv.setEagerValidation(eager); + } + } + + List getManifestDigests() { + ensureInitialization(); + if (jv != null) { + return jv.getManifestDigests(); + } + return new ArrayList(); + } } diff --git a/src/share/classes/java/util/jar/JarVerifier.java b/src/share/classes/java/util/jar/JarVerifier.java index 33c67c15a6dcbe84c438daeffdc608c946ffd7eb..abbb85e3768862c8bf9e00a2820260a7fa1d49ca 100644 --- a/src/share/classes/java/util/jar/JarVerifier.java +++ b/src/share/classes/java/util/jar/JarVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -26,9 +26,11 @@ package java.util.jar; import java.io.*; +import java.net.URL; import java.util.*; import java.security.*; import java.security.cert.CertificateException; +import java.util.zip.ZipEntry; import sun.security.util.ManifestDigester; import sun.security.util.ManifestEntryVerifier; @@ -81,6 +83,15 @@ class JarVerifier { /** the bytes for the manDig object */ byte manifestRawBytes[] = null; + /** controls eager signature validation */ + boolean eagerValidation; + + /** makes code source singleton instances unique to us */ + private Object csdomain = new Object(); + + /** collect -DIGEST-MANIFEST values for blacklist */ + private List manifestDigests; + public JarVerifier(byte rawBytes[]) { manifestRawBytes = rawBytes; sigFileSigners = new Hashtable(); @@ -88,6 +99,7 @@ class JarVerifier { sigFileData = new Hashtable(11); pendingBlocks = new ArrayList(); baos = new ByteArrayOutputStream(); + manifestDigests = new ArrayList(); } /** @@ -247,7 +259,7 @@ class JarVerifier { } sfv.setSignatureFile(bytes); - sfv.process(sigFileSigners); + sfv.process(sigFileSigners, manifestDigests); } } return; @@ -290,7 +302,7 @@ class JarVerifier { sfv.setSignatureFile(bytes); } } - sfv.process(sigFileSigners); + sfv.process(sigFileSigners, manifestDigests); } catch (IOException ioe) { // e.g. sun.security.pkcs.ParsingException @@ -312,12 +324,18 @@ class JarVerifier { /** * Return an array of java.security.cert.Certificate objects for * the given file in the jar. + * @deprecated */ public java.security.cert.Certificate[] getCerts(String name) { return mapSignersToCertArray(getCodeSigners(name)); } + public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry) + { + return mapSignersToCertArray(getCodeSigners(jar, entry)); + } + /** * return an array of CodeSigner objects for * the given file in the jar. this array is not cloned. @@ -328,6 +346,28 @@ class JarVerifier { return (CodeSigner[])verifiedSigners.get(name); } + public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry) + { + String name = entry.getName(); + if (eagerValidation && sigFileSigners.get(name) != null) { + /* + * Force a read of the entry data to generate the + * verification hash. + */ + try { + InputStream s = jar.getInputStream(entry); + byte[] buffer = new byte[1024]; + int n = buffer.length; + while (n != -1) { + n = s.read(buffer, 0, buffer.length); + } + s.close(); + } catch (IOException e) { + } + } + return getCodeSigners(name); + } + /* * Convert an array of signers into an array of concatenated certificate * arrays. @@ -444,4 +484,393 @@ class JarVerifier { } } + + // Extended JavaUtilJarAccess CodeSource API Support + + private Map urlToCodeSourceMap = new HashMap(); + private Map signerToCodeSource = new HashMap(); + private URL lastURL; + private Map lastURLMap; + + /* + * Create a unique mapping from codeSigner cache entries to CodeSource. + * In theory, multiple URLs origins could map to a single locally cached + * and shared JAR file although in practice there will be a single URL in use. + */ + private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) { + Map map; + if (url == lastURL) { + map = lastURLMap; + } else { + map = (Map) urlToCodeSourceMap.get(url); + if (map == null) { + map = new HashMap(); + urlToCodeSourceMap.put(url, map); + } + lastURLMap = map; + lastURL = url; + } + CodeSource cs = (CodeSource) map.get(signers); + if (cs == null) { + cs = new VerifierCodeSource(csdomain, url, signers); + signerToCodeSource.put(signers, cs); + } + return cs; + } + + private CodeSource[] mapSignersToCodeSources(URL url, List signers, boolean unsigned) { + List sources = new ArrayList(); + + for (int i = 0; i < signers.size(); i++) { + sources.add(mapSignersToCodeSource(url, (CodeSigner[]) signers.get(i))); + } + if (unsigned) { + sources.add(mapSignersToCodeSource(url, null)); + } + return (CodeSource[]) sources.toArray(new CodeSource[sources.size()]); + } + private CodeSigner[] emptySigner = new CodeSigner[0]; + + /* + * Match CodeSource to a CodeSigner[] in the signer cache. + */ + private CodeSigner[] findMatchingSigners(CodeSource cs) { + if (cs instanceof VerifierCodeSource) { + VerifierCodeSource vcs = (VerifierCodeSource) cs; + if (vcs.isSameDomain(csdomain)) { + return ((VerifierCodeSource) cs).getPrivateSigners(); + } + } + + /* + * In practice signers should always be optimized above + * but this handles a CodeSource of any type, just in case. + */ + CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true); + List sourceList = new ArrayList(); + for (int i = 0; i < sources.length; i++) { + sourceList.add(sources[i]); + } + int j = sourceList.indexOf(cs); + if (j != -1) { + CodeSigner[] match; + match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners(); + if (match == null) { + match = emptySigner; + } + return match; + } + return null; + } + + /* + * Instances of this class hold uncopied references to internal + * signing data that can be compared by object reference identity. + */ + private static class VerifierCodeSource extends CodeSource { + + URL vlocation; + CodeSigner[] vsigners; + java.security.cert.Certificate[] vcerts; + Object csdomain; + + VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) { + super(location, signers); + this.csdomain = csdomain; + vlocation = location; + vsigners = signers; // from signerCache + } + + VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) { + super(location, certs); + this.csdomain = csdomain; + vlocation = location; + vcerts = certs; // from signerCache + } + + /* + * All VerifierCodeSource instances are constructed based on + * singleton signerCache or signerCacheCert entries for each unique signer. + * No CodeSigner<->Certificate[] conversion is required. + * We use these assumptions to optimize equality comparisons. + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof VerifierCodeSource) { + VerifierCodeSource that = (VerifierCodeSource) obj; + + /* + * Only compare against other per-signer singletons constructed + * on behalf of the same JarFile instance. Otherwise, compare + * things the slower way. + */ + if (isSameDomain(that.csdomain)) { + if (that.vsigners != this.vsigners + || that.vcerts != this.vcerts) { + return false; + } + if (that.vlocation != null) { + return that.vlocation.equals(this.vlocation); + } else if (this.vlocation != null) { + return this.vlocation.equals(that.vlocation); + } else { // both null + return true; + } + } + } + return super.equals(obj); + } + + boolean isSameDomain(Object csdomain) { + return this.csdomain == csdomain; + } + + private CodeSigner[] getPrivateSigners() { + return vsigners; + } + + private java.security.cert.Certificate[] getPrivateCertificates() { + return vcerts; + } + } + private Map signerMap; + + private synchronized Map signerMap() { + if (signerMap == null) { + /* + * Snapshot signer state so it doesn't change on us. We care + * only about the asserted signatures. Verification of + * signature validity happens via the JarEntry apis. + */ + signerMap = new HashMap(verifiedSigners.size() + sigFileSigners.size()); + signerMap.putAll(verifiedSigners); + signerMap.putAll(sigFileSigners); + } + return signerMap; + } + + public synchronized Enumeration entryNames(JarFile jar, final CodeSource[] cs) { + final Map map = signerMap(); + final Iterator itor = map.entrySet().iterator(); + boolean matchUnsigned = false; + + /* + * Grab a single copy of the CodeSigner arrays. Check + * to see if we can optimize CodeSigner equality test. + */ + List req = new ArrayList(cs.length); + for (int i = 0; i < cs.length; i++) { + CodeSigner[] match = findMatchingSigners(cs[i]); + if (match != null) { + if (match.length > 0) { + req.add(match); + } else { + matchUnsigned = true; + } + } + } + + final List signersReq = req; + final Enumeration enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration; + + return new Enumeration() { + + String name; + + public boolean hasMoreElements() { + if (name != null) { + return true; + } + + while (itor.hasNext()) { + Map.Entry e = (Map.Entry) itor.next(); + if (signersReq.contains((CodeSigner[]) e.getValue())) { + name = (String) e.getKey(); + return true; + } + } + while (enum2.hasMoreElements()) { + name = (String) enum2.nextElement(); + return true; + } + return false; + } + + public String nextElement() { + if (hasMoreElements()) { + String value = name; + name = null; + return value; + } + throw new NoSuchElementException(); + } + }; + } + + /* + * Like entries() but screens out internal JAR mechanism entries + * and includes signed entries with no ZIP data. + */ + public Enumeration entries2(final JarFile jar, Enumeration e) { + final Map map = new HashMap(); + map.putAll(signerMap()); + final Enumeration enum_ = e; + return new Enumeration() { + + Enumeration signers = null; + JarEntry entry; + + public boolean hasMoreElements() { + if (entry != null) { + return true; + } + while (enum_.hasMoreElements()) { + ZipEntry ze = (ZipEntry) enum_.nextElement(); + if (JarVerifier.isSigningRelated(ze.getName())) { + continue; + } + entry = jar.newEntry(ze); + return true; + } + if (signers == null) { + signers = Collections.enumeration(map.keySet()); + } + while (signers.hasMoreElements()) { + String name = (String) signers.nextElement(); + entry = jar.newEntry(new ZipEntry(name)); + return true; + } + + // Any map entries left? + return false; + } + + public JarEntry nextElement() { + if (hasMoreElements()) { + JarEntry je = entry; + map.remove(je.getName()); + entry = null; + return je; + } + throw new NoSuchElementException(); + } + }; + } + private Enumeration emptyEnumeration = new Enumeration() { + + public boolean hasMoreElements() { + return false; + } + + public String nextElement() { + throw new NoSuchElementException(); + } + }; + + // true if file is part of the signature mechanism itself + static boolean isSigningRelated(String name) { + name = name.toUpperCase(Locale.ENGLISH); + if (!name.startsWith("META-INF/")) { + return false; + } + name = name.substring(9); + if (name.indexOf('/') != -1) { + return false; + } + if (name.endsWith(".DSA") + || name.endsWith(".RSA") + || name.endsWith(".SF") + || name.endsWith(".EC") + || name.startsWith("SIG-") + || name.equals("MANIFEST.MF")) { + return true; + } + return false; + } + + private Enumeration unsignedEntryNames(JarFile jar) { + final Map map = signerMap(); + final Enumeration entries = jar.entries(); + return new Enumeration() { + + String name; + + /* + * Grab entries from ZIP directory but screen out + * metadata. + */ + public boolean hasMoreElements() { + if (name != null) { + return true; + } + while (entries.hasMoreElements()) { + String value; + ZipEntry e = (ZipEntry) entries.nextElement(); + value = e.getName(); + if (e.isDirectory() || isSigningRelated(value)) { + continue; + } + if (map.get(value) == null) { + name = value; + return true; + } + } + return false; + } + + public String nextElement() { + if (hasMoreElements()) { + String value = name; + name = null; + return value; + } + throw new NoSuchElementException(); + } + }; + } + private List jarCodeSigners; + + private synchronized List getJarCodeSigners() { + CodeSigner[] signers; + if (jarCodeSigners == null) { + HashSet set = new HashSet(); + set.addAll(signerMap().values()); + jarCodeSigners = new ArrayList(); + jarCodeSigners.addAll(set); + } + return jarCodeSigners; + } + + public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) { + boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements(); + + return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned); + } + + public CodeSource getCodeSource(URL url, String name) { + CodeSigner[] signers; + + signers = (CodeSigner[]) signerMap().get(name); + return mapSignersToCodeSource(url, signers); + } + + public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) { + CodeSigner[] signers; + + return mapSignersToCodeSource(url, getCodeSigners(jar, je)); + } + + public void setEagerValidation(boolean eager) { + eagerValidation = eager; + } + + public synchronized List getManifestDigests() { + return Collections.unmodifiableList(manifestDigests); + } + + static CodeSource getUnsignedCS(URL url) { + return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null); + } } diff --git a/src/share/classes/java/util/jar/JavaUtilJarAccessImpl.java b/src/share/classes/java/util/jar/JavaUtilJarAccessImpl.java index bed2b5a77e5df1edd6349ae4bf6d0497162a4a9b..c53ba0c6f5212409cc4a9d089ccc3b961c85856b 100644 --- a/src/share/classes/java/util/jar/JavaUtilJarAccessImpl.java +++ b/src/share/classes/java/util/jar/JavaUtilJarAccessImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -26,10 +26,38 @@ package java.util.jar; import java.io.IOException; +import java.net.URL; +import java.security.CodeSource; +import java.util.Enumeration; +import java.util.List; import sun.misc.JavaUtilJarAccess; class JavaUtilJarAccessImpl implements JavaUtilJarAccess { public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException { return jar.hasClassPathAttribute(); } + + public CodeSource[] getCodeSources(JarFile jar, URL url) { + return jar.getCodeSources(url); + } + + public CodeSource getCodeSource(JarFile jar, URL url, String name) { + return jar.getCodeSource(url, name); + } + + public Enumeration entryNames(JarFile jar, CodeSource[] cs) { + return jar.entryNames(cs); + } + + public Enumeration entries2(JarFile jar) { + return jar.entries2(); + } + + public void setEagerValidation(JarFile jar, boolean eager) { + jar.setEagerValidation(eager); + } + + public List getManifestDigests(JarFile jar) { + return jar.getManifestDigests(); + } } diff --git a/src/share/classes/javax/script/ScriptEngineFactory.java b/src/share/classes/javax/script/ScriptEngineFactory.java index 9fc08a92bbd8c88073a15a89233584f34808d7d4..f39f33de4c0375dff066915a3132acf7c2f7678b 100644 --- a/src/share/classes/javax/script/ScriptEngineFactory.java +++ b/src/share/classes/javax/script/ScriptEngineFactory.java @@ -115,20 +115,19 @@ public interface ScriptEngineFactory { * with respect to concurrent execution of scripts and maintenance of state is also defined. * These values for the THREADING key are:

    *

      - *

      null - The engine implementation is not thread safe, and cannot + *

    • null - The engine implementation is not thread safe, and cannot * be used to execute scripts concurrently on multiple threads. - *

      "MULTITHREADED" - The engine implementation is internally + *

    • "MULTITHREADED" - The engine implementation is internally * thread-safe and scripts may execute concurrently although effects of script execution * on one thread may be visible to scripts on other threads. - *

      "THREAD-ISOLATED" - The implementation satisfies the requirements + *

    • "THREAD-ISOLATED" - The implementation satisfies the requirements * of "MULTITHREADED", and also, the engine maintains independent values * for symbols in scripts executing on different threads. - *

      "STATELESS" - The implementation satisfies the requirements of - * "THREAD-ISOLATED". In addition, script executions do not alter the + *

    • "STATELESS" - The implementation satisfies the requirements of + *
    • "THREAD-ISOLATED". In addition, script executions do not alter the * mappings in the Bindings which is the engine scope of the * ScriptEngine. In particular, the keys in the Bindings * and their associated values are the same before and after the execution of the script. - *
    • *
    *

    * Implementations may define implementation-specific keys. @@ -145,22 +144,23 @@ public interface ScriptEngineFactory { * of the supported scripting language. For instance, an implementaton for a Javascript * engine might be; *

    - *

    +     * 
    +     * 
          * public String getMethodCallSyntax(String obj,
          *                                   String m, String... args) {
          *      String ret = obj;
          *      ret += "." + m + "(";
          *      for (int i = 0; i < args.length; i++) {
          *          ret += args[i];
    -     *          if (i == args.length - 1) {
    -     *              ret += ")";
    -     *          } else {
    +     *          if (i < args.length - 1) {
          *              ret += ",";
          *          }
          *      }
    +     *      ret += ")";
          *      return ret;
          * }
    -     *
    + *
    + *
    *

    * * @param obj The name representing the object whose method is to be invoked. The diff --git a/src/share/classes/javax/sound/sampled/AudioSystem.java b/src/share/classes/javax/sound/sampled/AudioSystem.java index 3724cc2cdacb8cf1da8da667d7aaed2f58d1eb4c..fe3347540ff492d8fded2f5a26eb3e9d4b96c5ba 100644 --- a/src/share/classes/javax/sound/sampled/AudioSystem.java +++ b/src/share/classes/javax/sound/sampled/AudioSystem.java @@ -670,6 +670,12 @@ public class AudioSystem { *

    The returned TargetDataLine's default * audio format will be initialized with format. * + *

    If the system property + * {@code javax.sound.sampled.TargetDataLine} + * is defined or it is defined in the file "sound.properties", + * it is used to retrieve the default target data line. + * For details, refer to the {@link AudioSystem class description}. + * * @param format an AudioFormat object specifying * the supported audio format of the returned line, * or null for any audio format @@ -712,12 +718,6 @@ public class AudioSystem { *

    The returned TargetDataLine's default * audio format will be initialized with format. * - *

    If the system property - * javax.sound.sampled.TargetDataLine - * is defined or it is defined in the file "sound.properties", - * it is used to retrieve the default target data line. - * For details, refer to the {@link AudioSystem class description}. - * * @param format an AudioFormat object specifying * the supported audio format of the returned line, * or null for any audio format diff --git a/src/share/classes/javax/sql/rowset/serial/SerialClob.java b/src/share/classes/javax/sql/rowset/serial/SerialClob.java index ff482082e7c76e937d38be6a865d7753e71df03c..ad7238d1baf8dafbd8a7bf2c670dc09b9316b2c4 100644 --- a/src/share/classes/javax/sql/rowset/serial/SerialClob.java +++ b/src/share/classes/javax/sql/rowset/serial/SerialClob.java @@ -57,10 +57,10 @@ public class SerialClob implements Clob, Serializable, Cloneable { private char buf[]; /** - * Internal Clob representation if SerialClob is intialized with a - * Clob + * Internal Clob representation if SerialClob is initialized with a + * Clob. Null if SerialClob is initialized with a char[]. */ - private Clob clob; + private final Clob clob; /** * The length in characters of this SerialClob object's @@ -71,12 +71,12 @@ public class SerialClob implements Clob, Serializable, Cloneable { private long len; /** - * The original length in characters of tgus SerialClob - * objects internal array of characters. + * The original length in characters of this SerialClob + * object's internal array of characters. * * @serial */ - private long origLen; + private final long origLen; /** * Constructs a SerialClob object that is a serialized version of @@ -104,6 +104,7 @@ public class SerialClob implements Clob, Serializable, Cloneable { buf[i] = ch[i]; } origLen = len; + clob = null; } /** @@ -117,19 +118,19 @@ public class SerialClob implements Clob, Serializable, Cloneable { * the database. Otherwise, the new SerialClob object * object will contain no data. *

    - * Note: The Clob object supplied to this constructor cannot - * return null for the Clob.getCharacterStream() + * Note: The Clob object supplied to this constructor must + * return non-null for both the Clob.getCharacterStream() * and Clob.getAsciiStream methods. This SerialClob - * constructor cannot serialize a Clob object in this instance + * constructor cannot serialize a Clob object in this instance * and will throw an SQLException object. * * @param clob the Clob object from which this * SerialClob object is to be constructed; cannot be null * @throws SerialException if an error occurs during serialization * @throws SQLException if a SQL error occurs in capturing the CLOB; - * if the Clob object is a null; or if both the + * if the Clob object is a null; or if either of the * Clob.getCharacterStream() and Clob.getAsciiStream() - * methods on the Clob return a null + * methods on the Clob returns a null * @see java.sql.Clob */ public SerialClob(Clob clob) throws SerialException, SQLException { @@ -144,19 +145,27 @@ public class SerialClob implements Clob, Serializable, Cloneable { int read = 0; int offset = 0; - BufferedReader reader; - if ( (((reader = new BufferedReader(clob.getCharacterStream())) == null)) && - (clob.getAsciiStream() == null)) { - throw new SQLException("Invalid Clob object. Calls to getCharacterStream " + - "and getAsciiStream return null which cannot be serialized."); - } + try (Reader charStream = clob.getCharacterStream()) { + if (charStream == null) { + throw new SQLException("Invalid Clob object. The call to getCharacterStream " + + "returned null which cannot be serialized."); + } - try { - do { - read = reader.read(buf, offset, (int)(len - offset)); - offset += read; - } while (read > 0); + // Note: get an ASCII stream in order to null-check it, + // even though we don't do anything with it. + try (InputStream asciiStream = clob.getAsciiStream()) { + if (asciiStream == null) { + throw new SQLException("Invalid Clob object. The call to getAsciiStream " + + "returned null which cannot be serialized."); + } + } + try (Reader reader = new BufferedReader(charStream)) { + do { + read = reader.read(buf, offset, (int)(len - offset)); + offset += read; + } while (read > 0); + } } catch (java.io.IOException ex) { throw new SerialException("SerialClob: " + ex.getMessage()); } @@ -207,13 +216,13 @@ public class SerialClob implements Clob, Serializable, Cloneable { * used to create this SerialClob object */ public java.io.InputStream getAsciiStream() throws SerialException, SQLException { - if (this.clob != null) { - return this.clob.getAsciiStream(); - } else { - throw new SerialException("Unsupported operation. SerialClob cannot " + + if (this.clob != null) { + return this.clob.getAsciiStream(); + } else { + throw new SerialException("Unsupported operation. SerialClob cannot " + "return a the CLOB value as an ascii stream, unless instantiated " + "with a fully implemented Clob object."); - } + } } /** diff --git a/src/share/classes/javax/sql/rowset/spi/SyncFactory.java b/src/share/classes/javax/sql/rowset/spi/SyncFactory.java index f3358e0fb12b43805526fb3135c563a722484f5b..b52f345caf5b45d3e22194742113453ebac9207c 100644 --- a/src/share/classes/javax/sql/rowset/spi/SyncFactory.java +++ b/src/share/classes/javax/sql/rowset/spi/SyncFactory.java @@ -32,6 +32,7 @@ import java.sql.*; import javax.sql.*; import java.io.FileInputStream; +import java.io.InputStream; import java.io.IOException; import java.io.FileNotFoundException; @@ -366,7 +367,9 @@ public class SyncFactory { // Load user's implementation of SyncProvider // here. -Drowset.properties=/abc/def/pqr.txt ROWSET_PROPERTIES = strRowsetProperties; - properties.load(new FileInputStream(ROWSET_PROPERTIES)); + try (FileInputStream fis = new FileInputStream(ROWSET_PROPERTIES)) { + properties.load(fis); + } parseProperties(properties); } @@ -376,12 +379,19 @@ public class SyncFactory { ROWSET_PROPERTIES = "javax" + strFileSep + "sql" + strFileSep + "rowset" + strFileSep + "rowset.properties"; - // properties.load( - // ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES)); ClassLoader cl = Thread.currentThread().getContextClassLoader(); - properties.load(cl.getResourceAsStream(ROWSET_PROPERTIES)); + try (InputStream stream = + (cl == null) ? ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES) + : cl.getResourceAsStream(ROWSET_PROPERTIES)) { + if (stream == null) { + throw new SyncFactoryException( + "Resource " + ROWSET_PROPERTIES + " not found"); + } + properties.load(stream); + } + parseProperties(properties); // removed else, has properties should sum together diff --git a/src/share/classes/javax/swing/JComponent.java b/src/share/classes/javax/swing/JComponent.java index 7bed584dca6903e4c3f3574bcbaf934cf76952e9..eff194ca21265f10bfbba8207be57e02cbae391d 100644 --- a/src/share/classes/javax/swing/JComponent.java +++ b/src/share/classes/javax/swing/JComponent.java @@ -4910,14 +4910,17 @@ public abstract class JComponent extends Container implements Serializable, * Returns {@code true} if a paint triggered on a child component should cause * painting to originate from this Component, or one of its ancestors. *

    - * Calling {@link JComponent#repaint} on a Swing component will be delegated to - * the first ancestor which {@code isPaintingOrigin()} returns {@code true}, - * if there are any. + * Calling {@link #repaint} or {@link #paintImmediately(int, int, int, int)} + * on a Swing component will result in calling + * the {@link JComponent#paintImmediately(int, int, int, int)} method of + * the first ancestor which {@code isPaintingOrigin()} returns {@code true}, if there are any. *

    - * {@code JComponent} subclasses that need to be repainted when any of their + * {@code JComponent} subclasses that need to be painted when any of their * children are repainted should override this method to return {@code true}. * * @return always returns {@code false} + * + * @see #paintImmediately(int, int, int, int) */ protected boolean isPaintingOrigin() { return false; @@ -4932,12 +4935,16 @@ public abstract class JComponent extends Container implements Serializable, * and can collapse redundant requests into a single paint call. * This method is useful if one needs to update the display while * the current event is being dispatched. + *

    + * This method is to be overridden when the dirty region needs to be changed + * for components that are painting origins. * * @param x the x value of the region to be painted * @param y the y value of the region to be painted * @param w the width of the region to be painted * @param h the height of the region to be painted * @see #repaint + * @see #isPaintingOrigin() */ public void paintImmediately(int x,int y,int w, int h) { Component c = this; @@ -4946,6 +4953,15 @@ public abstract class JComponent extends Container implements Serializable, if(!isShowing()) { return; } + + JComponent paintingOigin = SwingUtilities.getPaintingOrigin(this); + if (paintingOigin != null) { + Rectangle rectangle = SwingUtilities.convertRectangle( + c, new Rectangle(x, y, w, h), paintingOigin); + paintingOigin.paintImmediately(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + return; + } + while(!c.isOpaque()) { parent = c.getParent(); if(parent != null) { diff --git a/src/share/classes/javax/swing/JLayer.java b/src/share/classes/javax/swing/JLayer.java index 764e553fd61e19671ac5b88a061ec8019669320e..592fe82e24c00af9cedc85cb7bbb426c9628ec6b 100644 --- a/src/share/classes/javax/swing/JLayer.java +++ b/src/share/classes/javax/swing/JLayer.java @@ -156,8 +156,9 @@ public final class JLayer // when layerUI is serializable private LayerUI layerUI; private JPanel glassPane; - private boolean isPainting; private long eventMask; + private transient boolean isPainting; + private transient boolean isPaintingImmediately; private static final LayerEventController eventController = new LayerEventController(); @@ -393,17 +394,25 @@ public final class JLayer } /** - * Delegates repainting to {@link javax.swing.plaf.LayerUI#repaint} method. + * Delegates its functionality to the + * {@link javax.swing.plaf.LayerUI#paintImmediately(int, int, int, int, JLayer)} method, + * if {@code LayerUI} is set. * - * @param tm this parameter is not used - * @param x the x value of the dirty region - * @param y the y value of the dirty region - * @param width the width of the dirty region - * @param height the height of the dirty region - */ - public void repaint(long tm, int x, int y, int width, int height) { - if (getUI() != null) { - getUI().repaint(tm, x, y, width, height, this); + * @param x the x value of the region to be painted + * @param y the y value of the region to be painted + * @param w the width of the region to be painted + * @param h the height of the region to be painted + */ + public void paintImmediately(int x, int y, int w, int h) { + if (!isPaintingImmediately && getUI() != null) { + isPaintingImmediately = true; + try { + getUI().paintImmediately(x, y, w, h, this); + } finally { + isPaintingImmediately = false; + } + } else { + super.paintImmediately(x, y, w, h); } } @@ -415,8 +424,11 @@ public final class JLayer public void paint(Graphics g) { if (!isPainting) { isPainting = true; - super.paintComponent(g); - isPainting = false; + try { + super.paintComponent(g); + } finally { + isPainting = false; + } } else { super.paint(g); } diff --git a/src/share/classes/javax/swing/LookAndFeel.java b/src/share/classes/javax/swing/LookAndFeel.java index 8c61c18f96a9b3de9ce7fa5518bc03bc79bbd968..bcaa06247b9b676a0929d451e231d5e23710ea92 100644 --- a/src/share/classes/javax/swing/LookAndFeel.java +++ b/src/share/classes/javax/swing/LookAndFeel.java @@ -332,12 +332,13 @@ public abstract class LookAndFeel { JTextComponent.KeyBinding[] rv = new JTextComponent.KeyBinding[keyBindingList.length / 2]; - for(int i = 0; i < keyBindingList.length; i += 2) { - KeyStroke keystroke = (keyBindingList[i] instanceof KeyStroke) - ? (KeyStroke)keyBindingList[i] - : KeyStroke.getKeyStroke((String)keyBindingList[i]); - String action = (String)keyBindingList[i+1]; - rv[i / 2] = new JTextComponent.KeyBinding(keystroke, action); + for(int i = 0; i < rv.length; i ++) { + Object o = keyBindingList[2 * i]; + KeyStroke keystroke = (o instanceof KeyStroke) + ? (KeyStroke) o + : KeyStroke.getKeyStroke((String) o); + String action = (String) keyBindingList[2 * i + 1]; + rv[i] = new JTextComponent.KeyBinding(keystroke, action); } return rv; diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java index 062d2e70b0932aeb808c9b5413f1bfdf1f7cb73f..361e863a518bdf4a771f506bf5c74eec6cc1b97d 100644 --- a/src/share/classes/javax/swing/RepaintManager.java +++ b/src/share/classes/javax/swing/RepaintManager.java @@ -438,7 +438,6 @@ public class RepaintManager * @param y Y coordinate of the region to repaint * @param w Width of the region to repaint * @param h Height of the region to repaint - * @see JComponent#isPaintingOrigin() * @see JComponent#repaint */ public void addDirtyRegion(JComponent c, int x, int y, int w, int h) @@ -448,16 +447,6 @@ public class RepaintManager delegate.addDirtyRegion(c, x, y, w, h); return; } - Container p = c; - while ((p = p.getParent()) instanceof JComponent) { - JComponent jp = (JComponent) p; - if (jp.isPaintingOrigin()) { - Rectangle rectangle = SwingUtilities.convertRectangle( - c, new Rectangle(x, y, w, h), jp); - jp.repaint(0, rectangle.x, rectangle.y, rectangle.width, rectangle.height); - return; - } - } addDirtyRegion0(c, x, y, w, h); } diff --git a/src/share/classes/javax/swing/SwingUtilities.java b/src/share/classes/javax/swing/SwingUtilities.java index 61dde01e93ce1112d7d9bab0a139e55844046340..86948132cea0ab0de36a5815a421240033d3e8d8 100644 --- a/src/share/classes/javax/swing/SwingUtilities.java +++ b/src/share/classes/javax/swing/SwingUtilities.java @@ -1532,6 +1532,17 @@ public class SwingUtilities implements SwingConstants return applet; } + static JComponent getPaintingOrigin(JComponent c) { + Container p = c; + while ((p = p.getParent()) instanceof JComponent) { + JComponent jp = (JComponent) p; + if (jp.isPaintingOrigin()) { + return jp; + } + } + return null; + } + /** * Process the key bindings for the Component associated with * event. This method is only useful if diff --git a/src/share/classes/javax/swing/plaf/LayerUI.java b/src/share/classes/javax/swing/plaf/LayerUI.java index 655d9f27177fedecd471d2e61e2cae98978c621c..e118467aa99967717bea219277f1aea9935ae690 100644 --- a/src/share/classes/javax/swing/plaf/LayerUI.java +++ b/src/share/classes/javax/swing/plaf/LayerUI.java @@ -703,21 +703,19 @@ public class LayerUI } /** - * Adds the specified region to the dirty region list if the component - * is showing. The component will be repainted after all of the - * currently pending events have been dispatched. + * Paints the specified region in the {@code JLayer} this {@code LayerUI} is set to, immediately. *

    * This method is to be overridden when the dirty region needs to be changed. + * The default implementation delegates its functionality to {@link JComponent#paintImmediately(int, int, int, int)}. * - * @param tm this parameter is not used - * @param x the x value of the dirty region - * @param y the y value of the dirty region - * @param width the width of the dirty region - * @param height the height of the dirty region - * @see java.awt.Component#isShowing - * @see RepaintManager#addDirtyRegion + * @param x the x value of the region to be painted + * @param y the y value of the region to be painted + * @param w the width of the region to be painted + * @param h the height of the region to be painted + * + * @see JComponent#paintImmediately(int, int, int, int) */ - public void repaint(long tm, int x, int y, int width, int height, JLayer l) { - RepaintManager.currentManager(l).addDirtyRegion(l, x, y, width, height); + public void paintImmediately(int x, int y, int width, int height, JLayer l) { + l.paintImmediately(x, y, width, height); } } diff --git a/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java b/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java index 23e8f7dce796d7d0eecef9c0b1e7985fdd3e4907..fb97f12eb8c9f6fc32fa2feac3108c36ec9c468e 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java @@ -1965,18 +1965,18 @@ public class BasicTreeUI extends TreeUI } /** Returns the preferred size to properly display the tree, - * this is a cover method for getPreferredSize(c, false). + * this is a cover method for getPreferredSize(c, true). */ public Dimension getPreferredSize(JComponent c) { return getPreferredSize(c, true); } /** Returns the preferred size to represent the tree in - * c. If checkConsistancy is true - * checkConsistancy is messaged first. + * c. If checkConsistency is true + * checkConsistency is messaged first. */ public Dimension getPreferredSize(JComponent c, - boolean checkConsistancy) { + boolean checkConsistency) { Dimension pSize = this.getPreferredMinSize(); if(!validCachedPreferredSize) diff --git a/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java b/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java index 0a4fa2767529c326f4eeb6f50fca1f2478b8a13e..63a8f377cea10efb01b9967da0dba13e87ef4de1 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java @@ -510,7 +510,6 @@ public class SynthGraphicsUtils { Font holdf = g.getFont(); Color holdc = g.getColor(); - paintBackground(g, lh); paintCheckIcon(g, lh, lr); paintIcon(g, lh, lr); paintText(g, lh, lr); diff --git a/src/share/classes/sun/dyn/AdapterMethodHandle.java b/src/share/classes/sun/dyn/AdapterMethodHandle.java index 4766ba57df61113b3bca68737777a9225233bece..676907c9185093c484009794fff8b9522f917d6c 100644 --- a/src/share/classes/sun/dyn/AdapterMethodHandle.java +++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -478,37 +478,60 @@ public class AdapterMethodHandle extends BoundMethodHandle { return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)); } - static MethodHandle makeTypeHandler(Access token, - MethodHandle target, MethodHandle typeHandler) { + static MethodHandle makeVarargsCollector(Access token, + MethodHandle target, Class arrayType) { Access.check(token); - return new WithTypeHandler(target, typeHandler); + return new AsVarargsCollector(target, arrayType); } - static class WithTypeHandler extends AdapterMethodHandle { - final MethodHandle target, typeHandler; - WithTypeHandler(MethodHandle target, MethodHandle typeHandler) { + static class AsVarargsCollector extends AdapterMethodHandle { + final MethodHandle target; + final Class arrayType; + MethodHandle cache; + + AsVarargsCollector(MethodHandle target, Class arrayType) { super(target, target.type(), makeConv(OP_RETYPE_ONLY)); this.target = target; - this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE); + this.arrayType = arrayType; + this.cache = target.asCollector(arrayType, 0); } + @Override + public boolean isVarargsCollector() { + return true; + } + + @Override public MethodHandle asType(MethodType newType) { - if (this.type() == newType) - return this; + MethodType type = this.type(); + int collectArg = type.parameterCount() - 1; + int newArity = newType.parameterCount(); + if (newArity == collectArg+1 && + type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) { + // if arity and trailing parameter are compatible, do normal thing + return super.asType(newType); + } + // check cache + if (cache.type().parameterCount() == newArity) + return cache.asType(newType); + // build and cache a collector + int arrayLength = newArity - collectArg; + MethodHandle collector; try { - MethodHandle retyped = (MethodHandle) typeHandler.invokeExact(target, newType); - // Contract: Must return the desired type, or throw WMT - if (retyped.type() != newType) - throw new WrongMethodTypeException(retyped.toString()); - return retyped; - } catch (Throwable ex) { - if (ex instanceof Error) throw (Error)ex; - if (ex instanceof RuntimeException) throw (RuntimeException)ex; - throw new RuntimeException(ex); + collector = target.asCollector(arrayType, arrayLength); + } catch (IllegalArgumentException ex) { + throw new WrongMethodTypeException("cannot build collector"); } + cache = collector; + return collector.asType(newType); + } + + public MethodHandle asVarargsCollector(Class arrayType) { + MethodType type = this.type(); + if (type.parameterType(type.parameterCount()-1) == arrayType) + return this; + return super.asVarargsCollector(arrayType); } - private static final MethodType TYPE_HANDLER_TYPE - = MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class); } /** Can a checkcast adapter validly convert the target to newType? @@ -939,7 +962,7 @@ public class AdapterMethodHandle extends BoundMethodHandle { @Override public String toString() { - return nonAdapter((MethodHandle)vmtarget).toString(); + return MethodHandleImpl.getNameString(IMPL_TOKEN, nonAdapter((MethodHandle)vmtarget), this); } private static MethodHandle nonAdapter(MethodHandle mh) { diff --git a/src/share/classes/sun/dyn/CallSiteImpl.java b/src/share/classes/sun/dyn/CallSiteImpl.java index 5257a285f68f23a0175e8b2d947f1bddf16732cc..703788ff48440306eabc79b7c7843fb3529b2b1b 100644 --- a/src/share/classes/sun/dyn/CallSiteImpl.java +++ b/src/share/classes/sun/dyn/CallSiteImpl.java @@ -37,17 +37,17 @@ public class CallSiteImpl { static CallSite makeSite(MethodHandle bootstrapMethod, // Callee information: String name, MethodType type, - // Call-site attributes, if any: + // Extra arguments for BSM, if any: Object info, // Caller information: MemberName callerMethod, int callerBCI) { Class callerClass = callerMethod.getDeclaringClass(); Object caller; - if (bootstrapMethod.type().parameterType(0) == Class.class) + if (bootstrapMethod.type().parameterType(0) == Class.class && TRANSITIONAL_BEFORE_PFD) caller = callerClass; // remove for PFD else caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass); - if (bootstrapMethod == null) { + if (bootstrapMethod == null && TRANSITIONAL_BEFORE_PFD) { // If there is no bootstrap method, throw IncompatibleClassChangeError. // This is a valid generic error type for resolution (JLS 12.3.3). throw new IncompatibleClassChangeError @@ -56,30 +56,35 @@ public class CallSiteImpl { CallSite site; try { Object binding; + info = maybeReBox(info); if (info == null) { - if (false) // switch when invokeGeneric works - binding = bootstrapMethod.invokeGeneric(caller, name, type); - else - binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type }); + binding = bootstrapMethod.invokeGeneric(caller, name, type); + } else if (!info.getClass().isArray()) { + binding = bootstrapMethod.invokeGeneric(caller, name, type, info); } else { - info = maybeReBox(info); - if (false) // switch when invokeGeneric works - binding = bootstrapMethod.invokeGeneric(caller, name, type, info); + Object[] argv = (Object[]) info; + if (3 + argv.length > 255) + new InvokeDynamicBootstrapError("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); else - binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type, info }); + binding = MethodHandles.spreadInvoker(bsmType, 3) + .invokeGeneric(bootstrapMethod, caller, name, type, argv); } //System.out.println("BSM for "+name+type+" => "+binding); if (binding instanceof CallSite) { site = (CallSite) binding; - } else if (binding instanceof MethodHandle) { + } else if (binding instanceof MethodHandle && TRANSITIONAL_BEFORE_PFD) { // Transitional! MethodHandle target = (MethodHandle) binding; site = new ConstantCallSite(target); } else { - throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite"); + throw new ClassCastException("bootstrap method failed to produce a CallSite"); } - PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type, - callerMethod, callerBCI); + if (TRANSITIONAL_BEFORE_PFD) + PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type, + callerMethod, callerBCI); assert(site.getTarget() != null); assert(site.getTarget().type().equals(type)); } catch (Throwable ex) { @@ -93,6 +98,8 @@ public class CallSiteImpl { return site; } + private static boolean TRANSITIONAL_BEFORE_PFD = true; // FIXME: remove for PFD + private static Object maybeReBox(Object x) { if (x instanceof Integer) { int xi = (int) x; @@ -117,11 +124,12 @@ public class CallSiteImpl { static { try { PRIVATE_INITIALIZE_CALL_SITE = + !TRANSITIONAL_BEFORE_PFD ? null : MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM", MethodType.methodType(void.class, String.class, MethodType.class, MemberName.class, int.class)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } } diff --git a/src/share/classes/sun/dyn/FilterGeneric.java b/src/share/classes/sun/dyn/FilterGeneric.java index c15a839877752566a32398f96ed2e16cabe6de13..e77d742a11e23477b0d7c8b38cc7a9aca48bfcea 100644 --- a/src/share/classes/sun/dyn/FilterGeneric.java +++ b/src/share/classes/sun/dyn/FilterGeneric.java @@ -187,7 +187,7 @@ class FilterGeneric { MethodHandle entryPoint = null; try { entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (entryPoint == null) continue; Constructor ctor = null; diff --git a/src/share/classes/sun/dyn/FilterOneArgument.java b/src/share/classes/sun/dyn/FilterOneArgument.java index f013bc352223313501c9a8a971b66a81c6bee113..86c722f3c0ab7fbf09504b4c30935aa937f1d720 100644 --- a/src/share/classes/sun/dyn/FilterOneArgument.java +++ b/src/share/classes/sun/dyn/FilterOneArgument.java @@ -56,7 +56,7 @@ public class FilterOneArgument extends BoundMethodHandle { INVOKE = MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", MethodType.genericMethodType(1)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } } diff --git a/src/share/classes/sun/dyn/FromGeneric.java b/src/share/classes/sun/dyn/FromGeneric.java index f5ad1061ce20888673065728af110a7d8df6339d..b996a6b3ead1da51c1b1dbe2e8a83f3fde199e9c 100644 --- a/src/share/classes/sun/dyn/FromGeneric.java +++ b/src/share/classes/sun/dyn/FromGeneric.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -28,6 +28,7 @@ package sun.dyn; import java.dyn.*; import java.lang.reflect.*; import sun.dyn.util.*; +import static sun.dyn.MethodTypeImpl.invokers; /** * Adapters which mediate between incoming calls which are generic @@ -128,7 +129,7 @@ class FromGeneric { MethodType targetType, MethodType internalType) { // All the adapters we have here have reference-untyped internal calls. assert(internalType == internalType.erase()); - MethodHandle invoker = MethodHandles.exactInvoker(targetType); + MethodHandle invoker = invokers(targetType).exactInvoker(); // cast all narrow reference types, unbox all primitive arguments: MethodType fixArgsType = internalType.changeReturnType(targetType.returnType()); MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN, @@ -203,7 +204,7 @@ class FromGeneric { MethodHandle entryPoint = null; try { entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (entryPoint == null) continue; Constructor ctor = null; diff --git a/src/share/classes/sun/dyn/InvokeGeneric.java b/src/share/classes/sun/dyn/InvokeGeneric.java index 8137bb4a67aa83157903c18d4cc8bb7bd84c3654..0d1a5defce3abf84a350b9c934096d5e41c642a6 100644 --- a/src/share/classes/sun/dyn/InvokeGeneric.java +++ b/src/share/classes/sun/dyn/InvokeGeneric.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,6 +28,7 @@ package sun.dyn; import java.dyn.*; import java.lang.reflect.*; import sun.dyn.util.*; +import static sun.dyn.MethodTypeImpl.invokers; /** * Adapters which manage MethodHanndle.invokeGeneric calls. @@ -43,7 +44,7 @@ class InvokeGeneric { /** Compute and cache information for this adapter, so that it can * call out to targets of the erasure-family of the given erased type. */ - private InvokeGeneric(MethodType erasedCallerType) throws NoAccessException { + private InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException { this.erasedCallerType = erasedCallerType; this.initialInvoker = makeInitialInvoker(); assert initialInvoker.type().equals(erasedCallerType @@ -63,14 +64,14 @@ class InvokeGeneric { try { InvokeGeneric gen = new InvokeGeneric(form.erasedType()); form.genericInvoker = genericInvoker = gen.initialInvoker; - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } return genericInvoker; } - private MethodHandle makeInitialInvoker() throws NoAccessException { + private MethodHandle makeInitialInvoker() throws ReflectiveOperationException { // postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)} MethodHandle postDispatch = makePostDispatchInvoker(); MethodHandle invoker; @@ -87,14 +88,14 @@ class InvokeGeneric { private MethodHandle makePostDispatchInvoker() { // Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...). MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS); - return MethodHandles.exactInvoker(invokerType); + return invokers(invokerType).exactInvoker(); } private MethodHandle dropDispatchArguments(MethodHandle targetInvoker) { assert(targetInvoker.type().parameterType(0) == MethodHandle.class); return MethodHandles.dropArguments(targetInvoker, 1, EXTRA_ARGS); } - private MethodHandle dispatcher(String dispatchName) throws NoAccessException { + private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException { return lookup().bind(this, dispatchName, MethodType.methodType(MethodHandle.class, MethodType.class, MethodHandle.class)); @@ -108,7 +109,7 @@ class InvokeGeneric { */ private MethodHandle dispatch(MethodType callerType, MethodHandle target) { MethodType targetType = target.type(); - if (USE_AS_TYPE_PATH || target instanceof AdapterMethodHandle.WithTypeHandler) { + if (USE_AS_TYPE_PATH || target.isVarargsCollector()) { MethodHandle newTarget = target.asType(callerType); targetType = callerType; Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType); diff --git a/src/share/classes/sun/dyn/Invokers.java b/src/share/classes/sun/dyn/Invokers.java index 35a8d2e4b55979ceca535ad4e372ec6f810f37fd..55eef1911ba7b652a1fd4611940b78d847e85f55 100644 --- a/src/share/classes/sun/dyn/Invokers.java +++ b/src/share/classes/sun/dyn/Invokers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -47,7 +47,7 @@ public class Invokers { private /*lazy*/ MethodHandle genericInvoker; // generic (untyped) invoker for the outgoing call; accepts a single Object[] - private final /*lazy*/ MethodHandle[] varargsInvokers; + private final /*lazy*/ MethodHandle[] spreadInvokers; // invoker for an unbound callsite private /*lazy*/ MethodHandle uninitializedCallSite; @@ -55,10 +55,9 @@ public class Invokers { /** Compute and cache information common to all collecting adapters * that implement members of the erasure-family of the given erased type. */ - public Invokers(Access token, MethodType targetType) { - Access.check(token); + /*non-public*/ Invokers(MethodType targetType) { this.targetType = targetType; - this.varargsInvokers = new MethodHandle[targetType.parameterCount()+1]; + this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1]; } public static MethodType invokerType(MethodType targetType) { @@ -69,8 +68,8 @@ public class Invokers { MethodHandle invoker = exactInvoker; if (invoker != null) return invoker; try { - invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); - } catch (NoAccessException ex) { + invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType); + } catch (ReflectiveOperationException ex) { throw new InternalError("JVM cannot find invoker for "+targetType); } assert(invokerType(targetType) == invoker.type()); @@ -101,13 +100,12 @@ public class Invokers { return invoker; } - public MethodHandle varargsInvoker(int objectArgCount) { - MethodHandle vaInvoker = varargsInvokers[objectArgCount]; + public MethodHandle spreadInvoker(int objectArgCount) { + MethodHandle vaInvoker = spreadInvokers[objectArgCount]; if (vaInvoker != null) return vaInvoker; MethodHandle gInvoker = genericInvoker(); - MethodType vaType = MethodType.genericMethodType(objectArgCount, true); - vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType)); - varargsInvokers[objectArgCount] = vaInvoker; + vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount); + spreadInvokers[objectArgCount] = vaInvoker; return vaInvoker; } @@ -118,7 +116,7 @@ public class Invokers { if (invoker != null) return invoker; if (targetType.parameterCount() > 0) { MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount()); - Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0); + Invokers invokers0 = MethodTypeImpl.invokers(type0); invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(), 0, targetType.parameterList()); assert(invoker.type().equals(targetType)); @@ -130,7 +128,7 @@ public class Invokers { THROW_UCS = MethodHandleImpl.IMPL_LOOKUP .findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Empty.class)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } diff --git a/src/share/classes/sun/dyn/MemberName.java b/src/share/classes/sun/dyn/MemberName.java index f45d1603ebf157031a65966d15d82dc33e41e259..5e98b85ca6030f134a5216569ee9decb65de489f 100644 --- a/src/share/classes/sun/dyn/MemberName.java +++ b/src/share/classes/sun/dyn/MemberName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -451,8 +451,6 @@ public final class MemberName implements Member, Cloneable { return type.toString(); // class java.lang.String // else it is a field, method, or constructor StringBuilder buf = new StringBuilder(); - if (!isResolved()) - buf.append("*."); if (getDeclaringClass() != null) { buf.append(getName(clazz)); buf.append('.'); @@ -512,14 +510,24 @@ public final class MemberName implements Member, Cloneable { public static RuntimeException newIllegalArgumentException(String message) { return new IllegalArgumentException(message); } - public static NoAccessException newNoAccessException(MemberName name, Class lookupClass) { - return newNoAccessException("cannot access", name, lookupClass); + public static IllegalAccessException newNoAccessException(MemberName name, Object from) { + return newNoAccessException("cannot access", name, from); } - public static NoAccessException newNoAccessException(String message, - MemberName name, Class lookupClass) { + public static IllegalAccessException newNoAccessException(String message, + MemberName name, Object from) { message += ": " + name; - if (lookupClass != null) message += ", from " + lookupClass.getName(); - return new NoAccessException(message); + if (from != null) message += ", from " + from; + return new IllegalAccessException(message); + } + public static ReflectiveOperationException newNoAccessException(MemberName name) { + if (name.isResolved()) + return new IllegalAccessException(name.toString()); + else if (name.isConstructor()) + return new NoSuchMethodException(name.toString()); + else if (name.isMethod()) + return new NoSuchMethodException(name.toString()); + else + return new NoSuchFieldException(name.toString()); } public static Error uncaughtException(Exception ex) { Error err = new InternalError("uncaught exception"); @@ -643,14 +651,20 @@ public final class MemberName implements Member, Cloneable { /** Produce a resolved version of the given member. * Super types are searched (for inherited members) if {@code searchSupers} is true. * Access checking is performed on behalf of the given {@code lookupClass}. - * If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown. + * If lookup fails or access is not permitted, a {@linkplain ReflectiveOperationException} is thrown. * Otherwise a fresh copy of the given member is returned, with modifier bits filled in. */ - public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class lookupClass) throws NoAccessException { + public + + MemberName resolveOrFail(MemberName m, boolean searchSupers, Class lookupClass, + Class nsmClass) + throws IllegalAccessException, NoSuchMemberException { MemberName result = resolveOrNull(m, searchSupers, lookupClass); if (result != null) return result; - throw newNoAccessException(m, lookupClass); + ReflectiveOperationException ex = newNoAccessException(m); + if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex; + throw nsmClass.cast(ex); } /** Return a list of all methods defined by the given class. * Super types are searched (for inherited members) if {@code searchSupers} is true. diff --git a/src/share/classes/sun/dyn/MethodHandleImpl.java b/src/share/classes/sun/dyn/MethodHandleImpl.java index 24f7aae73a9d2fa68dc33816a16e64b861ba3d01..eab4923540e73672c69571b2a2da93480888375a 100644 --- a/src/share/classes/sun/dyn/MethodHandleImpl.java +++ b/src/share/classes/sun/dyn/MethodHandleImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -30,7 +30,6 @@ import java.dyn.MethodHandles.Lookup; import java.util.logging.Level; import java.util.logging.Logger; import sun.dyn.util.VerifyType; -import java.dyn.NoAccessException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -136,6 +135,8 @@ public abstract class MethodHandleImpl { } static { + if (!MethodHandleNatives.JVM_SUPPORT) // force init of native API + throw new InternalError("No JVM support for JSR 292"); // Force initialization of Lookup, so it calls us back as initLookup: MethodHandles.publicLookup(); if (IMPL_LOOKUP_INIT == null) @@ -167,11 +168,11 @@ public abstract class MethodHandleImpl { * @param doDispatch whether the method handle will test the receiver type * @param lookupClass access-check relative to this class * @return a direct handle to the matching method - * @throws NoAccessException if the given method cannot be accessed by the lookup class + * @throws IllegalAccessException if the given method cannot be accessed by the lookup class */ public static MethodHandle findMethod(Access token, MemberName method, - boolean doDispatch, Class lookupClass) throws NoAccessException { + boolean doDispatch, Class lookupClass) throws IllegalAccessException { Access.check(token); // only trusted calls MethodType mtype = method.getMethodType(); if (!method.isStatic()) { @@ -184,7 +185,10 @@ public abstract class MethodHandleImpl { if (!mh.isValid()) throw newNoAccessException(method, lookupClass); assert(mh.type() == mtype); - return mh; + if (!method.isVarargs()) + return mh; + else + return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1)); } public static @@ -302,7 +306,7 @@ public abstract class MethodHandleImpl { MethodHandle invoke = null; try { invoke = lookup.findVirtual(AllocateObject.class, name, MethodType.genericMethodType(nargs)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (invoke == null) break; invokes.add(invoke); @@ -317,7 +321,7 @@ public abstract class MethodHandleImpl { static { try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } } @@ -469,7 +473,7 @@ public abstract class MethodHandleImpl { MethodHandle mh; try { mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } if (evclass != vclass || (!isStatic && ecclass != cclass)) { @@ -537,7 +541,7 @@ public abstract class MethodHandleImpl { MethodHandle mh; try { mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } if (caclass != null) { @@ -1009,7 +1013,7 @@ public abstract class MethodHandleImpl { MethodHandle invoke = null; try { invoke = lookup.findVirtual(GuardWithTest.class, name, MethodType.genericMethodType(nargs)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (invoke == null) break; invokes.add(invoke); @@ -1024,7 +1028,7 @@ public abstract class MethodHandleImpl { static { try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } } @@ -1145,7 +1149,7 @@ public abstract class MethodHandleImpl { MethodHandle invoke = null; try { invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (invoke == null) break; invokes.add(invoke); @@ -1160,7 +1164,7 @@ public abstract class MethodHandleImpl { static { try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } } @@ -1207,20 +1211,30 @@ public abstract class MethodHandleImpl { THROW_EXCEPTION = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", MethodType.methodType(Empty.class, Throwable.class)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } static Empty throwException(T t) throws T { throw t; } - public static String getNameString(Access token, MethodHandle target) { + public static String getNameString(Access token, MethodHandle target, Object type) { Access.check(token); + if (!(type instanceof MethodType)) { + if (type == null) + type = target.type(); + else if (type instanceof MethodHandle) + type = ((MethodHandle)type).type(); + } MemberName name = null; if (target != null) name = MethodHandleNatives.getMethodName(target); if (name == null) - return "invoke" + target.type(); - return name.getName() + target.type(); + return "invoke" + type; + return name.getName() + type; + } + + public static String getNameString(Access token, MethodHandle target) { + return getNameString(token, target, null); } static String addTypeString(Object obj, MethodHandle target) { @@ -1263,8 +1277,8 @@ public abstract class MethodHandleImpl { return MethodHandleNatives.getBootstrap(callerClass); } - public static MethodHandle withTypeHandler(Access token, MethodHandle target, MethodHandle typeHandler) { + public static MethodHandle asVarargsCollector(Access token, MethodHandle target, Class arrayType) { Access.check(token); - return AdapterMethodHandle.makeTypeHandler(token, target, typeHandler); + return AdapterMethodHandle.makeVarargsCollector(token, target, arrayType); } } diff --git a/src/share/classes/sun/dyn/MethodHandleNatives.java b/src/share/classes/sun/dyn/MethodHandleNatives.java index 3d0629dd034455cfe662dbbcda686fe0903835cb..5f1076bff6aa6143b8a158c6a2eb13f433fd67ef 100644 --- a/src/share/classes/sun/dyn/MethodHandleNatives.java +++ b/src/share/classes/sun/dyn/MethodHandleNatives.java @@ -350,7 +350,7 @@ class MethodHandleNatives { case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type ); } throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { Error err = new IncompatibleClassChangeError(); err.initCause(ex); throw err; diff --git a/src/share/classes/sun/dyn/MethodTypeImpl.java b/src/share/classes/sun/dyn/MethodTypeImpl.java index 62276dff3b0feea7f3eac9b8c4ebe622ec143a15..700ed307f74be0f7dd3cbbc9a6cbdc144a9121e4 100644 --- a/src/share/classes/sun/dyn/MethodTypeImpl.java +++ b/src/share/classes/sun/dyn/MethodTypeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -498,9 +498,12 @@ public class MethodTypeImpl { public static Invokers invokers(Access token, MethodType type) { Access.check(token); + return invokers(type); + } + /*non-public*/ static Invokers invokers(MethodType type) { Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type); if (inv != null) return inv; - inv = new Invokers(token, type); + inv = new Invokers(type); METHOD_TYPE_FRIEND.setInvokers(type, inv); return inv; } diff --git a/src/share/classes/sun/dyn/SpreadGeneric.java b/src/share/classes/sun/dyn/SpreadGeneric.java index 5a50e0195a1ebe6fd7e02fd7fdfa7951fda63968..4c6a0800bcc45fe066610abc27fbb1c50ae5d275 100644 --- a/src/share/classes/sun/dyn/SpreadGeneric.java +++ b/src/share/classes/sun/dyn/SpreadGeneric.java @@ -167,7 +167,7 @@ class SpreadGeneric { MethodHandle entryPoint = null; try { entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (entryPoint == null) continue; Constructor ctor = null; diff --git a/src/share/classes/sun/dyn/ToGeneric.java b/src/share/classes/sun/dyn/ToGeneric.java index 40ac10faf07dd1e1798a60f17d449193c3665a7b..38db3fea42fe2434c61c613047ea9cf781d0da7b 100644 --- a/src/share/classes/sun/dyn/ToGeneric.java +++ b/src/share/classes/sun/dyn/ToGeneric.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, 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 @@ -31,6 +31,7 @@ import java.lang.reflect.InvocationTargetException; import sun.dyn.util.ValueConversions; import sun.dyn.util.Wrapper; import static sun.dyn.MemberName.newIllegalArgumentException; +import static sun.dyn.MethodTypeImpl.invokers; /** * Adapters which mediate between incoming calls which are not generic @@ -72,7 +73,7 @@ class ToGeneric { assert(entryType.erase() == entryType); // for now // incoming call will first "forget" all reference types except Object this.entryType = entryType; - MethodHandle invoker0 = MethodHandles.exactInvoker(entryType.generic()); + MethodHandle invoker0 = invokers(entryType.generic()).exactInvoker(); MethodType rawEntryTypeInit; Adapter ad = findAdapter(rawEntryTypeInit = entryType); if (ad != null) { @@ -284,7 +285,7 @@ class ToGeneric { try { entryPoint = MethodHandleImpl.IMPL_LOOKUP. findSpecial(acls, iname, entryPointType, acls); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (entryPoint == null) continue; Constructor ctor = null; diff --git a/src/share/classes/sun/dyn/WrapperInstance.java b/src/share/classes/sun/dyn/WrapperInstance.java new file mode 100644 index 0000000000000000000000000000000000000000..d6e91fd0d03b5ebe86d444fdaa20be878ebccc4b --- /dev/null +++ b/src/share/classes/sun/dyn/WrapperInstance.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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.dyn; + +import java.dyn.MethodHandle; + +/** + * Private API used inside of java.dyn.MethodHandles. + * Interface implemented by every object which is produced by + * {@link java.dyn.MethodHandles#asInstance MethodHandles.asInstance}. + * The methods of this interface allow a caller to recover the parameters + * to {@code asInstance}. + * This allows applications to repeatedly convert between method handles + * and SAM objects, without the risk of creating unbounded delegation chains. + */ +public interface WrapperInstance { + /** Produce or recover a target method handle which is behaviorally + * equivalent to the SAM method of this object. + */ + public MethodHandle getWrapperInstanceTarget(); + /** Recover the SAM type for which this object was created. + */ + public Class getWrapperInstanceType(); +} + diff --git a/src/share/classes/sun/dyn/util/ValueConversions.java b/src/share/classes/sun/dyn/util/ValueConversions.java index 29e75eccedebe8b90abff653b1ea8608e188ec49..374bd245058a473b396868c98566b7433badc4af 100644 --- a/src/share/classes/sun/dyn/util/ValueConversions.java +++ b/src/share/classes/sun/dyn/util/ValueConversions.java @@ -153,7 +153,7 @@ public class ValueConversions { try { // actually, type is wrong; the Java method takes Object mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { mh = null; } } else { @@ -289,7 +289,7 @@ public class ValueConversions { if (exact) { try { mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { mh = null; } } else { @@ -408,7 +408,7 @@ public class ValueConversions { if (exact) { try { mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { mh = null; } } else { @@ -492,7 +492,7 @@ public class ValueConversions { case INT: case LONG: case FLOAT: case DOUBLE: try { mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { mh = null; } break; @@ -654,7 +654,7 @@ public class ValueConversions { type = type.appendParameterTypes(wrap.primitiveType()); try { mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { mh = null; } if (mh == null && wrap == Wrapper.VOID) { @@ -723,7 +723,7 @@ public class ValueConversions { MethodHandle array = null; try { array = lookup.findStatic(ValueConversions.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (array == null) break; arrays.add(array); @@ -784,7 +784,7 @@ public class ValueConversions { MethodHandle array = null; try { array = lookup.findStatic(ValueConversions.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { } if (array == null) break; arrays.add(array); diff --git a/src/share/classes/sun/dyn/util/VerifyAccess.java b/src/share/classes/sun/dyn/util/VerifyAccess.java index 88fdafbc310512031762434d680e1edc24da3684..1114bad269509788a0c747bb068155b6a07a4f55 100644 --- a/src/share/classes/sun/dyn/util/VerifyAccess.java +++ b/src/share/classes/sun/dyn/util/VerifyAccess.java @@ -25,7 +25,6 @@ package sun.dyn.util; -import java.dyn.NoAccessException; import java.lang.reflect.Modifier; import sun.dyn.MemberName; import sun.dyn.MethodHandleImpl; @@ -139,6 +138,8 @@ public class VerifyAccess { *

  • C is public. *
  • C and D are members of the same runtime package. *
+ * @param refc the symbolic reference class to which access is being checked (C) + * @param lookupClass the class performing the lookup (D) */ public static boolean isClassAccessible(Class refc, Class lookupClass) { int mods = refc.getModifiers(); diff --git a/src/share/classes/sun/misc/JarIndex.java b/src/share/classes/sun/misc/JarIndex.java index 39e2eca91d83b8f28536bff729805a97b1ae5b07..f9781d65a0dd5ec41bef283883df8bdd1495e544 100644 --- a/src/share/classes/sun/misc/JarIndex.java +++ b/src/share/classes/sun/misc/JarIndex.java @@ -103,6 +103,19 @@ public class JarIndex { parseJars(files); } + /** + * Returns the jar index, or null if none. + * + * This single parameter version of the method is retained + * for binary compatibility with earlier releases. + * + * @param jar the JAR file to get the index from. + * @exception IOException if an I/O error has occurred. + */ + public static JarIndex getJarIndex(JarFile jar) throws IOException { + return getJarIndex(jar, null); + } + /** * Returns the jar index, or null if none. * diff --git a/src/share/classes/sun/misc/JavaUtilJarAccess.java b/src/share/classes/sun/misc/JavaUtilJarAccess.java index e5e6b608ee88f2d302e20654e4f40ebe8188df75..0f1efd1d2cb52fb20d6fb00fb0f2a75d208c4bff 100644 --- a/src/share/classes/sun/misc/JavaUtilJarAccess.java +++ b/src/share/classes/sun/misc/JavaUtilJarAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -26,8 +26,19 @@ package sun.misc; import java.io.IOException; +import java.net.URL; +import java.security.CodeSource; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; import java.util.jar.JarFile; public interface JavaUtilJarAccess { public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException; + public CodeSource[] getCodeSources(JarFile jar, URL url); + public CodeSource getCodeSource(JarFile jar, URL url, String name); + public Enumeration entryNames(JarFile jar, CodeSource[] cs); + public Enumeration entries2(JarFile jar); + public void setEagerValidation(JarFile jar, boolean eager); + public List getManifestDigests(JarFile jar); } diff --git a/src/share/classes/sun/net/www/protocol/jar/URLJarFile.java b/src/share/classes/sun/net/www/protocol/jar/URLJarFile.java index 36acd9cc381a9e814cdd9a691ec756edc45a650d..f32141d5030efa094c2a61ddae8140c7ffd05ce4 100644 --- a/src/share/classes/sun/net/www/protocol/jar/URLJarFile.java +++ b/src/share/classes/sun/net/www/protocol/jar/URLJarFile.java @@ -27,6 +27,9 @@ package sun.net.www.protocol.jar; import java.io.*; import java.net.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.*; import java.util.jar.*; import java.util.zip.ZipFile; @@ -208,38 +211,23 @@ public class URLJarFile extends JarFile { JarFile result = null; /* get the stream before asserting privileges */ - final InputStream in = url.openConnection().getInputStream(); - - try { + try (final InputStream in = url.openConnection().getInputStream()) { result = AccessController.doPrivileged( new PrivilegedExceptionAction() { public JarFile run() throws IOException { - OutputStream out = null; - File tmpFile = null; + Path tmpFile = Files.createTempFile("jar_cache", null); try { - tmpFile = File.createTempFile("jar_cache", null); - tmpFile.deleteOnExit(); - out = new FileOutputStream(tmpFile); - int read = 0; - byte[] buf = new byte[BUF_SIZE]; - while ((read = in.read(buf)) != -1) { - out.write(buf, 0, read); - } - out.close(); - out = null; - return new URLJarFile(tmpFile, closeController); - } catch (IOException e) { - if (tmpFile != null) { - tmpFile.delete(); - } - throw e; - } finally { - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); + Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); + JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController); + tmpFile.toFile().deleteOnExit(); + return jarFile; + } catch (Throwable thr) { + try { + Files.delete(tmpFile); + } catch (IOException ioe) { + thr.addSuppressed(ioe); } + throw thr; } } }); diff --git a/src/share/classes/sun/nio/ch/FileChannelImpl.java b/src/share/classes/sun/nio/ch/FileChannelImpl.java index e9344cdab6408f3d8cd4df7e747618de19870d96..b15086eab12282ee663d15d143ba04fb91597c92 100644 --- a/src/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/share/classes/sun/nio/ch/FileChannelImpl.java @@ -51,6 +51,7 @@ public class FileChannelImpl // File access mode (immutable) private final boolean writable; private final boolean readable; + private final boolean append; // Required to prevent finalization of creating stream (immutable) private final Object parent; @@ -67,6 +68,7 @@ public class FileChannelImpl this.fd = fd; this.readable = readable; this.writable = writable; + this.append = append; this.parent = parent; this.nd = new FileDispatcherImpl(append); } @@ -242,7 +244,8 @@ public class FileChannelImpl if (!isOpen()) return 0; do { - p = position0(fd, -1); + // in append-mode then position is advanced to end before writing + p = (append) ? nd.size(fd) : position0(fd, -1); } while ((p == IOStatus.INTERRUPTED) && isOpen()); return IOStatus.normalize(p); } finally { diff --git a/src/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java b/src/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java index 328aadf7f5d8b4cb1f185d58c47660d970348303..c7694023d6b00cca54657666378007af2a853bb5 100644 --- a/src/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java +++ b/src/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java @@ -87,6 +87,7 @@ public final class SunNativeProvider extends Provider { gssLibs = new String[]{ "libgssapi.so", "libgssapi_krb5.so", + "libgssapi_krb5.so.2", }; } } else { diff --git a/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java b/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java index a4f83c87bbbeac0ca38a2c65e08f04fcc855ee1b..63989de46eed7d3e61ccdce79a101f6300bcef74 100644 --- a/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java +++ b/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java @@ -231,13 +231,6 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { AdaptableX509CertSelector issuerSelector = new AdaptableX509CertSelector(); - // check trusted certificate's key usage - boolean[] usages = trustedCert.getKeyUsage(); - if (usages != null) { - usages[5] = true; // keyCertSign - issuerSelector.setKeyUsage(usages); - } - // check trusted certificate's subject issuerSelector.setSubject(firstCert.getIssuerX500Principal()); diff --git a/src/share/classes/sun/security/util/SignatureFileVerifier.java b/src/share/classes/sun/security/util/SignatureFileVerifier.java index d3ce9013e1570a1bdf90a597e605f9fcf6526fdc..f60fcb75f8b0e97374d163dc2e0770e54023ceaf 100644 --- a/src/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/share/classes/sun/security/util/SignatureFileVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -181,7 +181,8 @@ public class SignatureFileVerifier { * * */ - public void process(Hashtable signers) + public void process(Hashtable signers, + List manifestDigests) throws IOException, SignatureException, NoSuchAlgorithmException, JarException, CertificateException { @@ -190,14 +191,15 @@ public class SignatureFileVerifier { Object obj = null; try { obj = Providers.startJarVerification(); - processImpl(signers); + processImpl(signers, manifestDigests); } finally { Providers.stopJarVerification(obj); } } - private void processImpl(Hashtable signers) + private void processImpl(Hashtable signers, + List manifestDigests) throws IOException, SignatureException, NoSuchAlgorithmException, JarException, CertificateException { @@ -232,7 +234,7 @@ public class SignatureFileVerifier { sf.getEntries().entrySet().iterator(); // see if we can verify the whole manifest first - boolean manifestSigned = verifyManifestHash(sf, md, decoder); + boolean manifestSigned = verifyManifestHash(sf, md, decoder, manifestDigests); // verify manifest main attributes if (!manifestSigned && !verifyManifestMainAttrs(sf, md, decoder)) { @@ -275,7 +277,8 @@ public class SignatureFileVerifier { */ private boolean verifyManifestHash(Manifest sf, ManifestDigester md, - BASE64Decoder decoder) + BASE64Decoder decoder, + List manifestDigests) throws IOException { Attributes mattr = sf.getMainAttributes(); @@ -290,6 +293,8 @@ public class SignatureFileVerifier { // 16 is length of "-Digest-Manifest" String algorithm = key.substring(0, key.length()-16); + manifestDigests.add(key); + manifestDigests.add(se.getValue()); MessageDigest digest = getDigest(algorithm); if (digest != null) { byte[] computedHash = md.manifestDigest(digest); diff --git a/src/share/classes/sun/tools/jps/Jps.java b/src/share/classes/sun/tools/jps/Jps.java index 7850f9656c28bb46e5006c4fa6294135dc316e4a..9611dd3a007fafae7182eb717e1a07ce85592032 100644 --- a/src/share/classes/sun/tools/jps/Jps.java +++ b/src/share/classes/sun/tools/jps/Jps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -77,9 +77,52 @@ public class Jps { MonitoredVm vm = null; String vmidString = "//" + lvmid + "?mode=r"; + String errorString = null; + try { + // Note: The VM associated with the current VM id may + // no longer be running so these queries may fail. We + // already added the VM id to the output stream above. + // If one of the queries fails, then we try to add a + // reasonable message to indicate that the requested + // info is not available. + + errorString = " -- process information unavailable"; VmIdentifier id = new VmIdentifier(vmidString); vm = monitoredHost.getMonitoredVm(id, 0); + + errorString = " -- main class information unavailable"; + output.append(" " + MonitoredVmUtil.mainClass(vm, + arguments.showLongPaths())); + + if (arguments.showMainArgs()) { + errorString = " -- main args information unavailable"; + String mainArgs = MonitoredVmUtil.mainArgs(vm); + if (mainArgs != null && mainArgs.length() > 0) { + output.append(" " + mainArgs); + } + } + if (arguments.showVmArgs()) { + errorString = " -- jvm args information unavailable"; + String jvmArgs = MonitoredVmUtil.jvmArgs(vm); + if (jvmArgs != null && jvmArgs.length() > 0) { + output.append(" " + jvmArgs); + } + } + if (arguments.showVmFlags()) { + errorString = " -- jvm flags information unavailable"; + String jvmFlags = MonitoredVmUtil.jvmFlags(vm); + if (jvmFlags != null && jvmFlags.length() > 0) { + output.append(" " + jvmFlags); + } + } + + errorString = " -- detach failed"; + monitoredHost.detach(vm); + + System.out.println(output); + + errorString = null; } catch (URISyntaxException e) { // unexpected as vmidString is based on a validated hostid lastError = e; @@ -87,7 +130,7 @@ public class Jps { } catch (Exception e) { lastError = e; } finally { - if (vm == null) { + if (errorString != null) { /* * we ignore most exceptions, as there are race * conditions where a JVM in 'jvms' may terminate @@ -95,7 +138,7 @@ public class Jps { * Other errors, such as access and I/O exceptions * should stop us from iterating over the complete set. */ - output.append(" -- process information unavailable"); + output.append(errorString); if (arguments.isDebug()) { if ((lastError != null) && (lastError.getMessage() != null)) { @@ -110,33 +153,6 @@ public class Jps { continue; } } - - output.append(" "); - output.append(MonitoredVmUtil.mainClass(vm, - arguments.showLongPaths())); - - if (arguments.showMainArgs()) { - String mainArgs = MonitoredVmUtil.mainArgs(vm); - if (mainArgs != null && mainArgs.length() > 0) { - output.append(" ").append(mainArgs); - } - } - if (arguments.showVmArgs()) { - String jvmArgs = MonitoredVmUtil.jvmArgs(vm); - if (jvmArgs != null && jvmArgs.length() > 0) { - output.append(" ").append(jvmArgs); - } - } - if (arguments.showVmFlags()) { - String jvmFlags = MonitoredVmUtil.jvmFlags(vm); - if (jvmFlags != null && jvmFlags.length() > 0) { - output.append(" ").append(jvmFlags); - } - } - - System.out.println(output); - - monitoredHost.detach(vm); } } catch (MonitorException e) { if (e.getMessage() != null) { diff --git a/src/share/classes/sun/util/resources/TimeZoneNames.java b/src/share/classes/sun/util/resources/TimeZoneNames.java index 17d9fae84a96bad92ecd9ea7ac1f1cfabfa1a037..a5c73549809116fa322aaec2347605f67aef6161 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames.java @@ -405,6 +405,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_de.java b/src/share/classes/sun/util/resources/TimeZoneNames_de.java index 45705ec799d8e4ee05e858263332330a3eb46047..b3cb23224fadb61f3858f4eab98a836193e4c1f2 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_de.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_de.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_es.java b/src/share/classes/sun/util/resources/TimeZoneNames_es.java index eecb3e31e55727a857dd11e8ecc9e14e3d83b748..613cc6419fb1309315025f3424a4e539214f1bd1 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_es.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_es.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_fr.java b/src/share/classes/sun/util/resources/TimeZoneNames_fr.java index c39a001aacafa0b796305ed6aba0f16d939e4e60..f1dc5300c25815ae7cbe70a0af6754c852eb28e3 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_fr.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_fr.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_it.java b/src/share/classes/sun/util/resources/TimeZoneNames_it.java index 1bbfc5d44a0d3d5bd9eef29adadb415730ad40de..e983872e7a169656012273a9540980f6b048bc65 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_it.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_it.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_ja.java b/src/share/classes/sun/util/resources/TimeZoneNames_ja.java index f38a8e025379e227f9dbac75d18bfdc734a75f1e..56330acf91dd54f2d25b92e7d430cd5db720dd11 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_ja.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_ja.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_ko.java b/src/share/classes/sun/util/resources/TimeZoneNames_ko.java index 5186d42ef99dece4ed2286893c2a12c32a5419b4..82edcce1ec58a038f988ce31b833a1a0383bfbab 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_ko.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_ko.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_pt_BR.java b/src/share/classes/sun/util/resources/TimeZoneNames_pt_BR.java index 981281217f75fa6100512706484b4b40d8ffb637..d9b83fe04912aadec05858760fefd10899ff4f73 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_pt_BR.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_pt_BR.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Panama", EST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_sv.java b/src/share/classes/sun/util/resources/TimeZoneNames_sv.java index 2813a181680432098097e9fca9c4a8062fefc38d..14108065241cdb438ef418ad291110b37cd6616f 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_sv.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_sv.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java b/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java index c79c6f2f69c09b7cc81f63dd49f51e8be72b5ceb..4f80a1b739cf8cc4d2cab35e0e20f588159baf1a 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java b/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java index 679ce531f83a02af86f69a486eafbe94f1a176f4..5218bed7ed6b097ebd0e3af1e3b114f61d1755fe 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java @@ -405,6 +405,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"America/Nipigon", EST}, {"America/Nome", AKST}, {"America/Noronha", NORONHA}, + {"America/North_Dakota/Beulah", CST}, {"America/North_Dakota/Center", CST}, {"America/North_Dakota/New_Salem", CST}, {"America/Ojinaga", MST}, diff --git a/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystemProvider.java b/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystemProvider.java index 60c8fed9291ae1988098a404a24bc35b44a63dc7..d8aa305d29b1ab1be0913966f75656406d3a6a54 100644 --- a/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystemProvider.java +++ b/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystemProvider.java @@ -42,6 +42,7 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.zip.ZipError; import java.util.concurrent.ExecutorService; /* @@ -78,42 +79,63 @@ public class ZipFileSystemProvider extends FileSystemProvider { } } - @Override - public FileSystem newFileSystem(URI uri, Map env) - throws IOException - { - return newFileSystem(uriToPath(uri), env, true); - } - - @Override - public FileSystem newFileSystem(Path path, Map env) - throws IOException - { - if (!path.toUri().getScheme().equalsIgnoreCase("file")) { - throw new UnsupportedOperationException(); + private boolean ensureFile(Path path) { + try { + BasicFileAttributes attrs = + Files.readAttributes(path, BasicFileAttributes.class); + if (!attrs.isRegularFile()) + throw new UnsupportedOperationException(); + return true; + } catch (IOException ioe) { + return false; } - return newFileSystem(path, env, false); } - private FileSystem newFileSystem(Path path, Map env, boolean checkIfFSExists) + @Override + public FileSystem newFileSystem(URI uri, Map env) throws IOException { + Path path = uriToPath(uri); synchronized(filesystems) { Path realPath = null; - if (checkIfFSExists && Files.exists(path)) { + if (ensureFile(path)) { realPath = path.toRealPath(true); if (filesystems.containsKey(realPath)) throw new FileSystemAlreadyExistsException(); } - ZipFileSystem zipfs = new ZipFileSystem(this, path, env); - if (realPath == null) - realPath = path.toRealPath(true); - if (!filesystems.containsKey(realPath)) - filesystems.put(realPath, zipfs); + ZipFileSystem zipfs = null; + try { + zipfs = new ZipFileSystem(this, path, env); + } catch (ZipError ze) { + String pname = path.toString(); + if (pname.endsWith(".zip") || pname.endsWith(".jar")) + throw ze; + // assume NOT a zip/jar file + throw new UnsupportedOperationException(); + } + filesystems.put(realPath, zipfs); return zipfs; } } + @Override + public FileSystem newFileSystem(Path path, Map env) + throws IOException + { + if (path.getFileSystem() != FileSystems.getDefault()) { + throw new UnsupportedOperationException(); + } + ensureFile(path); + try { + return new ZipFileSystem(this, path, env); + } catch (ZipError ze) { + String pname = path.toString(); + if (pname.endsWith(".zip") || pname.endsWith(".jar")) + throw ze; + throw new UnsupportedOperationException(); + } + } + @Override public Path getPath(URI uri) { diff --git a/src/solaris/classes/sun/awt/X11/XDesktopPeer.java b/src/solaris/classes/sun/awt/X11/XDesktopPeer.java index ce6ae164154fd58e8551d657109e5f8f1a2bf5e1..740eabac485bd8d6986c8a0499cc297159e311f4 100644 --- a/src/solaris/classes/sun/awt/X11/XDesktopPeer.java +++ b/src/solaris/classes/sun/awt/X11/XDesktopPeer.java @@ -44,11 +44,27 @@ import java.awt.peer.DesktopPeer; public class XDesktopPeer implements DesktopPeer { private static boolean nativeLibraryLoaded = false; - static { - nativeLibraryLoaded = init(); + private static boolean initExecuted = false; + + private static void initWithLock(){ + XToolkit.awtLock(); + try { + if (!initExecuted) { + nativeLibraryLoaded = init(); + } + } finally { + initExecuted = true; + XToolkit.awtUnlock(); + } + } + + //package-private + XDesktopPeer(){ + initWithLock(); } static boolean isDesktopSupported() { + initWithLock(); return nativeLibraryLoaded; } @@ -83,12 +99,17 @@ public class XDesktopPeer implements DesktopPeer { } private void launch(URI uri) throws IOException { - if (!nativeLibraryLoaded) { - throw new IOException("Failed to load native libraries."); - } - byte[] uriByteArray = ( uri.toString() + '\0' ).getBytes(); - boolean result = gnome_url_show(uriByteArray); + boolean result = false; + XToolkit.awtLock(); + try { + if (!nativeLibraryLoaded) { + throw new IOException("Failed to load native libraries."); + } + result = gnome_url_show(uriByteArray); + } finally { + XToolkit.awtUnlock(); + } if (!result) { throw new IOException("Failed to show URI:" + uri); } diff --git a/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java b/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java index 7bb58c736389a8781284fa83e908fce81382e4af..21c69ae4f4c16130c5ff20308d2cac96158dd122 100644 --- a/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java +++ b/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java @@ -136,7 +136,7 @@ class UnixChannelFactory { throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); FileDescriptor fdObj = open(dfd, path, pathForPermissionCheck, flags, mode); - return FileChannelImpl.open(fdObj, flags.read, flags.write, null); + return FileChannelImpl.open(fdObj, flags.read, flags.write, flags.append, null); } /** diff --git a/src/solaris/native/sun/xawt/awt_Desktop.c b/src/solaris/native/sun/xawt/awt_Desktop.c index d9f9be2bf1c995ff101cb12292b530f073a90773..69b9207bc99092cd49a372d4144ffc31e94ebb45 100644 --- a/src/solaris/native/sun/xawt/awt_Desktop.c +++ b/src/solaris/native/sun/xawt/awt_Desktop.c @@ -48,9 +48,15 @@ int init(){ } dlerror(); /* Clear errors */ gnome_vfs_init = (GNOME_VFS_INIT_TYPE*)dlsym(vfs_handle, "gnome_vfs_init"); + if (gnome_vfs_init == NULL){ +#ifdef INTERNAL_BUILD + fprintf(stderr, "dlsym( gnome_vfs_init) returned NULL\n"); +#endif + return 0; + } if ((errmsg = dlerror()) != NULL) { #ifdef INTERNAL_BUILD - fprintf(stderr, "can not find symble gnome_vfs_init\n"); + fprintf(stderr, "can not find symbol gnome_vfs_init %s \n", errmsg); #endif return 0; } diff --git a/src/windows/classes/sun/print/Win32PrintService.java b/src/windows/classes/sun/print/Win32PrintService.java index 6e60888092f295e7e77d45980c93f573c5c19da7..e9f1eb7df82bba6c9676f6f6527bd4a3bfa62851 100644 --- a/src/windows/classes/sun/print/Win32PrintService.java +++ b/src/windows/classes/sun/print/Win32PrintService.java @@ -273,6 +273,20 @@ public class Win32PrintService implements PrintService, AttributeUpdater, return DMPAPER_B6_JIS; } } + + // If not found in predefined Windows ID, then we search through + // the returned IDs of the driver because they can define their own + // unique IDs. + initMedia(); + + if ((idList != null) && (mediaSizes != null) && + (idList.size() == mediaSizes.length)) { + for (int i=0; i< idList.size(); i++) { + if (mediaSizes[i].getMediaSizeName() == msn) { + return ((Integer)idList.get(i)).intValue(); + } + } + } return 0; } @@ -439,7 +453,7 @@ public class Win32PrintService implements PrintService, AttributeUpdater, if (mediaName != null) { int defPaper = findPaperID(mediaName); - float[] prnArea = getMediaPrintableArea(printer, defPaper); + float[] prnArea = (defPaper != 0) ? getMediaPrintableArea(printer, defPaper) : null; MediaPrintableArea printableArea = null; if (prnArea != null) { try { diff --git a/test/ProblemList.txt b/test/ProblemList.txt index f2380764a1aed4e8ea3dacb3be281338c092d671..46b8cc1e8554a2cfcdca575bb95de0347eebf27e 100644 --- a/test/ProblemList.txt +++ b/test/ProblemList.txt @@ -41,14 +41,14 @@ # # Shell tests are othervm by default. # -# List items are testnames followed by labels, all MUST BE commented +# List items are testnames followed by labels, all MUST BE commented # as to why they are here and use a label: # generic-all Problems on all platforms # generic-ARCH Where ARCH is one of: sparc, sparcv9, x64, i586, etc. # OSNAME-all Where OSNAME is one of: solaris, linux, windows # OSNAME-ARCH Specific on to one OSNAME and ARCH, e.g. solaris-x64 # OSNAME-REV Specific on to one OSNAME and REV, e.g. solaris-5.8 -# +# # More than one label is allowed but must be on the same line. # ############################################################################# @@ -234,7 +234,7 @@ javax/management/remote/mandatory/threads/ExecutorTest.java generic-all # Linux 32bit Fedora 9, IllegalStateException javax/management/monitor/RuntimeExceptionTest.java generic-all -# Problems with rmi connection, othervm +# Problems with rmi connection, othervm javax/management/remote/mandatory/subjectDelegation/SubjectDelegation2Test.java generic-all # Fails with port already in use @@ -411,7 +411,7 @@ com/sun/nio/sctp/SctpChannel/SocketOptionTests.java com/sun/nio/sctp/SctpChannel/Send.java generic-all com/sun/nio/sctp/SctpChannel/Shutdown.java generic-all -# Fails on OpenSolaris, IllegalStateException: Cannot add or remove addresses +# Fails on OpenSolaris, IllegalStateException: Cannot add or remove addresses # from a channel that is bound to the wildcard address com/sun/nio/sctp/SctpChannel/Bind.java generic-all @@ -456,10 +456,10 @@ java/rmi/transport/pinLastArguments/PinLastArguments.java generic-all java/rmi/server/RemoteServer/AddrInUse.java generic-all # Connection error on Windows i586 -server -# Also connection errors in othervm on Solaris 10 sparc, same port??? +# Also connection errors in othervm on Solaris 10 sparc, same port??? sun/rmi/transport/tcp/DeadCachedConnection.java generic-all -# Connection errors in othervm on Solaris 10 sparc, same port??? +# Connection errors in othervm on Solaris 10 sparc, same port??? java/rmi/activation/Activatable/checkActivateRef/CheckActivateRef.java generic-all java/rmi/activation/Activatable/checkAnnotations/CheckAnnotations.java generic-all java/rmi/activation/Activatable/checkImplClassLoader/CheckImplClassLoader.java generic-all @@ -532,7 +532,7 @@ sun/security/pkcs11/ec/TestKeyFactory.java solaris-i586 java/security/Security/SynchronizedAccess.java generic-all # Failing on Solaris X64 (-d64 -server) with: -# GSSException: Failure unspecified at GSS-API level +# GSSException: Failure unspecified at GSS-API level # (Mechanism level: Specified version of key is not available (44)) sun/security/krb5/auto/BasicKrb5Test.java generic-all @@ -546,14 +546,14 @@ sun/security/tools/keytool/standard.sh generic-all sun/security/krb5/auto/HttpNegotiateServer.java generic-all # Fails on almost all platforms -# java.lang.UnsupportedClassVersionError: SerialTest : +# java.lang.UnsupportedClassVersionError: SerialTest : # Unsupported major.minor version 51.0 # at java.lang.ClassLoader.defineClass1(Native Method) sun/security/util/Oid/S11N.sh generic-all # Fails on Fedora 9 32bit -# GSSException: Failure unspecified at GSS-API level (Mechanism level: -# Invalid argument (400) - Cannot find key of appropriate type to decrypt +# GSSException: Failure unspecified at GSS-API level (Mechanism level: +# Invalid argument (400) - Cannot find key of appropriate type to decrypt # AP REP - DES CBC mode with MD5) # at sun.security.jgss.krb5.Krb5Context.acceptSecContext(Krb5Context.java:778) sun/security/krb5/auto/NonMutualSpnego.java generic-all @@ -673,7 +673,7 @@ sun/security/rsa/TestSignatures.java solaris-all # Timeout on solaris-sparc and i586 and x64, -client and -server sun/security/ssl/com/sun/net/ssl/internal/ssl/InputRecord/InterruptedIO.java solaris-all -# Do not seem to run on windows machines? dll missing? +# Do not seem to run on windows machines? dll missing? sun/security/tools/jarsigner/emptymanifest.sh windows-all # Files does not exist or no encoding? solaris-sparcv9 @@ -734,8 +734,5 @@ java/util/concurrent/FutureTask/BlockingTaskExecutor.java generic-all # Problems on windows, jmap.exe hangs? (these run jmap), fails on Solaris 10 x86 java/util/concurrent/locks/Lock/TimedAcquireLeak.java generic-all -# Fails on solaris-sparc -server (Set not equal to copy. 1) -java/util/EnumSet/EnumSetBash.java solaris-sparc - ############################################################################ diff --git a/test/demo/zipfs/ZipFSTester.java b/test/demo/zipfs/ZipFSTester.java index 14aebbdf685a3c78bd06ffc402ba957094f00c58..4969c21f361b336057c5fe665341f584e12ea6a4 100644 --- a/test/demo/zipfs/ZipFSTester.java +++ b/test/demo/zipfs/ZipFSTester.java @@ -105,6 +105,18 @@ public class ZipFSTester { os.write(bits); os.close(); + try { + provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(), + new HashMap()); + throw new RuntimeException("newFileSystem() opens a directory as zipfs"); + } catch (UnsupportedOperationException uoe) {} + + try { + provider.newFileSystem(src, new HashMap()); + throw new RuntimeException("newFileSystem() opens a non-zip file as zipfs"); + } catch (UnsupportedOperationException uoe) {} + + // copyin Path dst = getPathWithParents(fs, tmpName); Files.copy(src, dst); diff --git a/test/demo/zipfs/basic.sh b/test/demo/zipfs/basic.sh index 832a75d4fb428b2b0fa98e621df51f5783786828..06353dfa761cc2fcd346f1f19fc5c8b6c12c7119 100644 --- a/test/demo/zipfs/basic.sh +++ b/test/demo/zipfs/basic.sh @@ -21,7 +21,7 @@ # questions. # # @test -# @bug 6990846 7009092 7009085 7015391 7014948 7005986 7017840 +# @bug 6990846 7009092 7009085 7015391 7014948 7005986 7017840 7007596 # @summary Test ZipFileSystem demo # @build Basic PathOps ZipFSTester # @run shell basic.sh diff --git a/test/java/dyn/InvokeDynamicPrintArgs.java b/test/java/dyn/InvokeDynamicPrintArgs.java index 1bf1b73b197dcf31bcff7ac2c16922eaa3e69e45..233c4fc316cf9b30ba0f77af8d5238f5d336a58e 100644 --- a/test/java/dyn/InvokeDynamicPrintArgs.java +++ b/test/java/dyn/InvokeDynamicPrintArgs.java @@ -23,15 +23,19 @@ /* @test * @summary smoke test for invokedynamic instructions - * @library indify + * @build indify.Indify * @compile InvokeDynamicPrintArgs.java * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic * indify.Indify * --verify-specifier-count=3 --transitionalJSR292=false * --expand-properties --classpath ${test.classes} - * --java InvokeDynamicPrintArgs --check-output + * --java test.java.dyn.InvokeDynamicPrintArgs --check-output */ +package test.java.dyn; + +import org.junit.Test; + import java.util.*; import java.io.*; @@ -53,6 +57,20 @@ public class InvokeDynamicPrintArgs { closeBuf(); } + @Test + public void testInvokeDynamicPrintArgs() throws IOException { + System.err.println(System.getProperties()); + String testClassPath = System.getProperty("build.test.classes.dir"); + if (testClassPath == null) throw new RuntimeException(); + String[] args = new String[]{ + "--verify-specifier-count=3", "--transitionalJSR292=false", + "--expand-properties", "--classpath", testClassPath, + "--java", "test.java.dyn.InvokeDynamicPrintArgs", "--check-output" + }; + System.err.println("Indify: "+Arrays.toString(args)); + indify.Indify.main(args); + } + private static PrintStream oldOut; private static ByteArrayOutputStream buf; private static void openBuf() { @@ -79,11 +97,11 @@ public class InvokeDynamicPrintArgs { } private static final String[] EXPECT_OUTPUT = { "Printing some argument lists, starting with a empty one:", - "[InvokeDynamicPrintArgs, nothing, ()void][]", - "[InvokeDynamicPrintArgs, bar, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]", - "[InvokeDynamicPrintArgs, bar2, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]", - "[InvokeDynamicPrintArgs, baz, (java.lang.String,int,double)void, 1234.5][baz arg, 2, 3.14]", - "[InvokeDynamicPrintArgs, foo, (java.lang.String)void][foo arg]", + "[test.java.dyn.InvokeDynamicPrintArgs, nothing, ()void][]", + "[test.java.dyn.InvokeDynamicPrintArgs, bar, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]", + "[test.java.dyn.InvokeDynamicPrintArgs, bar2, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]", + "[test.java.dyn.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]", + "[test.java.dyn.InvokeDynamicPrintArgs, foo, (String)void][foo arg]", "Done printing argument lists." }; @@ -110,18 +128,15 @@ public class InvokeDynamicPrintArgs { return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm()); } - private static CallSite bsm2(Lookup caller, String name, MethodType type, Object arg) throws ReflectiveOperationException { + private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException { // ignore caller and name, but match the type: List bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type)); - if (arg instanceof Object[]) - bsmInfo.addAll(Arrays.asList((Object[])arg)); - else - bsmInfo.add(arg); + bsmInfo.addAll(Arrays.asList((Object[])arg)); return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type)); } private static MethodType MT_bsm2() { shouldNotCallThis(); - return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object.class); + return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class); } private static MethodHandle MH_bsm2() throws ReflectiveOperationException { shouldNotCallThis(); diff --git a/test/java/dyn/InvokeGenericTest.java b/test/java/dyn/InvokeGenericTest.java index 12670668c7e11dce205e994c308a74b142c57683..bae888060a0e8cdab125ac61123f99723e6558e3 100644 --- a/test/java/dyn/InvokeGenericTest.java +++ b/test/java/dyn/InvokeGenericTest.java @@ -320,7 +320,7 @@ public class InvokeGenericTest { MethodHandle callable(List> params) { MethodHandle mh = CALLABLES.get(params); if (mh == null) { - mh = collectArguments(collector_MH, methodType(Object.class, params)); + mh = collector_MH.asType(methodType(Object.class, params)); CALLABLES.put(params, mh); } return mh; @@ -338,7 +338,7 @@ public class InvokeGenericTest { = LOOKUP.findStatic(LOOKUP.lookupClass(), "collector", methodType(Object.class, Object[].class)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } diff --git a/test/java/dyn/JavaDocExamplesTest.java b/test/java/dyn/JavaDocExamplesTest.java index 9cb41f9fcec8336759aa8e93769498919174dc26..72a62851f82ed505a6476ff0e8e01ba9c1876dcb 100644 --- a/test/java/dyn/JavaDocExamplesTest.java +++ b/test/java/dyn/JavaDocExamplesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -74,7 +74,7 @@ static final private Lookup LOOKUP = lookup(); // static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class, // "hashCode", methodType(int.class)); -// form required if NoAccessException is intercepted: +// form required if ReflectiveOperationException is intercepted: static final private MethodHandle CONCAT_2, HASHCODE_2; static { try { @@ -82,7 +82,7 @@ static { "concat", methodType(String.class, String.class)); HASHCODE_2 = LOOKUP.findVirtual(Object.class, "hashCode", methodType(int.class)); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } @@ -142,37 +142,79 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY Assert.assertEquals(exp, act); } -static MethodHandle asList; - @Test public void testWithTypeHandler() throws Throwable { + @Test public void testMethodHandlesSummary() throws Throwable { {{ {} /// JAVADOC -MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList()); -MethodHandle asList = lookup() - .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); - -JavaDocExamplesTest.asList = asList; -/* -static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) { - return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType); -} -*/ - -MethodHandle collectingTypeHandler = lookup() - .findStatic(lookup().lookupClass(), "collectingTypeHandler", - methodType(MethodHandle.class, MethodHandle.class, MethodType.class)); -MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler); - -assertEquals("[]", makeAnyList.invokeGeneric().toString()); -assertEquals("[1]", makeAnyList.invokeGeneric(1).toString()); -assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString()); +Object x, y; String s; int i; +MethodType mt; MethodHandle mh; +MethodHandles.Lookup lookup = MethodHandles.lookup(); +// mt is (char,char)String +mt = MethodType.methodType(String.class, char.class, char.class); +mh = lookup.findVirtual(String.class, "replace", mt); +s = (String) mh.invokeExact("daddy",'d','n'); +// invokeExact(Ljava/lang/String;CC)Ljava/lang/String; +assert(s.equals("nanny")); +// weakly typed invocation (using MHs.invoke) +s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); +assert(s.equals("savvy")); +// mt is (Object[])List +mt = MethodType.methodType(java.util.List.class, Object[].class); +mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); +assert(mh.isVarargsCollector()); +x = mh.invokeGeneric("one", "two"); +// invokeGeneric(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); +mh = mh.asType(mt); +x = mh.invokeExact((Object)1, (Object)2, (Object)3); +// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +assert(x.equals(java.util.Arrays.asList(1,2,3))); +// mt is { => int} +mt = MethodType.methodType(int.class); +mh = lookup.findVirtual(java.util.List.class, "size", mt); +i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); +// invokeExact(Ljava/util/List;)I +assert(i == 3); +mt = MethodType.methodType(void.class, String.class); +mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); +mh.invokeExact(System.out, "Hello, world."); +// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V +{} }} } -static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) { - //System.out.println("Converting "+asList+" to "+newType); - MethodHandle conv = asList.asCollector(Object[].class, newType.parameterCount()).asType(newType); - //System.out.println(" =>"+conv); - return conv; -} + @Test public void testAsVarargsCollector() throws Throwable { + {{ +{} /// JAVADOC +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()); +Object[] argv = { "three", "thee", "tee" }; +assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString()); +List ls = (List) asList.invokeGeneric((Object)argv); +assertEquals(1, ls.size()); +assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); + }} + } + @Test public void testVarargsCollectorSuppression() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle vamh = publicLookup() + .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) + .asVarargsCollector(Object[].class); +MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh); +assert(vamh.type().equals(mh.type())); +assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString()); +boolean failed = false; +try { mh.invokeGeneric(1,2,3); } +catch (WrongMethodTypeException ex) { failed = true; } +assert(failed); +{} + }} + } } diff --git a/test/java/dyn/MethodHandlesTest.java b/test/java/dyn/MethodHandlesTest.java index e92bc6dbae477c823e249697dd7b5c5418119875..b44f262fa024cf387138aa911b83b70e92d36b21 100644 --- a/test/java/dyn/MethodHandlesTest.java +++ b/test/java/dyn/MethodHandlesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -323,6 +323,44 @@ public class MethodHandlesTest { return x.getClass().getSimpleName(); } + /** Return lambda(arg...[arity]) { new Object[]{ arg... } } */ + static MethodHandle varargsList(int arity) { + return ValueConversions.varargsList(arity); + } + /** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */ + static MethodHandle varargsArray(int arity) { + return ValueConversions.varargsArray(arity); + } + /** Variation of varargsList, but with the given rtype. */ + static MethodHandle varargsList(int arity, Class rtype) { + MethodHandle list = varargsList(arity); + MethodType listType = list.type().changeReturnType(rtype); + if (List.class.isAssignableFrom(rtype) || rtype == void.class || rtype == Object.class) { + // OK + } else if (rtype.isAssignableFrom(String.class)) { + if (LIST_TO_STRING == null) + try { + LIST_TO_STRING = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToString", + MethodType.methodType(String.class, List.class)); + } catch (Exception ex) { throw new RuntimeException(ex); } + list = MethodHandles.filterReturnValue(list, LIST_TO_STRING); + } else if (rtype.isPrimitive()) { + if (LIST_TO_INT == null) + try { + LIST_TO_INT = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToInt", + MethodType.methodType(int.class, List.class)); + } catch (Exception ex) { throw new RuntimeException(ex); } + list = MethodHandles.filterReturnValue(list, LIST_TO_INT); + list = MethodHandles.explicitCastArguments(list, listType); + } else { + throw new RuntimeException("varargsList: "+rtype); + } + return list.asType(listType); + } + private static MethodHandle LIST_TO_STRING, LIST_TO_INT; + private static String listToString(List x) { return x.toString(); } + private static int listToInt(List x) { return x.toString().hashCode(); } + static MethodHandle changeArgTypes(MethodHandle target, Class argType) { return changeArgTypes(target, 0, 999, argType); } @@ -458,8 +496,12 @@ public class MethodHandlesTest { try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); target = lookup.in(defc).findStatic(defc, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { noAccess = ex; + if (name.contains("bogus")) + assertTrue(noAccess instanceof NoSuchMethodException); + else + assertTrue(noAccess instanceof IllegalAccessException); } if (verbosity >= 3) System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target @@ -528,8 +570,12 @@ public class MethodHandlesTest { try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); target = lookup.in(defc).findVirtual(defc, methodName, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { noAccess = ex; + if (name.contains("bogus")) + assertTrue(noAccess instanceof NoSuchMethodException); + else + assertTrue(noAccess instanceof IllegalAccessException); } if (verbosity >= 3) System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target @@ -558,11 +604,12 @@ public class MethodHandlesTest { testFindSpecial(SubExample.class, Example.class, void.class, "v0"); testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0"); // Do some negative testing: + testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus"); + testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus"); for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) { testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0"); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "", int.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0"); - testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus"); } } @@ -583,8 +630,12 @@ public class MethodHandlesTest { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller)); target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { noAccess = ex; + if (name.contains("bogus")) + assertTrue(noAccess instanceof NoSuchMethodException); + else + assertTrue(noAccess instanceof IllegalAccessException); } if (verbosity >= 3) System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target @@ -639,8 +690,12 @@ public class MethodHandlesTest { try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); target = lookup.in(defc).bind(receiver, methodName, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { noAccess = ex; + if (name.contains("bogus")) + assertTrue(noAccess instanceof NoSuchMethodException); + else + assertTrue(noAccess instanceof IllegalAccessException); } if (verbosity >= 3) System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target @@ -698,14 +753,9 @@ public class MethodHandlesTest { Class defc, Class rcvc, Class ret, String name, Class... params) throws Throwable { countTest(positive); MethodType type = MethodType.methodType(ret, params); - Method rmethod = null; + Method rmethod = defc.getDeclaredMethod(name, params); MethodHandle target = null; Exception noAccess = null; - try { - rmethod = defc.getDeclaredMethod(name, params); - } catch (NoSuchMethodException ex) { - throw new NoAccessException(ex); - } boolean isStatic = (rcvc == null); boolean isSpecial = (specialCaller != null); try { @@ -714,8 +764,12 @@ public class MethodHandlesTest { target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller); else target = lookup.in(defc).unreflect(rmethod); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { noAccess = ex; + if (name.contains("bogus")) + assertTrue(noAccess instanceof NoSuchMethodException); + else + assertTrue(noAccess instanceof IllegalAccessException); } if (verbosity >= 3) System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type @@ -824,25 +878,28 @@ public class MethodHandlesTest { if (type == float.class) { float v = 'F'; if (isStatic) v++; - assert(value.equals(v)); + assertTrue(value.equals(v)); } - assert(name.equals(field.getName())); - assert(type.equals(field.getType())); - assert(isStatic == (Modifier.isStatic(field.getModifiers()))); + assertTrue(name.equals(field.getName())); + assertTrue(type.equals(field.getType())); + assertTrue(isStatic == (Modifier.isStatic(field.getModifiers()))); cases.add(new Object[]{ field, value }); } } + cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class }); + cases.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class }); CASES = cases.toArray(new Object[0][]); } } - static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC_FIELD = 3; + static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10; static boolean testModeMatches(int testMode, boolean isStatic) { switch (testMode) { - case TEST_FIND_STATIC_FIELD: return isStatic; + case TEST_FIND_STATIC: return isStatic; case TEST_FIND_FIELD: return !isStatic; - default: return true; // unreflect matches both + case TEST_UNREFLECT: return true; // unreflect matches both } + throw new InternalError("testMode="+testMode); } @Test @@ -858,54 +915,161 @@ public class MethodHandlesTest { @Test public void testFindStaticGetter() throws Throwable { startTest("findStaticGetter"); - testGetter(TEST_FIND_STATIC_FIELD); + testGetter(TEST_FIND_STATIC); } public void testGetter(int testMode) throws Throwable { Lookup lookup = PRIVATE; // FIXME: test more lookups than this one for (Object[] c : HasFields.CASES) { - Field f = (Field)c[0]; - Object value = c[1]; - Class type = f.getType(); - testGetter(lookup, f, type, value, testMode); - } - } - public void testGetter(MethodHandles.Lookup lookup, - Field f, Class type, Object value, int testMode) throws Throwable { - boolean isStatic = Modifier.isStatic(f.getModifiers()); - Class fclass = f.getDeclaringClass(); - String fname = f.getName(); - Class ftype = f.getType(); + boolean positive = (c[1] != Error.class); + testGetter(positive, lookup, c[0], c[1], testMode); + } + testGetter(true, lookup, + new Object[]{ true, System.class, "out", java.io.PrintStream.class }, + System.out, testMode); + for (int isStaticN = 0; isStaticN <= 1; isStaticN++) { + testGetter(false, lookup, + new Object[]{ (isStaticN != 0), System.class, "bogus", char.class }, + null, testMode); + } + } + public void testGetter(boolean positive, MethodHandles.Lookup lookup, + Object fieldRef, Object value, int testMode) throws Throwable { + testAccessor(positive, lookup, fieldRef, value, testMode); + } + + public void testAccessor(boolean positive, MethodHandles.Lookup lookup, + Object fieldRef, Object value, int testMode0) throws Throwable { + boolean isGetter = ((testMode0 & TEST_SETTER) == 0); + int testMode = testMode0 & ~TEST_SETTER; + boolean isStatic; + Class fclass; + String fname; + Class ftype; + Field f = (fieldRef instanceof Field ? (Field)fieldRef : null); + if (f != null) { + isStatic = Modifier.isStatic(f.getModifiers()); + fclass = f.getDeclaringClass(); + fname = f.getName(); + ftype = f.getType(); + } else { + Object[] scnt = (Object[]) fieldRef; + isStatic = (Boolean) scnt[0]; + fclass = (Class) scnt[1]; + fname = (String) scnt[2]; + ftype = (Class) scnt[3]; + try { + f = fclass.getDeclaredField(fname); + } catch (ReflectiveOperationException ex) { + f = null; + } + } if (!testModeMatches(testMode, isStatic)) return; - countTest(true); - MethodType expType = MethodType.methodType(type, HasFields.class); + if (f == null && testMode == TEST_UNREFLECT) return; + countTest(positive); + MethodType expType; + if (isGetter) + expType = MethodType.methodType(ftype, HasFields.class); + else + expType = MethodType.methodType(void.class, HasFields.class, ftype); if (isStatic) expType = expType.dropParameterTypes(0, 1); - MethodHandle mh = lookup.unreflectGetter(f); + Exception noAccess = null; + MethodHandle mh; + try { + switch (testMode0) { + case TEST_UNREFLECT: mh = lookup.unreflectGetter(f); break; + case TEST_FIND_FIELD: mh = lookup.findGetter(fclass, fname, ftype); break; + case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype); break; + case TEST_SETTER| + TEST_UNREFLECT: mh = lookup.unreflectSetter(f); break; + case TEST_SETTER| + TEST_FIND_FIELD: mh = lookup.findSetter(fclass, fname, ftype); break; + case TEST_SETTER| + TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype); break; + default: + throw new InternalError("testMode="+testMode); + } + } catch (ReflectiveOperationException ex) { + mh = null; + noAccess = ex; + if (fname.contains("bogus")) + assertTrue(noAccess instanceof NoSuchFieldException); + else + assertTrue(noAccess instanceof IllegalAccessException); + } + if (verbosity >= 3) + System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype + +" => "+mh + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw new RuntimeException(noAccess); + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null); + if (!positive) return; // negative test failed as expected + assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount()); + + assertSame(mh.type(), expType); assertNameStringContains(mh, fname); HasFields fields = new HasFields(); Object sawValue; - Class rtype = type; - if (type != int.class) rtype = Object.class; - mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(rtype)); - Object expValue = value; - for (int i = 0; i <= 1; i++) { - if (isStatic) { - if (type == int.class) - sawValue = (int) mh.invokeExact(); // do these exactly - else - sawValue = mh.invokeExact(); - } else { - if (type == int.class) - sawValue = (int) mh.invokeExact((Object) fields); - else - sawValue = mh.invokeExact((Object) fields); + Class vtype = ftype; + if (ftype != int.class) vtype = Object.class; + if (isGetter) { + mh = MethodHandles.convertArguments(mh, mh.type().generic() + .changeReturnType(vtype)); + } else { + int last = mh.type().parameterCount() - 1; + mh = MethodHandles.convertArguments(mh, mh.type().generic() + .changeReturnType(void.class) + .changeParameterType(last, vtype)); + } + if (f != null && f.getDeclaringClass() == HasFields.class) { + assertEquals(f.get(fields), value); // clean to start with + } + if (isGetter) { + Object expValue = value; + for (int i = 0; i <= 1; i++) { + if (isStatic) { + if (ftype == int.class) + sawValue = (int) mh.invokeExact(); // do these exactly + else + sawValue = mh.invokeExact(); + } else { + if (ftype == int.class) + sawValue = (int) mh.invokeExact((Object) fields); + else + sawValue = mh.invokeExact((Object) fields); + } + assertEquals(sawValue, expValue); + if (f != null && f.getDeclaringClass() == HasFields.class + && !Modifier.isFinal(f.getModifiers())) { + Object random = randomArg(ftype); + f.set(fields, random); + expValue = random; + } else { + break; + } } - assertEquals(sawValue, expValue); - Object random = randomArg(type); - f.set(fields, random); - expValue = random; + } else { + for (int i = 0; i <= 1; i++) { + Object putValue = randomArg(ftype); + if (isStatic) { + if (ftype == int.class) + mh.invokeExact((int)putValue); // do these exactly + else + mh.invokeExact(putValue); + } else { + if (ftype == int.class) + mh.invokeExact((Object) fields, (int)putValue); + else + mh.invokeExact((Object) fields, putValue); + } + if (f != null && f.getDeclaringClass() == HasFields.class) { + assertEquals(f.get(fields), putValue); + } + } + } + if (f != null && f.getDeclaringClass() == HasFields.class) { + f.set(fields, value); // put it back } - f.set(fields, value); // put it back } @@ -922,61 +1086,24 @@ public class MethodHandlesTest { @Test public void testFindStaticSetter() throws Throwable { startTest("findStaticSetter"); - testSetter(TEST_FIND_STATIC_FIELD); + testSetter(TEST_FIND_STATIC); } public void testSetter(int testMode) throws Throwable { Lookup lookup = PRIVATE; // FIXME: test more lookups than this one startTest("unreflectSetter"); for (Object[] c : HasFields.CASES) { - Field f = (Field)c[0]; - Object value = c[1]; - Class type = f.getType(); - testSetter(lookup, f, type, value, testMode); - } - } - public void testSetter(MethodHandles.Lookup lookup, - Field f, Class type, Object value, int testMode) throws Throwable { - boolean isStatic = Modifier.isStatic(f.getModifiers()); - Class fclass = f.getDeclaringClass(); - String fname = f.getName(); - Class ftype = f.getType(); - if (!testModeMatches(testMode, isStatic)) return; - countTest(true); - MethodType expType = MethodType.methodType(void.class, HasFields.class, type); - if (isStatic) expType = expType.dropParameterTypes(0, 1); - MethodHandle mh; - if (testMode == TEST_UNREFLECT) - mh = lookup.unreflectSetter(f); - else if (testMode == TEST_FIND_FIELD) - mh = lookup.findSetter(fclass, fname, ftype); - else if (testMode == TEST_FIND_STATIC_FIELD) - mh = lookup.findStaticSetter(fclass, fname, ftype); - else throw new InternalError(); - assertSame(mh.type(), expType); - assertNameStringContains(mh, fname); - HasFields fields = new HasFields(); - Object sawValue; - Class vtype = type; - if (type != int.class) vtype = Object.class; - int last = mh.type().parameterCount() - 1; - mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(void.class).changeParameterType(last, vtype)); - assertEquals(f.get(fields), value); // clean to start with - for (int i = 0; i <= 1; i++) { - Object putValue = randomArg(type); - if (isStatic) { - if (type == int.class) - mh.invokeExact((int)putValue); // do these exactly - else - mh.invokeExact(putValue); - } else { - if (type == int.class) - mh.invokeExact((Object) fields, (int)putValue); - else - mh.invokeExact((Object) fields, putValue); - } - assertEquals(f.get(fields), putValue); + boolean positive = (c[1] != Error.class); + testSetter(positive, lookup, c[0], c[1], testMode); + } + for (int isStaticN = 0; isStaticN <= 1; isStaticN++) { + testSetter(false, lookup, + new Object[]{ (isStaticN != 0), System.class, "bogus", char.class }, + null, testMode); } - f.set(fields, value); // put it back + } + public void testSetter(boolean positive, MethodHandles.Lookup lookup, + Object fieldRef, Object value, int testMode) throws Throwable { + testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER); } @Test @@ -1108,18 +1235,6 @@ public class MethodHandlesTest { } } - static MethodHandle typeHandler2(MethodHandle target, MethodType newType) { - MethodType oldType = target.type(); - int oldArity = oldType.parameterCount(); - int newArity = newType.parameterCount(); - if (newArity < oldArity) - return MethodHandles.insertArguments(target, oldArity, "OPTIONAL"); - else if (newArity > oldArity) - return MethodHandles.dropArguments(target, oldArity-1, newType.parameterType(oldArity-1)); - else - return target; // attempt no further conversions - } - @Test public void testConvertArguments() throws Throwable { if (CAN_SKIP_WORKING) return; @@ -1137,23 +1252,6 @@ public class MethodHandlesTest { testConvert(true, true, id, rtype, name, params); } - @Test - public void testTypeHandler() throws Throwable { - MethodHandle id = Callee.ofType(1); - MethodHandle th2 = PRIVATE.findStatic(MethodHandlesTest.class, "typeHandler2", - MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class)); - MethodHandle id2 = id.withTypeHandler(th2); - testConvert(true, false, id2, null, "id", Object.class); - testConvert(true, true, id2, null, "id", Object.class); - if (true) return; //FIXME - testConvert(true, false, id2, null, "id", String.class); // FIXME: throws WMT - testConvert(false, true, id2, null, "id", String.class); // FIXME: should not succeed - testConvert(false, false, id2, null, "id", Object.class, String.class); //FIXME: array[1] line 1164 - testConvert(true, true, id2, null, "id", Object.class, String.class); - testConvert(false, false, id2, null, "id"); - testConvert(true, true, id2, null, "id"); - } - void testConvert(boolean positive, boolean useAsType, MethodHandle id, Class rtype, String name, Class... params) throws Throwable { countTest(positive); @@ -1183,9 +1281,9 @@ public class MethodHandlesTest { RuntimeException error = null; try { if (useAsType) - target = MethodHandles.convertArguments(id, newType); - else target = id.asType(newType); + else + target = MethodHandles.convertArguments(id, newType); } catch (RuntimeException ex) { error = ex; } @@ -1204,6 +1302,20 @@ public class MethodHandlesTest { System.out.print(':'); } + @Test + public void testVarargsCollector() throws Throwable { + MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called", + MethodType.methodType(Object.class, String.class, Object[].class)); + vac0 = vac0.bindTo("vac"); + MethodHandle vac = vac0.asVarargsCollector(Object[].class); + testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac"); + testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac"); + for (Class at : new Class[] { Object.class, String.class, Integer.class }) { + testConvert(true, true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at); + testConvert(true, true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at); + } + } + @Test public void testPermuteArguments() throws Throwable { if (CAN_SKIP_WORKING) return; @@ -1300,7 +1412,7 @@ public class MethodHandlesTest { types[i] = args[i].getClass(); } int inargs = args.length, outargs = reorder.length; - assert(inargs == types.length); + assertTrue(inargs == types.length); if (verbosity >= 3) System.out.println("permuteArguments "+Arrays.toString(reorder)); Object[] permArgs = new Object[outargs]; @@ -1317,7 +1429,7 @@ public class MethodHandlesTest { } MethodType inType = MethodType.methodType(Object.class, types); MethodType outType = MethodType.methodType(Object.class, permTypes); - MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType); + MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType); MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); Object result = newTarget.invokeWithArguments(args); Object expected = Arrays.asList(permArgs); @@ -1344,7 +1456,7 @@ public class MethodHandlesTest { } public void testSpreadArguments(Class argType, int pos, int nargs) throws Throwable { countTest(); - MethodHandle target = ValueConversions.varargsArray(nargs); + MethodHandle target = varargsArray(nargs); MethodHandle target2 = changeArgTypes(target, argType); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); @@ -1374,7 +1486,7 @@ public class MethodHandlesTest { spreadParams.clear(); spreadParams.add(Object[].class); } MethodType newType = MethodType.methodType(Object.class, newParams); - MethodHandle result = MethodHandles.spreadArguments(target2, newType); + MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType); Object[] returnValue; if (pos == 0) { // In the following line, the first cast implies @@ -1408,7 +1520,7 @@ public class MethodHandlesTest { public void testCollectArguments(Class argType, int pos, int nargs) throws Throwable { countTest(); // fake up a MH with the same type as the desired adapter: - MethodHandle fake = ValueConversions.varargsArray(nargs); + MethodHandle fake = varargsArray(nargs); fake = changeArgTypes(fake, argType); MethodType newType = fake.type(); Object[] args = randomArgs(newType.parameterArray()); @@ -1416,12 +1528,12 @@ public class MethodHandlesTest { Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1); collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length); // here is the MH which will witness the collected argument tail: - MethodHandle target = ValueConversions.varargsArray(pos+1); + MethodHandle target = varargsArray(pos+1); target = changeArgTypes(target, 0, pos, argType); target = changeArgTypes(target, pos, pos+1, Object[].class); if (verbosity >= 3) System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]"); - MethodHandle result = MethodHandles.collectArguments(target, newType); + MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType); Object[] returnValue = (Object[]) result.invokeWithArguments(args); // assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]); // returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]); @@ -1445,7 +1557,7 @@ public class MethodHandlesTest { void testInsertArguments(int nargs, int pos, int ins) throws Throwable { countTest(); - MethodHandle target = ValueConversions.varargsArray(nargs + ins); + MethodHandle target = varargsArray(nargs + ins); Object[] args = randomArgs(target.type().parameterArray()); List resList = Arrays.asList(args); List argsToPass = new ArrayList(resList); @@ -1464,6 +1576,55 @@ public class MethodHandlesTest { assertEquals(resList, res2List); } + @Test + public void testFilterReturnValue() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("filterReturnValue"); + Class classOfVCList = varargsList(1).invokeWithArguments(0).getClass(); + assertTrue(List.class.isAssignableFrom(classOfVCList)); + for (int nargs = 0; nargs <= 3; nargs++) { + for (Class rtype : new Class[] { Object.class, + List.class, + int.class, + //byte.class, //FIXME: add this + //long.class, //FIXME: add this + CharSequence.class, + String.class }) { + testFilterReturnValue(nargs, rtype); + } + } + } + + void testFilterReturnValue(int nargs, Class rtype) throws Throwable { + countTest(); + MethodHandle target = varargsList(nargs, rtype); + MethodHandle filter; + if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class)) + filter = varargsList(1); // add another layer of list-ness + else + filter = MethodHandles.identity(rtype); + filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype)); + Object[] argsToPass = randomArgs(nargs, Object.class); + if (verbosity >= 3) + System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter); + MethodHandle target2 = MethodHandles.filterReturnValue(target, filter); + if (verbosity >= 4) + System.out.println("filtered target: "+target2); + // Simulate expected effect of filter on return value: + Object unfiltered = target.invokeWithArguments(argsToPass); + Object expected = filter.invokeWithArguments(unfiltered); + if (verbosity >= 4) + System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName()); + if (verbosity >= 4) + System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName()); + Object result = target2.invokeWithArguments(argsToPass); + if (verbosity >= 3) + System.out.println("result: "+result+" : "+result.getClass().getSimpleName()); + if (!expected.equals(result)) + System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected); + assertEquals(expected, result); + } + @Test public void testFilterArguments() throws Throwable { if (CAN_SKIP_WORKING) return; @@ -1477,8 +1638,8 @@ public class MethodHandlesTest { void testFilterArguments(int nargs, int pos) throws Throwable { countTest(); - MethodHandle target = ValueConversions.varargsList(nargs); - MethodHandle filter = ValueConversions.varargsList(1); + MethodHandle target = varargsList(nargs); + MethodHandle filter = varargsList(1); filter = MethodHandles.convertArguments(filter, filter.type().generic()); Object[] argsToPass = randomArgs(nargs, Object.class); if (verbosity >= 3) @@ -1492,7 +1653,7 @@ public class MethodHandlesTest { if (verbosity >= 3) System.out.println("result: "+result); if (!expected.equals(result)) - System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+argsToPass+" => "+result); + System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected); assertEquals(expected, result); } @@ -1512,8 +1673,8 @@ public class MethodHandlesTest { void testFoldArguments(int nargs, int pos, int fold) throws Throwable { if (pos != 0) return; // can fold only at pos=0 for now countTest(); - MethodHandle target = ValueConversions.varargsList(1 + nargs); - MethodHandle combine = ValueConversions.varargsList(fold).asType(MethodType.genericMethodType(fold)); + MethodHandle target = varargsList(1 + nargs); + MethodHandle combine = varargsList(fold).asType(MethodType.genericMethodType(fold)); List argsToPass = Arrays.asList(randomArgs(nargs, Object.class)); if (verbosity >= 3) System.out.println("fold "+target+" with "+combine); @@ -1529,7 +1690,7 @@ public class MethodHandlesTest { if (verbosity >= 3) System.out.println("result: "+result); if (!expected.equals(result)) - System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result); + System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected); assertEquals(expected, result); } @@ -1548,7 +1709,7 @@ public class MethodHandlesTest { void testDropArguments(int nargs, int pos, int drop) throws Throwable { countTest(); - MethodHandle target = ValueConversions.varargsArray(nargs); + MethodHandle target = varargsArray(nargs); Object[] args = randomArgs(target.type().parameterArray()); MethodHandle target2 = MethodHandles.dropArguments(target, pos, Collections.nCopies(drop, Object.class).toArray(new Class[0])); @@ -1599,7 +1760,8 @@ public class MethodHandlesTest { boolean testRetCode = type.returnType() != void.class; MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee", MethodType.genericMethodType(0, true)); - target = MethodHandles.collectArguments(target, type); + assertTrue(target.isVarargsCollector()); + target = target.asType(type); Object[] args = randomArgs(type.parameterArray()); List targetPlusArgs = new ArrayList(Arrays.asList(args)); targetPlusArgs.add(0, target); @@ -1644,14 +1806,14 @@ public class MethodHandlesTest { assertCalled("invokee", args); // varargs invoker #0 calledLog.clear(); - inv = MethodHandles.varargsInvoker(type, 0); + inv = MethodHandles.spreadInvoker(type, 0); result = inv.invokeExact(target, args); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); if (nargs >= 1) { // varargs invoker #1 calledLog.clear(); - inv = MethodHandles.varargsInvoker(type, 1); + inv = MethodHandles.spreadInvoker(type, 1); result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs)); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); @@ -1659,7 +1821,7 @@ public class MethodHandlesTest { if (nargs >= 2) { // varargs invoker #2 calledLog.clear(); - inv = MethodHandles.varargsInvoker(type, 2); + inv = MethodHandles.spreadInvoker(type, 2); result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs)); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); @@ -1667,7 +1829,7 @@ public class MethodHandlesTest { if (nargs >= 3) { // varargs invoker #3 calledLog.clear(); - inv = MethodHandles.varargsInvoker(type, 3); + inv = MethodHandles.spreadInvoker(type, 3); result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs)); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); @@ -1676,7 +1838,7 @@ public class MethodHandlesTest { // varargs invoker #0..N countTest(); calledLog.clear(); - inv = MethodHandles.varargsInvoker(type, k); + inv = MethodHandles.spreadInvoker(type, k); List targetPlusVarArgs = new ArrayList(targetPlusArgs); List tailList = targetPlusVarArgs.subList(1+k, 1+nargs); Object[] tail = tailList.toArray(); @@ -1823,7 +1985,7 @@ public class MethodHandlesTest { MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2)); while (thrower.type().parameterCount() < nargs) thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class); - MethodHandle catcher = ValueConversions.varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs)); + MethodHandle catcher = varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs)); MethodHandle target = MethodHandles.catchException(thrower, thrown.getClass(), catcher); assertEquals(thrower.type(), target.type()); @@ -2089,25 +2251,12 @@ public class MethodHandlesTest { } } // Test error checking: - MethodHandle genericMH = ValueConversions.varargsArray(0); - genericMH = MethodHandles.convertArguments(genericMH, genericMH.type().generic()); - for (Class sam : new Class[] { Runnable.class, - Fooable.class, - Iterable.class }) { - try { - // Must throw, because none of these guys has generic type. - MethodHandles.asInstance(genericMH, sam); - System.out.println("Failed to throw"); - assertTrue(false); - } catch (IllegalArgumentException ex) { - } - } for (Class nonSAM : new Class[] { Object.class, String.class, CharSequence.class, Example.class }) { try { - MethodHandles.asInstance(ValueConversions.varargsArray(0), nonSAM); + MethodHandles.asInstance(varargsArray(0), nonSAM); System.out.println("Failed to throw"); assertTrue(false); } catch (IllegalArgumentException ex) { @@ -2159,12 +2308,13 @@ class ValueConversions { MethodHandle array = null; try { array = lookup.findStatic(ValueConversions.class, name, type); - } catch (NoAccessException ex) { + } catch (ReflectiveOperationException ex) { + // break from loop! } if (array == null) break; arrays.add(array); } - assert(arrays.size() == 11); // current number of methods + assertTrue(arrays.size() == 11); // current number of methods return arrays.toArray(new MethodHandle[0]); } static final MethodHandle[] ARRAYS = makeArrays(); @@ -2211,22 +2361,23 @@ class ValueConversions { Object a8, Object a9) { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } static MethodHandle[] makeLists() { - ArrayList arrays = new ArrayList(); + ArrayList lists = new ArrayList(); MethodHandles.Lookup lookup = IMPL_LOOKUP; for (;;) { - int nargs = arrays.size(); + int nargs = lists.size(); MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class); String name = "list"; - MethodHandle array = null; + MethodHandle list = null; try { - array = lookup.findStatic(ValueConversions.class, name, type); - } catch (NoAccessException ex) { + list = lookup.findStatic(ValueConversions.class, name, type); + } catch (ReflectiveOperationException ex) { + // break from loop! } - if (array == null) break; - arrays.add(array); + if (list == null) break; + lists.add(list); } - assert(arrays.size() == 11); // current number of methods - return arrays.toArray(new MethodHandle[0]); + assertTrue(lists.size() == 11); // current number of methods + return lists.toArray(new MethodHandle[0]); } static final MethodHandle[] LISTS = makeLists(); diff --git a/test/java/dyn/MethodTypeTest.java b/test/java/dyn/MethodTypeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..caadaa04a333a807aa3cf4bb988846029d1b341e --- /dev/null +++ b/test/java/dyn/MethodTypeTest.java @@ -0,0 +1,542 @@ +/* + * Copyright 2008, 2011 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @summary unit tests for java.dyn.MethodType + * @compile MethodTypeTest.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.MethodTypeTest + */ + +package test.java.dyn; + +import sun.dyn.MemberName; +import java.dyn.MethodType; +import java.lang.reflect.Method; + +import java.util.*; +import org.junit.*; +import static org.junit.Assert.*; + +/** + * + * @author jrose + */ +public class MethodTypeTest { + + private Class rtype; + private Class[] ptypes; + private MethodType mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov; + private MethodType mt_iSI, mt_ISi, mt_ISI, mt_iSi; + private MethodType mt_viO, mt_iO2, mt_OOi, mt_iOi; + private MethodType mt_VIO, mt_IO2, mt_OOI, mt_IOI, mt_VIS; + private MethodType mt_vOiSzA, mt_OO99; + private MethodType[] GALLERY; + private Method compareTo; + + @Before + public void setUp() throws Exception { + rtype = void.class; + ptypes = new Class[] { int.class, String.class }; + + mt_viS = MethodType.methodType(void.class, int.class, String.class); + mt_OO = MethodType.methodType(Object.class, Object.class); + mt_OO2 = MethodType.methodType(Object.class, Object.class, Object.class); + mt_vv = MethodType.methodType(void.class); + mt_Vv = MethodType.methodType(Void.class); + mt_Ov = MethodType.methodType(Object.class); + mt_iSI = MethodType.methodType(int.class, String.class, Integer.class); + mt_ISi = MethodType.methodType(Integer.class, String.class, int.class); + mt_ISI = MethodType.methodType(Integer.class, String.class, Integer.class); + mt_iSi = MethodType.methodType(int.class, String.class, int.class); + + compareTo = String.class.getDeclaredMethod("compareTo", String.class); + + mt_viO = MethodType.methodType(void.class, int.class, Object.class); + mt_iO2 = MethodType.methodType(int.class, Object.class, Object.class); + mt_OOi = MethodType.methodType(Object.class, Object.class, int.class); + mt_iOi = MethodType.methodType(int.class, Object.class, int.class); + + mt_VIO = MethodType.methodType(Void.class, Integer.class, Object.class); + mt_IO2 = MethodType.methodType(Integer.class, Object.class, Object.class); + mt_OOI = MethodType.methodType(Object.class, Object.class, Integer.class); + mt_IOI = MethodType.methodType(Integer.class, Object.class, Integer.class); + mt_VIS = MethodType.methodType(Void.class, Integer.class, String.class); + + mt_vOiSzA = MethodType.methodType(void.class, Object.class, int.class, String.class, boolean.class, Object[].class); + mt_OO99 = MethodType.genericMethodType(99); + + GALLERY = new MethodType[] { + mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov, + mt_iSI, mt_ISi, mt_ISI, mt_iSi, + mt_viO, mt_iO2, mt_OOi, mt_iOi, + mt_VIO, mt_IO2, mt_OOI, mt_IOI, + mt_VIS, mt_vOiSzA, mt_OO99 + }; + } + + @After + public void tearDown() throws Exception { + } + + /** Make sure the method types are all distinct. */ + @Test + public void testDistinct() { + List gallery2 = new ArrayList<>(); + for (MethodType mt : GALLERY) { + assertFalse(mt.toString(), gallery2.contains(mt)); + gallery2.add(mt); + } + // check self-equality also: + assertEquals(Arrays.asList(GALLERY), gallery2); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Class_ClassArr() { + System.out.println("make (from type array)"); + MethodType result = MethodType.methodType(rtype, ptypes); + assertSame(mt_viS, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Class_List() { + System.out.println("make (from type list)"); + MethodType result = MethodType.methodType(rtype, Arrays.asList(ptypes)); + assertSame(mt_viS, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_3args() { + System.out.println("make (from type with varargs)"); + MethodType result = MethodType.methodType(rtype, ptypes[0], ptypes[1]); + assertSame(mt_viS, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Class() { + System.out.println("make (from single type)"); + Class rt = Integer.class; + MethodType expResult = MethodType.methodType(rt, new Class[0]); + MethodType result = MethodType.methodType(rt); + assertSame(expResult, result); + } + + @Test + public void testMakeGeneric() { + System.out.println("makeGeneric"); + int objectArgCount = 2; + MethodType expResult = mt_OO2; + MethodType result = MethodType.genericMethodType(objectArgCount); + assertSame(expResult, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Method() { + System.out.println("make (via MemberName.getMethodType)"); + MethodType expResult = MethodType.methodType(int.class, String.class); + MemberName name = new MemberName(compareTo); + MethodType result = name.getMethodType(); + assertSame(expResult, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_MethodType() { + System.out.println("make (from rtype, MethodType)"); + MethodType expResult = mt_iO2; + MethodType result = MethodType.methodType(int.class, mt_IO2); + assertSame(expResult, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_String_ClassLoader() { + System.out.println("make (from bytecode signature)"); + ClassLoader loader = null; + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + String obj = "Ljava/lang/Object;"; + assertEquals(obj, concat(Object.class)); + String[] expResults = { + "(ILjava/lang/String;)V", + concat("(", obj, 2, ")", Object.class), + "()V", "()"+obj, + concat("(", String.class, Integer.class, ")I"), + concat("(", String.class, "I)", Integer.class), + concat("(", String.class, Integer.class, ")", Integer.class), + concat("(", String.class, "I)I") + }; + for (int i = 0; i < instances.length; i++) { + MethodType instance = instances[i]; + String result = instance.toMethodDescriptorString(); + assertEquals("#"+i, expResults[i], result); + MethodType parsed = MethodType.fromMethodDescriptorString(result, loader); + assertSame("--#"+i, instance, parsed); + } + } + private static String concat(Object... parts) { + StringBuilder sb = new StringBuilder(); + Object prevPart = ""; + for (Object part : parts) { + if (part instanceof Class) { + part = "L"+((Class)part).getName()+";"; + } + if (part instanceof Integer) { + for (int n = (Integer) part; n > 1; n--) + sb.append(prevPart); + part = ""; + } + sb.append(part); + prevPart = part; + } + return sb.toString().replace('.', '/'); + } + + @Test + public void testHasPrimitives() { + System.out.println("hasPrimitives"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + boolean[] expResults = {true, false, true, false, true, true, false, true}; + for (int i = 0; i < instances.length; i++) { + boolean result = instances[i].hasPrimitives(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testHasWrappers() { + System.out.println("hasWrappers"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + boolean[] expResults = {false, false, false, false, true, true, true, false}; + for (int i = 0; i < instances.length; i++) { + System.out.println(" hasWrappers "+instances[i]); + boolean result = instances[i].hasWrappers(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testErase() { + System.out.println("erase"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_viO, mt_OO2, mt_vv, mt_Ov, mt_iO2, mt_OOi, mt_OO2, mt_iOi}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].erase(); + assertSame("#"+i, expResults[i], result); + } + } + + @Test + public void testGeneric() { + System.out.println("generic"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_OO2, mt_OO2, mt_Ov, mt_Ov, mt_OO2, mt_OO2, mt_OO2, mt_OO2}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].generic(); + assertSame("#"+i, expResults[i], result); + } + } + + @Test + public void testWrap() { + System.out.println("wrap"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_VIS, mt_OO2, mt_Vv, mt_Ov, mt_ISI, mt_ISI, mt_ISI, mt_ISI}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].wrap(); + assertSame("#"+i, expResults[i], result); + } + } + + @Test + public void testUnwrap() { + System.out.println("unwrap"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSi, mt_iSi, mt_iSi, mt_iSi}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].unwrap(); + assertSame("#"+i, expResults[i], result); + } + } + + /** + * Test of parameterType method, of class MethodType. + */ + @Test + public void testParameterType() { + System.out.println("parameterType"); + for (int num = 0; num < ptypes.length; num++) { + MethodType instance = mt_viS; + Class expResult = ptypes[num]; + Class result = instance.parameterType(num); + assertSame(expResult, result); + } + } + + /** + * Test of parameterCount method, of class MethodType. + */ + @Test + public void testParameterCount() { + System.out.println("parameterCount"); + MethodType instance = mt_viS; + int expResult = 2; + int result = instance.parameterCount(); + assertEquals(expResult, result); + } + + /** + * Test of returnType method, of class MethodType. + */ + @Test + public void testReturnType() { + System.out.println("returnType"); + MethodType instance = mt_viS; + Class expResult = void.class; + Class result = instance.returnType(); + assertSame(expResult, result); + } + + /** + * Test of parameterList method, of class MethodType. + */ + @Test + public void testParameterList() { + System.out.println("parameterList"); + MethodType instance = mt_viS; + List> expResult = Arrays.asList(ptypes); + List> result = instance.parameterList(); + assertEquals(expResult, result); + } + + /** + * Test of parameterArray method, of class MethodType. + */ + @Test + public void testParameterArray() { + System.out.println("parameterArray"); + MethodType instance = mt_viS; + Class[] expResult = ptypes; + Class[] result = instance.parameterArray(); + assertEquals(Arrays.asList(expResult), Arrays.asList(result)); + } + + /** + * Test of equals method, of class MethodType. + */ + @Test + public void testEquals_Object() { + System.out.println("equals"); + Object x = null; + MethodType instance = mt_viS; + boolean expResult = false; + boolean result = instance.equals(x); + assertEquals(expResult, result); + } + + /** + * Test of equals method, of class MethodType. + */ + @Test + public void testEquals_MethodType() { + System.out.println("equals"); + MethodType that = mt_viS; + MethodType instance = mt_viS; + boolean expResult = true; + boolean result = instance.equals(that); + assertEquals(expResult, result); + } + + /** + * Test of hashCode method, of class MethodType. + */ + @Test + public void testHashCode() { + System.out.println("hashCode"); + MethodType instance = mt_viS; + ArrayList> types = new ArrayList>(); + types.add(instance.returnType()); + types.addAll(instance.parameterList()); + int expResult = types.hashCode(); + int result = instance.hashCode(); + assertEquals(expResult, result); + } + + /** + * Test of toString method, of class MethodType. + */ + @Test + public void testToString() { + System.out.println("toString"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + //String expResult = "void[int, class java.lang.String]"; + String[] expResults = { + "(int,String)void", + "(Object,Object)Object", + "()void", + "()Object", + "(String,Integer)int", + "(String,int)Integer", + "(String,Integer)Integer", + "(String,int)int" + }; + for (int i = 0; i < instances.length; i++) { + MethodType instance = instances[i]; + String result = instance.toString(); + System.out.println("#"+i+":"+result); + assertEquals("#"+i, expResults[i], result); + } + } + + private static byte[] writeSerial(Object x) throws java.io.IOException { + try (java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream(); + java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(bout) + ) { + out.writeObject(x); + out.flush(); + return bout.toByteArray(); + } + } + private static Object readSerial(byte[] wire) throws java.io.IOException, ClassNotFoundException { + try (java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(wire); + java.io.ObjectInputStream in = new java.io.ObjectInputStream(bin)) { + return in.readObject(); + } + } + private static void testSerializedEquality(Object x) throws java.io.IOException, ClassNotFoundException { + if (x instanceof Object[]) + x = Arrays.asList((Object[]) x); // has proper equals method + byte[] wire = writeSerial(x); + Object y = readSerial(wire); + assertEquals(x, y); + } + + /** Test (de-)serialization. */ + @Test + public void testSerialization() throws Throwable { + System.out.println("serialization"); + for (MethodType mt : GALLERY) { + testSerializedEquality(mt); + } + testSerializedEquality(GALLERY); + + // Make a list of mixed objects: + List stuff = new ArrayList<>(); + Collections.addAll(stuff, GALLERY); // copy #1 + Object[] triples = Arrays.copyOfRange(GALLERY, 0, GALLERY.length/2); + Collections.addAll(stuff, triples); // copy #3 (partial) + for (MethodType mt : GALLERY) { + Collections.addAll(stuff, mt.parameterArray()); + } + Collections.shuffle(stuff, new Random(292)); + Collections.addAll(stuff, GALLERY); // copy #2 + testSerializedEquality(stuff); + } + + /** Test serialization formats. */ + @Test + public void testPortableSerialFormat() throws Throwable { + System.out.println("portable serial format"); + Object[][] cases = { + { mt_vv, new byte[] { // ()void + (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13, + (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x64, (byte)0x79, (byte)0x6e, + (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54, + (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, + (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x6f, (byte)0x69, (byte)0x64, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00, + (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, + (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61, + (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab, (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb, + (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, + } }, + { mt_OO, new byte[] { // (Object)Object + (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13, + (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x64, (byte)0x79, (byte)0x6e, + (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54, + (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, + (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x10, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, + (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x4f, (byte)0x62, + (byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, + (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00, (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a, + (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, + (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61, (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab, + (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb, (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02, + (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, + (byte)0x71, (byte)0x00, (byte)0x7e, (byte)0x00, (byte)0x03, (byte)0x78, + } }, + }; + boolean generateData = false; + //generateData = true; + for (Object[] c : cases) { + MethodType mt = (MethodType) c[0]; + System.out.println("deserialize "+mt); + byte[] wire = (byte[]) c[1]; + if (generateData) { + wire = writeSerial(mt); + final String INDENT = " "; + System.out.print("{ // "+mt); + for (int i = 0; i < wire.length; i++) { + if (i % 8 == 0) { System.out.println(); System.out.print(INDENT+" "); } + String hex = Integer.toHexString(wire[i] & 0xFF); + if (hex.length() == 1) hex = "0"+hex; + System.out.print(" (byte)0x"+hex+","); + } + System.out.println(); + System.out.println(INDENT+"}"); + System.out.flush(); + } + Object decode; + try { + decode = readSerial(wire); + } catch (Exception ex) { + decode = ex; // oops! + } + assertEquals(mt, decode); + } + } +} diff --git a/test/java/dyn/indify/Indify.java b/test/java/dyn/indify/Indify.java index 0896f0e0a434fc8de4583e776f6dde5172076898..ba24b79a9d96fec797291eb2d92001889f302c42 100644 --- a/test/java/dyn/indify/Indify.java +++ b/test/java/dyn/indify/Indify.java @@ -98,8 +98,9 @@ $ $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic (same output as above) * *

- * Until the format of {@code CONSTANT_InvokeDynamic} entries is finalized, - * the {@code --transitionalJSR292} switch is recommended (and turned on by default). + * Before OpenJDK build b123, the format of {@code CONSTANT_InvokeDynamic} is in transition, + * and the switch {@code --transitionalJSR292=yes} is recommended. + * It is turned off by default, but users of earlier builds may need to turn it on. *

* A version of this transformation built on top of http://asm.ow2.org/ would be welcome. * @author John Rose @@ -116,7 +117,7 @@ public class Indify { public boolean overwrite = false; public boolean quiet = false; public boolean verbose = false; - public boolean transitionalJSR292 = true; // default to false later + public boolean transitionalJSR292 = false; // final version is distributed public boolean all = false; public int verifySpecifierCount = -1; @@ -158,6 +159,7 @@ public class Indify { av = avl.toArray(new String[0]); Class mainClass = Class.forName(mainClassName, true, makeClassLoader()); java.lang.reflect.Method main = mainClass.getMethod("main", String[].class); + try { main.setAccessible(true); } catch (SecurityException ex) { } main.invoke(null, (Object) av); } @@ -223,8 +225,8 @@ public class Indify { private boolean booleanOption(String s) { if (s == null) return true; switch (s) { - case "true": case "yes": case "1": return true; - case "false": case "no": case "0": return false; + case "true": case "yes": case "on": case "1": return true; + case "false": case "no": case "off": case "0": return false; } throw new IllegalArgumentException("unrecognized boolean flag="+s); } @@ -284,7 +286,7 @@ public class Indify { } File classPathFile(File pathDir, String className) { - String qualname = className+".class"; + String qualname = className.replace('.','/')+".class"; qualname = qualname.replace('/', File.separatorChar); return new File(pathDir, qualname); } @@ -339,6 +341,7 @@ public class Indify { private File findClassInPath(String name) { for (String s : classpath) { File f = classPathFile(new File(s), name); + //System.out.println("Checking for "+f); if (f.exists() && f.canRead()) { return f; } @@ -353,11 +356,11 @@ public class Indify { } } private Class transformAndLoadClass(File f) throws ClassNotFoundException, IOException { - if (verbose) System.out.println("Loading class from "+f); + if (verbose) System.err.println("Loading class from "+f); ClassFile cf = new ClassFile(f); Logic logic = new Logic(cf); boolean changed = logic.transform(); - if (verbose && !changed) System.out.println("(no change)"); + if (verbose && !changed) System.err.println("(no change)"); logic.reportPatternMethods(!verbose, keepgoing); byte[] bytes = cf.toByteArray(); return defineClass(null, bytes, 0, bytes.length); @@ -525,6 +528,7 @@ public class Indify { throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount); } } + if (!quiet) System.err.flush(); } // mark constant pool entries according to participation in patterns @@ -696,6 +700,18 @@ public class Indify { args.clear(); break; + case opc_new: + { + String type = pool.getString(CONSTANT_Class, (short)i.u2At(1)); + //System.out.println("new "+type); + switch (type) { + case "java/lang/StringBuilder": + jvm.push("StringBuilder"); + continue decode; // go to next instruction + } + break decode; // bail out + } + case opc_getstatic: { // int.class compiles to getstatic Integer.TYPE @@ -732,8 +748,9 @@ public class Indify { case opc_invokestatic: case opc_invokevirtual: + case opc_invokespecial: { - boolean hasRecv = (bc == opc_invokevirtual); + boolean hasRecv = (bc != opc_invokestatic); int methi = i.u2At(1); char mark = poolMarks[methi]; Short[] ref = pool.getMemberRef((short)methi); @@ -770,6 +787,7 @@ public class Indify { if (mark == 'T' || mark == 'H' || mark == 'I') { ownMethod = findMember(cf.methods, ref[1], ref[2]); } + //if (intrinsic != null) System.out.println("intrinsic = "+intrinsic); switch (intrinsic == null ? "" : intrinsic) { case "fromMethodDescriptorString": con = makeMethodTypeCon(args.get(0)); @@ -860,6 +878,15 @@ public class Indify { } } break decode; + case "StringBuilder.append": + // allow calls like ("value = "+x) + removeEmptyJVMSlots(args); + args.subList(1, args.size()).clear(); + continue; + case "StringBuilder.toString": + args.clear(); + args.add(intrinsic); + continue; } if (!hasRecv && ownMethod != null && patternMark != 0) { con = constants.get(ownMethod); @@ -1506,6 +1533,7 @@ public class Indify { out.write(bytes); } else { trueSize = flatten(out); + //if (!(item instanceof Code)) System.err.println("wrote complex attr name="+(int)(char)name+" size="+trueSize+" data="+Arrays.toString(flatten())); } if (trueSize != size && size >= 0) System.err.println("warning: attribute size changed "+size+" to "+trueSize); @@ -1525,7 +1553,7 @@ public class Indify { } public List attrs() { return null; } // Code overrides this public byte[] flatten() { - ByteArrayOutputStream buf = new ByteArrayOutputStream(size); + ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size)); flatten(buf); return buf.toByteArray(); } @@ -1642,6 +1670,7 @@ public class Indify { opc_invokestatic = 184, opc_invokeinterface = 185, opc_invokedynamic = 186, + opc_new = 187, opc_anewarray = 189, opc_checkcast = 192, opc_ifnull = 198, diff --git a/test/java/lang/Thread/StopBeforeStart.java b/test/java/lang/Thread/StopBeforeStart.java deleted file mode 100644 index 91b62597cb64f8541ffc258da1c3fd363d93fa09..0000000000000000000000000000000000000000 --- a/test/java/lang/Thread/StopBeforeStart.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2005, 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 4519200 - * @summary Confirm a Thread.stop before start complies with the spec - * @author Pete Soper - * - * Confirm that a thread that had its stop method invoked before start - * does properly terminate with expected exception behavior. NOTE that - * arbitrary application threads could return from their run methods faster - * than the VM can throw an async exception. - */ -public class StopBeforeStart { - - private static final int JOIN_TIMEOUT=10000; - - private class MyThrowable extends Throwable { - } - - private class Catcher implements Thread.UncaughtExceptionHandler { - private boolean nullaryStop; - private Throwable theThrowable; - private Throwable expectedThrowable; - private boolean exceptionThrown; - - Catcher(boolean nullaryStop) { - this.nullaryStop = nullaryStop; - if (!nullaryStop) { - expectedThrowable = new MyThrowable(); - } - } - - public void uncaughtException(Thread t, Throwable th) { - exceptionThrown = true; - theThrowable = th; - } - - void check(String label) throws Throwable { - if (!exceptionThrown) { - throw new RuntimeException(label + - " test:" + " missing uncaught exception"); - } - - if (nullaryStop) { - if (! (theThrowable instanceof ThreadDeath)) { - throw new RuntimeException(label + - " test:" + " expected ThreadDeath in uncaught handler"); - } - } else if (theThrowable != expectedThrowable) { - throw new RuntimeException(label + - " test:" + " wrong Throwable in uncaught handler"); - } - } - } - - private class MyRunnable implements Runnable { - public void run() { - while(true) - ; - } - } - - private class MyThread extends Thread { - public void run() { - while(true) - ; - } - } - - - public static void main(String args[]) throws Throwable { - (new StopBeforeStart()).doit(); - System.out.println("Test passed"); - } - - private void doit() throws Throwable { - - runit(false, new Thread(new MyRunnable()),"Thread"); - runit(true, new Thread(new MyRunnable()),"Thread"); - runit(false, new MyThread(),"Runnable"); - runit(true, new MyThread(),"Runnable"); - } - - private void runit(boolean nullaryStop, Thread thread, - String type) throws Throwable { - - Catcher c = new Catcher(nullaryStop); - thread.setUncaughtExceptionHandler(c); - - if (nullaryStop) { - thread.stop(); - } else { - thread.stop(c.expectedThrowable); - } - - thread.start(); - thread.join(JOIN_TIMEOUT); - - if (thread.getState() != Thread.State.TERMINATED) { - - thread.stop(); - - // Under high load this could be a false positive - throw new RuntimeException(type + - " test:" + " app thread did not terminate"); - } - - c.check(type); - } -} diff --git a/test/java/nio/channels/FileChannel/Position.java b/test/java/nio/channels/FileChannel/Position.java index dc10f27afd27f4ccf4ce105fe88a464bb9b9790f..c6c674d9f4fce738be754079b0b0573bf17a05bf 100644 --- a/test/java/nio/channels/FileChannel/Position.java +++ b/test/java/nio/channels/FileChannel/Position.java @@ -22,13 +22,16 @@ */ /* @test + * @bug 4429043 6526860 * @summary Test position method of FileChannel */ import java.io.*; -import java.nio.MappedByteBuffer; -import java.nio.channels.*; +import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.*; +import static java.nio.file.StandardOpenOption.*; +import java.nio.charset.Charset; import java.util.Random; @@ -38,32 +41,42 @@ import java.util.Random; public class Position { - private static PrintStream err = System.err; + private static final Charset ISO8859_1 = Charset.forName("8859_1"); - private static Random generator = new Random(); - - private static int CHARS_PER_LINE = File.separatorChar == '/' ? 5 : 6; - - private static File blah; + private static final Random generator = new Random(); public static void main(String[] args) throws Exception { - blah = File.createTempFile("blah", null); - blah.deleteOnExit(); + Path blah = Files.createTempFile("blah", null); + blah.toFile().deleteOnExit(); initTestFile(blah); - FileInputStream fis = new FileInputStream(blah); - FileChannel c = fis.getChannel(); + for (int i=0; i<10; i++) { + try (FileChannel fc = (generator.nextBoolean()) ? + FileChannel.open(blah, READ) : + new FileInputStream(blah.toFile()).getChannel()) { + for (int j=0; j<100; j++) { + long newPos = generator.nextInt(1000); + fc.position(newPos); + if (fc.position() != newPos) + throw new RuntimeException("Position failed"); + } + } + } - for(int i=0; i<100; i++) { - long newPos = generator.nextInt(1000); - c.position(newPos); - if (c.position() != newPos) - throw new RuntimeException("Position failed"); + for (int i=0; i<10; i++) { + try (FileChannel fc = (generator.nextBoolean()) ? + FileChannel.open(blah, APPEND) : + new FileOutputStream(blah.toFile(), true).getChannel()) { + for (int j=0; j<10; j++) { + if (fc.position() != fc.size()) + throw new RuntimeException("Position expected to be size"); + byte[] buf = new byte[generator.nextInt(100)]; + fc.write(ByteBuffer.wrap(buf)); + } + } } - c.close(); - fis.close(); - blah.delete(); + Files.delete(blah); } /** @@ -78,19 +91,15 @@ public class Position { * 3999 * */ - private static void initTestFile(File blah) throws Exception { - FileOutputStream fos = new FileOutputStream(blah); - BufferedWriter awriter - = new BufferedWriter(new OutputStreamWriter(fos, "8859_1")); - - for(int i=0; i<4000; i++) { - String number = new Integer(i).toString(); - for (int h=0; h<4-number.length(); h++) - awriter.write("0"); - awriter.write(""+i); - awriter.newLine(); + private static void initTestFile(Path blah) throws IOException { + try (BufferedWriter awriter = Files.newBufferedWriter(blah, ISO8859_1)) { + for(int i=0; i<4000; i++) { + String number = new Integer(i).toString(); + for (int h=0; h<4-number.length(); h++) + awriter.write("0"); + awriter.write(""+i); + awriter.newLine(); + } } - awriter.flush(); - awriter.close(); } } diff --git a/test/java/nio/file/Files/walkFileTree/PrintFileTree.java b/test/java/nio/file/Files/walkFileTree/PrintFileTree.java index 83d554dc6898b4fedb0625906937f35936a87ab4..048298bcb19689affeb22aefa81ebd34392d590b 100644 --- a/test/java/nio/file/Files/walkFileTree/PrintFileTree.java +++ b/test/java/nio/file/Files/walkFileTree/PrintFileTree.java @@ -54,6 +54,7 @@ public class PrintFileTree { if (followLinks) options.add(FileVisitOption.FOLLOW_LINKS); + final boolean follow = followLinks; final boolean reportCycles = printCycles; Files.walkFileTree(dir, options, Integer.MAX_VALUE, new FileVisitor() { @Override @@ -63,8 +64,7 @@ public class PrintFileTree { } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - if (!attrs.isDirectory() || reportCycles) - System.out.println(file); + System.out.println(file); return FileVisitResult.CONTINUE; } @Override @@ -79,11 +79,13 @@ public class PrintFileTree { public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { - if (reportCycles && (exc instanceof FileSystemLoopException)) { - System.out.println(file); + if (follow && (exc instanceof FileSystemLoopException)) { + if (reportCycles) + System.out.println(file); return FileVisitResult.CONTINUE; + } else { + throw exc; } - throw exc; } }); } diff --git a/test/java/util/Hashtable/SerializationDeadlock.java b/test/java/util/Hashtable/SerializationDeadlock.java new file mode 100644 index 0000000000000000000000000000000000000000..c9048fb691f2c1e83238c9401d215e202b43701a --- /dev/null +++ b/test/java/util/Hashtable/SerializationDeadlock.java @@ -0,0 +1,156 @@ +/* + * 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 + * 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. + * + * ------------------------------------------- + * + * Portions Copyright (c) 2010, 2011 IBM Corporation + */ + +/* + * @test + * @bug 6927486 + * @summary Serializing Hashtable objects which refer to each other should not be able to deadlock. + * @author Neil Richards , + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.concurrent.CyclicBarrier; + +public class SerializationDeadlock { + public static void main(final String[] args) throws Exception { + // Test for Hashtable serialization deadlock + final Hashtable h1 = new Hashtable<>(); + final Hashtable h2 = new Hashtable<>(); + final TestBarrier testStart = new TestBarrier(3); + + // Populate the hashtables so that they refer to each other + h1.put(testStart, h2); + h2.put(testStart, h1); + + final CyclicBarrier testEnd = new CyclicBarrier(3); + final TestThread t1 = new TestThread(h1, testEnd); + final TestThread t2 = new TestThread(h2, testEnd); + + t1.start(); + t2.start(); + + // Wait for both test threads to have initiated serialization + // of the 'testStart' object (and hence of both 'h1' and 'h2') + testStart.await(); + + // Wait for both test threads to successfully finish serialization + // of 'h1' and 'h2'. + System.out.println("Waiting for Hashtable serialization to complete ..."); + System.out.println("(This test will hang if serialization deadlocks)"); + testEnd.await(); + System.out.println("Test PASSED: serialization completed successfully"); + + TestThread.handleExceptions(); + } + + static final class TestBarrier extends CyclicBarrier + implements Serializable { + public TestBarrier(final int count) { + super(count); + } + + private void writeObject(final ObjectOutputStream oos) + throws IOException { + oos.defaultWriteObject(); + // Wait until all test threads have started serializing data + try { + await(); + } catch (final Exception e) { + throw new IOException("Test ERROR: Unexpected exception caught", e); + } + } + } + + static final class TestThread extends Thread { + private static final List exceptions = new ArrayList<>(); + + private final Hashtable hashtable; + private final CyclicBarrier testEnd; + + public TestThread(final Hashtable hashtable, + final CyclicBarrier testEnd) { + this.hashtable = hashtable; + this.testEnd = testEnd; + setDaemon(true); + } + + public void run() { + try { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(hashtable); + oos.close(); + } catch (final IOException ioe) { + addException(ioe); + } finally { + try { + testEnd.await(); + } catch (Exception e) { + addException(e); + } + } + } + + private static synchronized void addException(final Exception exception) { + exceptions.add(exception); + } + + public static synchronized void handleExceptions() { + if (false == exceptions.isEmpty()) { + throw new RuntimeException(getErrorText(exceptions)); + } + } + + private static String getErrorText(final List exceptions) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + + pw.println("Test ERROR: Unexpected exceptions thrown on test threads:"); + for (Exception exception : exceptions) { + pw.print("\t"); + pw.println(exception); + for (StackTraceElement element : exception.getStackTrace()) { + pw.print("\t\tat "); + pw.println(element); + } + } + + pw.close(); + return sw.toString(); + } + } +} + diff --git a/test/java/util/Hashtable/SimpleSerialization.java b/test/java/util/Hashtable/SimpleSerialization.java new file mode 100644 index 0000000000000000000000000000000000000000..257cf105b5742f2ce59e993731efa69613d26789 --- /dev/null +++ b/test/java/util/Hashtable/SimpleSerialization.java @@ -0,0 +1,86 @@ +/* + * 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 + * 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. + * + * ------------------------------------------- + * + * Portions Copyright (c) 2010, 2011 IBM Corporation + */ + +/* + * @test + * @bug 6927486 + * @summary A serialized Hashtable can be de-serialized properly. + * @author Neil Richards , + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Hashtable; + +public class SimpleSerialization { + public static void main(final String[] args) throws Exception { + Hashtable h1 = new Hashtable<>(); + + h1.put("key", "value"); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(h1); + oos.close(); + + final byte[] data = baos.toByteArray(); + final ByteArrayInputStream bais = new ByteArrayInputStream(data); + final ObjectInputStream ois = new ObjectInputStream(bais); + + final Object deserializedObject = ois.readObject(); + ois.close(); + + if (false == h1.equals(deserializedObject)) { + throw new RuntimeException(getFailureText(h1, deserializedObject)); + } + } + + private static String getFailureText(final Object orig, final Object copy) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + + pw.println("Test FAILED: Deserialized object is not equal to the original object"); + pw.print("\tOriginal: "); + printObject(pw, orig).println(); + pw.print("\tCopy: "); + printObject(pw, copy).println(); + + pw.close(); + return sw.toString(); + } + + private static PrintWriter printObject(final PrintWriter pw, final Object o) { + pw.printf("%s@%08x", o.getClass().getName(), System.identityHashCode(o)); + return pw; + } +} diff --git a/test/java/util/TimeZone/DaylightTimeTest.java b/test/java/util/TimeZone/DaylightTimeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4b637136f96b29cf73110a71ced256762792d91c --- /dev/null +++ b/test/java/util/TimeZone/DaylightTimeTest.java @@ -0,0 +1,103 @@ +/* + * 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 6936350 + * @summary Test case for TimeZone.observesDaylightTime() + */ + +import java.util.*; +import static java.util.GregorianCalendar.*; + +public class DaylightTimeTest { + private static final int ONE_HOUR = 60 * 60 * 1000; // one hour + private static final int INTERVAL = 24 * ONE_HOUR; // one day + private static final String[] ZONES = TimeZone.getAvailableIDs(); + private static int errors = 0; + + public static void main(String[] args) { + + // Test default TimeZone + for (String id : ZONES) { + TimeZone tz = TimeZone.getTimeZone(id); + long now = System.currentTimeMillis(); + boolean observes = tz.observesDaylightTime(); + boolean found = findDSTTransition(tz, now); + if (observes != found) { + // There's a critical section. If DST ends after the + // System.currentTimeMills() call, there should be + // inconsistency in the determination. Try the same + // thing again to see the inconsistency was due to the + // critical section. + now = System.currentTimeMillis(); + observes = tz.observesDaylightTime(); + found = findDSTTransition(tz, now); + if (observes != found) { + System.err.printf("%s: observesDaylightTime() should return %s at %d%n", + tz.getID(), found, now); + errors++; + } + } + } + + // Test SimpleTimeZone in which observesDaylightTime() is + // equivalent to useDaylightTime(). + testSimpleTimeZone(new SimpleTimeZone(-8*ONE_HOUR, "X", + APRIL, 1, -SUNDAY, 2*ONE_HOUR, + OCTOBER, -1, SUNDAY, 2*ONE_HOUR, + 1*ONE_HOUR)); + testSimpleTimeZone(new SimpleTimeZone(-8*ONE_HOUR, "Y")); + + if (errors > 0) { + throw new RuntimeException("DaylightTimeTest: failed"); + } + } + + /** + * Returns true if it's `now' in DST or there's any + * standard-to-daylight transition within 50 years after `now'. + */ + private static boolean findDSTTransition(TimeZone tz, long now) { + GregorianCalendar cal = new GregorianCalendar(tz, Locale.US); + cal.setTimeInMillis(now); + cal.add(YEAR, 50); + long end = cal.getTimeInMillis(); + + for (long t = now; t < end; t += INTERVAL) { + cal.setTimeInMillis(t); + if (cal.get(DST_OFFSET) > 0) { + return true; + } + } + return false; + } + + private static void testSimpleTimeZone(SimpleTimeZone stz) { + if (stz.useDaylightTime() != stz.observesDaylightTime()) { + System.err.printf("Failed: useDaylightTime=%b, observesDaylightTime()=%b%n\t%s%n", + stz.useDaylightTime(),stz.observesDaylightTime(), stz); + errors++; + } + } +} diff --git a/test/java/util/Vector/SerializationDeadlock.java b/test/java/util/Vector/SerializationDeadlock.java new file mode 100644 index 0000000000000000000000000000000000000000..dd4471e03047132d73e412b86844cf8e97f388a7 --- /dev/null +++ b/test/java/util/Vector/SerializationDeadlock.java @@ -0,0 +1,157 @@ +/* + * 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 + * 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. + */ + +/* + * Portions Copyright (c) 2010, 2011 IBM Corporation + */ + +/* + * @test + * @bug 6934356 + * @summary Serializing Vector objects which refer to each other should not be able to deadlock. + * @author Neil Richards , + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.CyclicBarrier; + +public class SerializationDeadlock { + public static void main(final String[] args) throws Exception { + // Test for Vector serialization deadlock + final Vector v1 = new Vector<>(); + final Vector v2 = new Vector<>(); + final TestBarrier testStart = new TestBarrier(3); + + // Populate the vectors so that they refer to each other + v1.add(testStart); + v1.add(v2); + v2.add(testStart); + v2.add(v1); + + final CyclicBarrier testEnd = new CyclicBarrier(3); + final TestThread t1 = new TestThread(v1, testEnd); + final TestThread t2 = new TestThread(v2, testEnd); + + t1.start(); + t2.start(); + + // Wait for both test threads to have initiated serialization + // of the 'testStart' object (and hence of both 'v1' and 'v2') + testStart.await(); + + // Wait for both test threads to successfully finish serialization + // of 'v1' and 'v2'. + System.out.println("Waiting for Vector serialization to complete ..."); + System.out.println("(This test will hang if serialization deadlocks)"); + testEnd.await(); + System.out.println("Test PASSED: serialization completed successfully"); + + TestThread.handleExceptions(); + } + + static final class TestBarrier extends CyclicBarrier + implements Serializable { + public TestBarrier(final int count) { + super(count); + } + + private void writeObject(final ObjectOutputStream oos) + throws IOException { + oos.defaultWriteObject(); + // Wait until all test threads have started serializing data + try { + await(); + } catch (final Exception e) { + throw new IOException("Test ERROR: Unexpected exception caught", e); + } + } + } + + static final class TestThread extends Thread { + private static final List exceptions = new ArrayList<>(); + + private final Vector vector; + private final CyclicBarrier testEnd; + + public TestThread(final Vector vector, final CyclicBarrier testEnd) { + this.vector = vector; + this.testEnd = testEnd; + setDaemon(true); + } + + public void run() { + try { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(vector); + oos.close(); + } catch (final IOException ioe) { + addException(ioe); + } finally { + try { + testEnd.await(); + } catch (Exception e) { + addException(e); + } + } + } + + private static synchronized void addException(final Exception exception) { + exceptions.add(exception); + } + + public static synchronized void handleExceptions() { + if (false == exceptions.isEmpty()) { + throw new RuntimeException(getErrorText(exceptions)); + } + } + + private static String getErrorText(final List exceptions) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + + pw.println("Test ERROR: Unexpected exceptions thrown on test threads:"); + for (Exception exception : exceptions) { + pw.print("\t"); + pw.println(exception); + for (StackTraceElement element : exception.getStackTrace()) { + pw.print("\t\tat "); + pw.println(element); + } + } + + pw.close(); + return sw.toString(); + } + } +} + diff --git a/test/java/util/Vector/SimpleSerialization.java b/test/java/util/Vector/SimpleSerialization.java new file mode 100644 index 0000000000000000000000000000000000000000..f7fe2e6199f5d35c78b63dc6d82f298f34e17dc5 --- /dev/null +++ b/test/java/util/Vector/SimpleSerialization.java @@ -0,0 +1,87 @@ +/* + * 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 + * 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. + */ + +/* + * Portions Copyright (c) 2010, 2011 IBM Corporation + */ + +/* + * @test + * @bug 6934356 + * @summary A serialized Vector can be successfully de-serialized. + * @author Neil Richards , + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Vector; + +public class SimpleSerialization { + public static void main(final String[] args) throws Exception { + final Vector v1 = new Vector<>(); + + v1.add("entry1"); + v1.add("entry2"); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(v1); + oos.close(); + + final byte[] data = baos.toByteArray(); + final ByteArrayInputStream bais = new ByteArrayInputStream(data); + final ObjectInputStream ois = new ObjectInputStream(bais); + + final Object deserializedObject = ois.readObject(); + ois.close(); + + if (false == v1.equals(deserializedObject)) { + throw new RuntimeException(getFailureText(v1, deserializedObject)); + } + } + + private static String getFailureText(final Object orig, final Object copy) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + + pw.println("Test FAILED: Deserialized object is not equal to the original object"); + pw.print("\tOriginal: "); + printObject(pw, orig).println(); + pw.print("\tCopy: "); + printObject(pw, copy).println(); + + pw.close(); + return sw.toString(); + } + + private static PrintWriter printObject(final PrintWriter pw, final Object o) { + pw.printf("%s@%08x", o.getClass().getName(), System.identityHashCode(o)); + return pw; + } +} diff --git a/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java b/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java index 34f0722d8bd8ffb69c418c37c3cd5fd4b97b0193..755ff9c2d9769e58db141fa4436be81822dde88d 100644 --- a/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java +++ b/test/java/util/concurrent/BlockingQueue/CancelledProducerConsumerLoops.java @@ -124,11 +124,11 @@ public class CancelledProducerConsumerLoops { oneRun(new ArrayBlockingQueue(CAPACITY), pairs, iters); oneRun(new LinkedBlockingQueue(CAPACITY), pairs, iters); oneRun(new LinkedBlockingDeque(CAPACITY), pairs, iters); - oneRun(new LinkedTransferQueue(), pairs, iters); oneRun(new SynchronousQueue(), pairs, iters / 8); - /* PriorityBlockingQueue is unbounded + /* unbounded queue implementations are prone to OOME oneRun(new PriorityBlockingQueue(iters / 2 * pairs), pairs, iters / 4); + oneRun(new LinkedTransferQueue(), pairs, iters); */ } diff --git a/test/javax/swing/JComponent/6989617/bug6989617.java b/test/javax/swing/JComponent/6989617/bug6989617.java index 7f7aa958bf6d567eaaffaf63726b1eebdb5c2e92..7c85e255f72a5fe8914988f8f71fbfe281f9654b 100644 --- a/test/javax/swing/JComponent/6989617/bug6989617.java +++ b/test/javax/swing/JComponent/6989617/bug6989617.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,76 +28,107 @@ @run main bug6989617 */ +import sun.awt.SunToolkit; + import javax.swing.*; import java.awt.*; public class bug6989617 { + private static MyPanel panel; + private static JButton button; - private boolean isPaintingOrigin; - private boolean innerPanelRepainted, outerPanelRepainted; - - public bug6989617() { + public static void main(String... args) throws Exception { + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + JFrame frame = new JFrame(); + frame. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + panel = new MyPanel(); - final JButton button = new JButton("button"); + button = new JButton("Hello"); + panel.add(button); + frame.add(panel); - JPanel innerPanel = new JPanel() { - protected boolean isPaintingOrigin() { - return isPaintingOrigin; + frame.setSize(200, 300); + frame.setVisible(true); } - - public void repaint(long tm, int x, int y, int width, int height) { - if (button.getParent() != null) { - innerPanelRepainted = true; - if (!button.getSize().equals(new Dimension(width, height))) { - throw new RuntimeException("Wrong size of the dirty area"); - } - if (!button.getLocation().equals(new Point(x, y))) { - throw new RuntimeException("Wrong location of the dirty area"); - } + }); + // Testing the panel as a painting origin, + // the panel.paintImmediately() must be triggered + // when button.repaint() is called + toolkit.realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + if (panel.getPaintRectangle() != null) { + throw new RuntimeException("paint rectangle is not null"); } - super.repaint(tm, x, y, width, height); + button.repaint(); } - }; - - JPanel outerPanel = new JPanel() { - protected boolean isPaintingOrigin() { - return isPaintingOrigin; + }); + toolkit.realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + Rectangle pr = panel.getPaintRectangle(); + if (!pr.getSize().equals(button.getSize())) { + throw new RuntimeException("wrong size of the dirty area"); + } + if (!pr.getLocation().equals(button.getLocation())) { + throw new RuntimeException("wrong location of the dirty area"); + } } - - public void repaint(long tm, int x, int y, int width, int height) { - if (button.getParent() != null) { - outerPanelRepainted = true; - if (!button.getSize().equals(new Dimension(width, height))) { - throw new RuntimeException("Wrong size of the dirty area"); - } + }); + // Testing the panel as NOT a painting origin + // the panel.paintImmediately() must NOT be triggered + // when button.repaint() is called + toolkit.realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + panel.resetPaintRectangle(); + panel.setPaintingOrigin(false); + if (panel.getPaintRectangle() != null) { + throw new RuntimeException("paint rectangle is not null"); + } + button.repaint(); + } + }); + toolkit.realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + if(panel.getPaintRectangle() != null) { + throw new RuntimeException("paint rectangle is not null"); } - super.repaint(tm, x, y, width, height); + System.out.println("Test passed..."); } - }; + }); + } + static class MyPanel extends JPanel { + private boolean isPaintingOrigin = true; + private Rectangle paintRectangle; - outerPanel.add(innerPanel); - innerPanel.add(button); + { + setLayout(new GridBagLayout()); + } - outerPanel.setSize(100, 100); - innerPanel.setBounds(10, 10, 50, 50); - button.setBounds(10, 10, 20, 20); + public boolean isPaintingOrigin() { + return isPaintingOrigin; + } - if (innerPanelRepainted || outerPanelRepainted) { - throw new RuntimeException("Repainted flag is unexpectedly on"); + public void setPaintingOrigin(boolean paintingOrigin) { + isPaintingOrigin = paintingOrigin; } - button.repaint(); - if (innerPanelRepainted || outerPanelRepainted) { - throw new RuntimeException("Repainted flag is unexpectedly on"); + + public void paintImmediately(int x, int y, int w, int h) { + super.paintImmediately(x, y, w, h); + paintRectangle = new Rectangle(x, y, w, h); } - isPaintingOrigin = true; - button.repaint(); - if (!innerPanelRepainted || !outerPanelRepainted) { - throw new RuntimeException("Repainted flag is unexpectedly off"); + + public Rectangle getPaintRectangle() { + return paintRectangle == null? null: new Rectangle(paintRectangle); } - } - public static void main(String... args) throws Exception { - new bug6989617(); + public void resetPaintRectangle() { + this.paintRectangle = null; + } } } diff --git a/test/javax/swing/JFileChooser/6798062/bug6798062.html b/test/javax/swing/JFileChooser/6798062/bug6798062.html index 12955f6eee722d6892e1bdc337140d5c3e5746ee..80a3e1c503d80a6a7c58d1d1db8194b0f79692ee 100644 --- a/test/javax/swing/JFileChooser/6798062/bug6798062.html +++ b/test/javax/swing/JFileChooser/6798062/bug6798062.html @@ -1,6 +1,8 @@ +The test is suitable only for Windows + 1. Create a link 2. Copy path to the link into TextField 3. Run the Windows Task Manager. Select the Processes tab and find the java process diff --git a/test/javax/swing/JFileChooser/6798062/bug6798062.java b/test/javax/swing/JFileChooser/6798062/bug6798062.java index 44d59dc82783bb3780031a3e7edb0fc83df8b266..45df0b643de68bd3964c9f03013257ca0940c257 100644 --- a/test/javax/swing/JFileChooser/6798062/bug6798062.java +++ b/test/javax/swing/JFileChooser/6798062/bug6798062.java @@ -28,6 +28,7 @@ @run applet/manual=done bug6798062.html */ +import sun.awt.OSInfo; import sun.awt.shell.ShellFolder; import javax.swing.*; @@ -68,13 +69,23 @@ public class bug6798062 extends JApplet { add(initialize()); } - private JPanel initialize() { - File file = new File("c:/"); + private JComponent initialize() { + if (OSInfo.getOSType() != OSInfo.OSType.WINDOWS) { + return new JLabel("The test is suitable only for Windows"); + } + + String tempDir = System.getProperty("java.io.tmpdir"); + + if (tempDir.length() == 0) { // 'java.io.tmpdir' isn't guaranteed to be defined + tempDir = System.getProperty("user.home"); + } + + System.out.println("Temp directory: " + tempDir); try { - folder = ShellFolder.getShellFolder(file); + folder = ShellFolder.getShellFolder(new File(tempDir)); } catch (FileNotFoundException e) { - fail("Directory " + file.getPath() + " not found"); + fail("Directory " + tempDir + " not found"); } slider.setMajorTickSpacing(10); diff --git a/test/javax/swing/JScrollBar/6542335/bug6542335.java b/test/javax/swing/JScrollBar/6542335/bug6542335.java index d4ed4736e346a2f440632d5be534d37169619d3e..b71ae1cde51c9fcd9806c108ae29ea95f6884f01 100644 --- a/test/javax/swing/JScrollBar/6542335/bug6542335.java +++ b/test/javax/swing/JScrollBar/6542335/bug6542335.java @@ -69,8 +69,6 @@ public class bug6542335 { frame.setSize(200, 100); frame.setVisible(true); - - thumbBounds[0] = new Rectangle(ui.getThumbBounds()); } }); @@ -78,6 +76,8 @@ public class bug6542335 { SwingUtilities.invokeAndWait(new Runnable() { public void run() { + thumbBounds[0] = new Rectangle(ui.getThumbBounds()); + Point l = sb.getLocationOnScreen(); robot.mouseMove(l.x + (int) (0.75 * sb.getWidth()), l.y + sb.getHeight() / 2); diff --git a/test/javax/swing/LookAndFeel/6474153/bug6474153.java b/test/javax/swing/LookAndFeel/6474153/bug6474153.java new file mode 100644 index 0000000000000000000000000000000000000000..4ee5198f4cba002078e23e6b1344287010f47155 --- /dev/null +++ b/test/javax/swing/LookAndFeel/6474153/bug6474153.java @@ -0,0 +1,53 @@ +/* + * 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 6474153 + * @summary LookAndFeel.makeKeyBindings(...) doesn't ignore last element in keyBindingList with odd size + * @author Alexander Potochkin + */ + +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.JTextComponent; + +public class bug6474153 { + + public static void main(String... args) throws Exception { + checkArray(LookAndFeel.makeKeyBindings(new Object[] {"UP", DefaultEditorKit.upAction} )); + checkArray(LookAndFeel.makeKeyBindings(new Object[] {"UP", DefaultEditorKit.upAction, "PAGE_UP"} )); + } + + private static void checkArray(JTextComponent.KeyBinding[] keyActionArray) { + if (keyActionArray.length != 1) { + throw new RuntimeException("Wrong array lenght!"); + } + if (!DefaultEditorKit.upAction.equals(keyActionArray[0].actionName)) { + throw new RuntimeException("Wrong action name!"); + } + if (!KeyStroke.getKeyStroke("UP").equals(keyActionArray[0].key)) { + throw new RuntimeException("Wrong keystroke!"); + } + } +} diff --git a/test/sun/java2d/pipe/RegionOps.java b/test/sun/java2d/pipe/RegionOps.java index 30f8c223a2526b1662d9172becf00108fa5856ba..e02f280b1feea0b3d235459c07c151e856ed6574 100644 --- a/test/sun/java2d/pipe/RegionOps.java +++ b/test/sun/java2d/pipe/RegionOps.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 %W% %E% * @bug 6504874 diff --git a/test/sun/security/krb5/auto/BadKdc1.java b/test/sun/security/krb5/auto/BadKdc1.java index 524e6d32e6778c727b2e1c10ece83c57837f43a8..a4f52b490e290cf0d44cf40d3cda4e4294b30c48 100644 --- a/test/sun/security/krb5/auto/BadKdc1.java +++ b/test/sun/security/krb5/auto/BadKdc1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,7 +24,7 @@ /* * @test * @bug 6843127 - * @run main/timeout=300 BadKdc1 + * @run main/othervm/timeout=300 BadKdc1 * @summary krb5 should not try to access unavailable kdc too often */ diff --git a/test/sun/security/krb5/auto/BadKdc2.java b/test/sun/security/krb5/auto/BadKdc2.java index 88b7b0cdf7b117ca9c68a7087e553ecce67a3592..9a5564292934de6161d9920167cf720a25ba3183 100644 --- a/test/sun/security/krb5/auto/BadKdc2.java +++ b/test/sun/security/krb5/auto/BadKdc2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,7 +24,7 @@ /* * @test * @bug 6843127 - * @run main/timeout=300 BadKdc2 + * @run main/othervm/timeout=300 BadKdc2 * @summary krb5 should not try to access unavailable kdc too often */ diff --git a/test/sun/security/krb5/auto/BadKdc3.java b/test/sun/security/krb5/auto/BadKdc3.java index 95399b072c3c62cd3e4fedcb0024aaa30fc57817..c9ce4db382762f5cd3f9f15f82ef492c7243dd35 100644 --- a/test/sun/security/krb5/auto/BadKdc3.java +++ b/test/sun/security/krb5/auto/BadKdc3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,7 +24,7 @@ /* * @test * @bug 6843127 - * @run main/timeout=300 BadKdc3 + * @run main/othervm/timeout=300 BadKdc3 * @summary krb5 should not try to access unavailable kdc too often */ diff --git a/test/sun/security/krb5/auto/BadKdc4.java b/test/sun/security/krb5/auto/BadKdc4.java index 3a148712c05c391e10274aaae315f71ac1db1234..eef77f282c4281e9e4381c98858c5414b4046b1b 100644 --- a/test/sun/security/krb5/auto/BadKdc4.java +++ b/test/sun/security/krb5/auto/BadKdc4.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,7 +24,7 @@ /* * @test * @bug 6843127 - * @run main/timeout=300 BadKdc4 + * @run main/othervm/timeout=300 BadKdc4 * @summary krb5 should not try to access unavailable kdc too often */ diff --git a/test/sun/security/krb5/auto/CleanState.java b/test/sun/security/krb5/auto/CleanState.java index 316a730edfe577f5a1e95683c0cfd10b9c11ecc7..fbd8785cbab2f7a07fb986a1e3fc2f2c3e9be694 100644 --- a/test/sun/security/krb5/auto/CleanState.java +++ b/test/sun/security/krb5/auto/CleanState.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 @@ -24,6 +24,7 @@ /* * @test * @bug 6716534 + * @run main/othervm CleanState * @summary Krb5LoginModule has not cleaned temp info between authentication attempts */ import com.sun.security.auth.module.Krb5LoginModule; diff --git a/test/sun/security/krb5/auto/CrossRealm.java b/test/sun/security/krb5/auto/CrossRealm.java index 8b7ecc3d6ee47e078540e2de52848f41395c0e3c..ecaafedaa62dd6cfea99d55ac1230becf5b3c2bf 100644 --- a/test/sun/security/krb5/auto/CrossRealm.java +++ b/test/sun/security/krb5/auto/CrossRealm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 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 @@ -24,6 +24,7 @@ /* * @test * @bug 6706974 + * @run main/othervm CrossRealm * @summary Add krb5 test infrastructure */ import java.io.File; diff --git a/test/sun/security/krb5/auto/HttpNegotiateServer.java b/test/sun/security/krb5/auto/HttpNegotiateServer.java index e25e60d6ab54dd8497fab111d00dc35dd11daad9..423ef0d853d5df91443e25c08c0276534f9156d2 100644 --- a/test/sun/security/krb5/auto/HttpNegotiateServer.java +++ b/test/sun/security/krb5/auto/HttpNegotiateServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,6 +24,7 @@ /* * @test * @bug 6578647 6829283 + * @run main/othervm HttpNegotiateServer * @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication() * @summary HTTP/Negotiate: Authenticator triggered again when user cancels the first one */ diff --git a/test/sun/security/krb5/auto/IgnoreChannelBinding.java b/test/sun/security/krb5/auto/IgnoreChannelBinding.java index 0995fd8ed619c2b5f4467d0457c98648ba1a81f1..e2641a9a4f9d283964540316548ea36b6f3a69c4 100644 --- a/test/sun/security/krb5/auto/IgnoreChannelBinding.java +++ b/test/sun/security/krb5/auto/IgnoreChannelBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,6 +24,7 @@ /* * @test * @bug 6851973 + * @run main/othervm IgnoreChannelBinding * @summary ignore incoming channel binding if acceptor does not set one */ diff --git a/test/sun/security/krb5/auto/KerberosHashEqualsTest.java b/test/sun/security/krb5/auto/KerberosHashEqualsTest.java index b6cd147f6cf4267cf8a61258fb3ae517f884d0fa..e7c026dab03a4686577a18a490556a12b46c143a 100644 --- a/test/sun/security/krb5/auto/KerberosHashEqualsTest.java +++ b/test/sun/security/krb5/auto/KerberosHashEqualsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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,6 +24,7 @@ /* * @test * @bug 4641821 + * @run main/othervm KerberosHashEqualsTest * @summary hashCode() and equals() for KerberosKey and KerberosTicket */ diff --git a/test/sun/security/krb5/auto/LifeTimeInSeconds.java b/test/sun/security/krb5/auto/LifeTimeInSeconds.java index 9f948cc8e67ab3cfb82c78604d2d129478a39be6..9c518ff6cbb52dc1e5addb3bd7723f221bae3900 100644 --- a/test/sun/security/krb5/auto/LifeTimeInSeconds.java +++ b/test/sun/security/krb5/auto/LifeTimeInSeconds.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,6 +24,7 @@ /* * @test * @bug 6857802 + * @run main/othervm LifeTimeInSeconds * @summary GSS getRemainingInitLifetime method returns milliseconds not seconds */ import org.ietf.jgss.GSSCredential; diff --git a/test/sun/security/krb5/auto/LoginModuleOptions.java b/test/sun/security/krb5/auto/LoginModuleOptions.java index f22f774316fee21f63a19119effec86503d39ada..a6dd33b80298782084c53b7d2ebaeb81c5b876c6 100644 --- a/test/sun/security/krb5/auto/LoginModuleOptions.java +++ b/test/sun/security/krb5/auto/LoginModuleOptions.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 @@ -24,6 +24,7 @@ /* * @test * @bug 6765491 + * @run main/othervm LoginModuleOptions * @summary Krb5LoginModule a little too restrictive, and the doc is not clear. */ import com.sun.security.auth.module.Krb5LoginModule; diff --git a/test/sun/security/krb5/auto/MaxRetries.java b/test/sun/security/krb5/auto/MaxRetries.java index 4f13878c98d65ec062814955e75626f65bc13ed1..fec1bec68a64d1c7038e927aaa6b77b632aaf6ca 100644 --- a/test/sun/security/krb5/auto/MaxRetries.java +++ b/test/sun/security/krb5/auto/MaxRetries.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,7 +24,7 @@ /* * @test * @bug 6844193 - * @run main/timeout=300 MaxRetries + * @run main/othervm/timeout=300 MaxRetries * @summary support max_retries in krb5.conf */ diff --git a/test/sun/security/krb5/auto/MoreKvno.java b/test/sun/security/krb5/auto/MoreKvno.java index 84cfa362d0d8718ec0b79da6c335cd0631b5ecd1..20cac93d0a0a2a598b53de6ca4df126867739642 100644 --- a/test/sun/security/krb5/auto/MoreKvno.java +++ b/test/sun/security/krb5/auto/MoreKvno.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,6 +25,7 @@ * @test * @bug 6893158 * @bug 6907425 + * @run main/othervm MoreKvno * @summary AP_REQ check should use key version number */ diff --git a/test/sun/security/krb5/auto/NewSalt.java b/test/sun/security/krb5/auto/NewSalt.java index 54cc9562f37f77b8e2588e8078b355496fa11f4d..2a0a17fcd9fd1aa52cbed3999e1d747695f705a3 100644 --- a/test/sun/security/krb5/auto/NewSalt.java +++ b/test/sun/security/krb5/auto/NewSalt.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 @@ -25,7 +25,7 @@ * @test * @bug 6960894 * @summary Better AS-REQ creation and processing - * @run main NewSalt + * @run main/othervm NewSalt * @run main/othervm -Dnopreauth NewSalt * @run main/othervm -Donlyonepreauth NewSalt */ diff --git a/test/sun/security/krb5/auto/NonMutualSpnego.java b/test/sun/security/krb5/auto/NonMutualSpnego.java index f5b387b1186d4d3dfa1f2bc9168c7720417c9036..304dca1be89fdb7f9cdd8ff159914d78a802b7be 100644 --- a/test/sun/security/krb5/auto/NonMutualSpnego.java +++ b/test/sun/security/krb5/auto/NonMutualSpnego.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 @@ -24,6 +24,7 @@ /* * @test * @bug 6733095 + * @run main/othervm NonMutualSpnego * @summary Failure when SPNEGO request non-Mutual */ diff --git a/test/sun/security/krb5/auto/SSL.java b/test/sun/security/krb5/auto/SSL.java index 1deae8e6f16b5b30af9f883e5a1237478cb5c7c4..7bd4601481e4abdf7b66c91c9ec650d070a0ba77 100644 --- a/test/sun/security/krb5/auto/SSL.java +++ b/test/sun/security/krb5/auto/SSL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,16 +25,16 @@ * @test * @bug 6894643 6913636 * @summary Test JSSE Kerberos ciphersuite - * @run main SSL TLS_KRB5_WITH_RC4_128_SHA - * @run main SSL TLS_KRB5_WITH_RC4_128_MD5 - * @run main SSL TLS_KRB5_WITH_3DES_EDE_CBC_SHA - * @run main SSL TLS_KRB5_WITH_3DES_EDE_CBC_MD5 - * @run main SSL TLS_KRB5_WITH_DES_CBC_SHA - * @run main SSL TLS_KRB5_WITH_DES_CBC_MD5 - * @run main SSL TLS_KRB5_EXPORT_WITH_RC4_40_SHA - * @run main SSL TLS_KRB5_EXPORT_WITH_RC4_40_MD5 - * @run main SSL TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA - * @run main SSL TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 + * @run main/othervm SSL TLS_KRB5_WITH_RC4_128_SHA + * @run main/othervm SSL TLS_KRB5_WITH_RC4_128_MD5 + * @run main/othervm SSL TLS_KRB5_WITH_3DES_EDE_CBC_SHA + * @run main/othervm SSL TLS_KRB5_WITH_3DES_EDE_CBC_MD5 + * @run main/othervm SSL TLS_KRB5_WITH_DES_CBC_SHA + * @run main/othervm SSL TLS_KRB5_WITH_DES_CBC_MD5 + * @run main/othervm SSL TLS_KRB5_EXPORT_WITH_RC4_40_SHA + * @run main/othervm SSL TLS_KRB5_EXPORT_WITH_RC4_40_MD5 + * @run main/othervm SSL TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA + * @run main/othervm SSL TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 */ import java.io.*; import java.net.InetAddress; diff --git a/test/sun/security/krb5/auto/SpnegoReqFlags.java b/test/sun/security/krb5/auto/SpnegoReqFlags.java index 75d6b884bbbf7980f469077be1fd72c744abf04a..62c55048d1f27273e5f52b6ed3908dbaa73f2e91 100644 --- a/test/sun/security/krb5/auto/SpnegoReqFlags.java +++ b/test/sun/security/krb5/auto/SpnegoReqFlags.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,6 +24,7 @@ /* * @test * @bug 6815182 + * @run main/othervm SpnegoReqFlags * @summary GSSAPI/SPNEGO does not work with server using MIT Kerberos library */ diff --git a/test/sun/security/krb5/auto/Test5653.java b/test/sun/security/krb5/auto/Test5653.java index 71fc92b823f0eb11fd5b3c3f80c3bda1902fdcc4..4384b87ee0b4a26d98412161ba40195e0edd67c3 100644 --- a/test/sun/security/krb5/auto/Test5653.java +++ b/test/sun/security/krb5/auto/Test5653.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,6 +24,7 @@ /* * @test * @bug 6895424 + * @run main/othervm Test5653 * @summary RFC 5653 */