OpenJDK / portola / portola
changeset 20542:a7dcd7811f02
Merge
author | lana |
---|---|
date | Tue, 08 Oct 2013 14:57:32 -0700 |
parents | 5f3e7611790a bd0a8b142cb3 |
children | 298a19a1a1f6 |
files | jdk/makefiles/CompileJavaClasses.gmk jdk/makefiles/CreateJars.gmk jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java jdk/src/share/classes/java/time/chrono/ChronoDateImpl.java jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java |
diffstat | 265 files changed, 12825 insertions(+), 3613 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/make/docs/Makefile Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/make/docs/Makefile Tue Oct 08 14:57:32 2013 -0700 @@ -50,6 +50,7 @@ DEV_DOCS_URL-5 = http://java.sun.com/j2se/1.5.0/docs/index.html DEV_DOCS_URL-6 = http://download.oracle.com/javase/6/docs/index.html DEV_DOCS_URL-7 = http://download.oracle.com/javase/7/docs/index.html +DEV_DOCS_URL-8 = http://download.oracle.com/javase/8/docs/index.html DEV_DOCS_URL = $(DEV_DOCS_URL-$(JDK_MINOR_VERSION)) DOCS_BASE_URL = http://download.oracle.com/javase/7/docs
--- a/jdk/make/java/java/FILES_c.gmk Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/make/java/java/FILES_c.gmk Tue Oct 08 14:57:32 2013 -0700 @@ -33,6 +33,7 @@ Console_md.c \ Double.c \ Executable.c \ + Field.c \ FileDescriptor_md.c \ FileInputStream.c \ FileInputStream_md.c \
--- a/jdk/make/java/java/mapfile-vers Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/make/java/java/mapfile-vers Tue Oct 08 14:57:32 2013 -0700 @@ -190,6 +190,8 @@ Java_java_lang_reflect_Array_setLong; Java_java_lang_reflect_Array_setShort; Java_java_lang_reflect_Executable_getParameters0; + Java_java_lang_reflect_Executable_getTypeAnnotationBytes0; + Java_java_lang_reflect_Field_getTypeAnnotationBytes0; Java_java_lang_Runtime_freeMemory; Java_java_lang_Runtime_maxMemory; Java_java_lang_Runtime_gc;
--- a/jdk/make/netbeans/common/java-data-native.ent Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/make/netbeans/common/java-data-native.ent Tue Oct 08 14:57:32 2013 -0700 @@ -31,14 +31,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> -<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3"> +<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4"> <compilation-unit> <package-root>${root}/src/share/classes</package-root> <package-root>${root}/src/macosx/classes</package-root> <package-root>${root}/src/solaris/classes</package-root> <package-root>${root}/src/windows/classes</package-root> <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> - <built-to>${root}/build/${platform}-${arch}/classes</built-to> + <built-to>${root}/../build/${platform}-${arch}/jdk/classes</built-to> <javadoc-built-to>${root}/build/${platform}-${arch}/docs/api</javadoc-built-to> <source-level>1.8</source-level> </compilation-unit>
--- a/jdk/make/netbeans/common/java-data-no-native.ent Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/make/netbeans/common/java-data-no-native.ent Tue Oct 08 14:57:32 2013 -0700 @@ -31,11 +31,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> -<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3"> +<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4"> <compilation-unit> <package-root>${root}/src/share/classes</package-root> <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> - <built-to>${root}/build/${platform}-${arch}/classes</built-to> + <built-to>${root}/../build/${platform}-${arch}/jdk/classes</built-to> <javadoc-built-to>${root}/build/${platform}-${arch}/docs/api</javadoc-built-to> <source-level>1.8</source-level> </compilation-unit>
--- a/jdk/make/tools/src/build/tools/buildmetaindex/BuildMetaIndex.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/make/tools/src/build/tools/buildmetaindex/BuildMetaIndex.java Tue Oct 08 14:57:32 2013 -0700 @@ -174,6 +174,44 @@ private HashMap<String, HashSet<String>> knownPrefixMap = new HashMap<>(); /* + * A class for mapping package prefixes to the number of + * levels of package elements to include. + */ + static class ExtraLevel { + public ExtraLevel(String prefix, int levels) { + this.prefix = prefix; + this.levels = levels; + } + String prefix; + int levels; + } + + /* + * A list of the special-cased package names. + */ + private static ArrayList<ExtraLevel> extraLevels = new ArrayList<>(); + + static { + // The order of these statements is significant, + // since we stop looking after the first match. + + // Need more precise information to disambiguate + // (illegal) references from applications to + // obsolete backported collections classes in + // com/sun/java/util + extraLevels.add(new ExtraLevel("com/sun/java/util/", Integer.MAX_VALUE)); + extraLevels.add(new ExtraLevel("com/sun/java/", 4)); + // Need more information than just first two package + // name elements to determine that classes in + // deploy.jar are not in rt.jar + extraLevels.add(new ExtraLevel("com/sun/", 3)); + // Need to make sure things in jfr.jar aren't + // confused with other com/oracle/** packages + extraLevels.add(new ExtraLevel("com/oracle/jrockit", 3)); + } + + + /* * We add maximum 5 second level entries to "sun", "java" and * "javax" entries. Tune this parameter to get a balance on the * cold start and footprint. @@ -237,39 +275,25 @@ String[] pkgElements = name.split("/"); // Last one is the class name; definitely ignoring that if (pkgElements.length > 2) { - String meta = null; - // Need more information than just first two package - // name elements to determine that classes in - // deploy.jar are not in rt.jar - if (pkgElements.length > 3 && - pkgElements[0].equals("com") && - pkgElements[1].equals("sun")) { - // Need more precise information to disambiguate - // (illegal) references from applications to - // obsolete backported collections classes in - // com/sun/java/util - if (pkgElements.length > 4 && - pkgElements[2].equals("java")) { - int bound = 0; - if (pkgElements[3].equals("util")) { - // Take all of the packages - bound = pkgElements.length - 1; - } else { - // Trim it somewhat more - bound = 4; - } - meta = ""; - for (int j = 0; j < bound; j++) { - meta += pkgElements[j] + "/"; - } - } else { - meta = pkgElements[0] + "/" + pkgElements[1] - + "/" + pkgElements[2] + "/"; + String meta = ""; + + // Default is 2 levels of package elements + int levels = 2; + + // But for some packages we add more elements + for(ExtraLevel el : extraLevels) { + if (name.startsWith(el.prefix)) { + levels = el.levels; + break; } - } else { - meta = pkgElements[0] + "/" + pkgElements[1] + "/"; + } + for (int i = 0; i < levels && i < pkgElements.length - 1; i++) { + meta += pkgElements[i] + "/"; } - indexSet.add(meta); + + if (!meta.equals("")) { + indexSet.add(meta); + } } } // end of "while" loop;
--- a/jdk/makefiles/CompileJavaClasses.gmk Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/makefiles/CompileJavaClasses.gmk Tue Oct 08 14:57:32 2013 -0700 @@ -237,10 +237,10 @@ # These files do not appear in the build result of the old build. This # is because they are generated sources, but the AUTO_JAVA_FILES won't -# pick them up since they aren't generated when the source dirs are +# pick them up since they aren't generated when the source dirs are # searched and they aren't referenced by any other classes so they won't # be picked up by implicit compilation. On a rebuild, they are picked up -# and compiled. Exclude them here to produce the same rt.jar as the old +# and compiled. Exclude them here to produce the same rt.jar as the old # build does when building just once. EXFILES+=javax/swing/plaf/nimbus/InternalFrameTitlePanePainter.java \ javax/swing/plaf/nimbus/OptionPaneMessageAreaPainter.java \ @@ -308,19 +308,6 @@ ########################################################################################## -ifndef OPENJDK - - $(eval $(call SetupJavaCompilation,BUILD_ALTCLASSES,\ - SETUP:=GENERATE_JDKBYTECODE,\ - SRC:=$(JDK_TOPDIR)/src/closed/share/altclasses, \ - BIN:=$(JDK_OUTPUTDIR)/altclasses_classes)) - - $(BUILD_ALTCLASSES): $(BUILD_JDK) - -endif - -########################################################################################## - $(JDK_OUTPUTDIR)/classes/META-INF/services/com.sun.tools.xjc.Plugin: $(MKDIR) -p $(@D) $(TOUCH) $@ @@ -403,7 +390,7 @@ ########################################################################################## -all: $(BUILD_JDK) $(BUILD_ALTCLASSES) $(BUILD_JOBJC) $(BUILD_JOBJC_HEADERS) $(COPY_EXTRA) \ +all: $(BUILD_JDK) $(BUILD_JOBJC) $(BUILD_JOBJC_HEADERS) $(COPY_EXTRA) \ $(JDK_OUTPUTDIR)/classes/META-INF/services/com.sun.tools.xjc.Plugin \ $(BUILD_ACCESSBRIDGE_32) $(BUILD_ACCESSBRIDGE_64) \ $(BUILD_ACCESSBRIDGE_LEGACY)
--- a/jdk/makefiles/CreateJars.gmk Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/makefiles/CreateJars.gmk Tue Oct 08 14:57:32 2013 -0700 @@ -1003,15 +1003,6 @@ ########################################################################################## -ifndef OPENJDK - $(eval $(call SetupArchive,BUILD_ALT_RT_JAR,,\ - SRCS:=$(JDK_OUTPUTDIR)/altclasses_classes,\ - JAR:=$(IMAGES_OUTPUTDIR)/lib/alt-rt.jar)) - -endif - -########################################################################################## - # This file is imported from hotspot in Import.gmk. Copying it into images/lib so that # all jars can be found in one place when creating images in Images.gmk. It needs to be # done here so that clean targets can be simple and accurate.
--- a/jdk/makefiles/Profiles.gmk Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/makefiles/Profiles.gmk Tue Oct 08 14:57:32 2013 -0700 @@ -40,8 +40,8 @@ # imported (signed jars) rather than built. # # The incoming lists, eg PROFILE_1_JRE_JARS_FILES, are the jars to be -# included in this profile. They have the jar name relative to the lib -# directory. We have to turn these into targets by adding the +# included in this profile. They have the jar name relative to the lib +# directory. We have to turn these into targets by adding the # $(IMAGES_OUTPUTDIR)/lib prefix # # Note that some jars may be optional depending on the type of build (jdk vs. @@ -69,10 +69,6 @@ $(addprefix $(IMAGES_OUTPUTDIR)/lib/, $(PROFILE_3_JRE_JAR_FILES)) \ $(PROFILE_2_JARS) -ifdef OPENJDK - FULL_JRE_JAR_FILES := $(filter-out alt-rt.jar, $(FULL_JRE_JAR_FILES)) -endif - ifneq ($(ENABLE_JFR), true) FULL_JRE_JAR_FILES := $(filter-out jfr.jar, $(FULL_JRE_JAR_FILES)) endif @@ -107,7 +103,7 @@ ifeq ($(OPENJDK_TARGET_OS),windows) ALL_JARS += $(IMAGES_OUTPUTDIR)/lib/ext/sunmscapi.jar -endif +endif ifeq ($(OPENJDK_TARGET_OS),macosx) ALL_JARS += $(IMAGES_OUTPUTDIR)/lib/JObjC.jar @@ -142,7 +138,7 @@ $(PROFILE_1_JRE_BIN_FILES) \ $(PROFILE_2_JRE_BIN_FILES) \ $(PROFILE_3_JRE_BIN_FILES) \ - $(FULL_JRE_BIN_FILES) + $(FULL_JRE_BIN_FILES) NOT_JRE_BIN_FILES := $(filter-out $(ALL_JRE_BIN_FILES), $(NEW_ALL_BIN_LIST)) @@ -151,18 +147,18 @@ NOT_JRE_BIN_FILES += \ $(PROFILE_2_JRE_BIN_FILES) \ $(PROFILE_3_JRE_BIN_FILES) \ - $(FULL_JRE_BIN_FILES) + $(FULL_JRE_BIN_FILES) endif ifeq ($(PROFILE), profile_2) NOT_JRE_BIN_FILES += \ $(PROFILE_3_JRE_BIN_FILES) \ - $(FULL_JRE_BIN_FILES) + $(FULL_JRE_BIN_FILES) endif ifeq ($(PROFILE), profile_3) NOT_JRE_BIN_FILES += \ - $(FULL_JRE_BIN_FILES) + $(FULL_JRE_BIN_FILES) endif NOT_JRE_BIN_FILES := $(addprefix $(JDK_OUTPUTDIR)/bin/, $(NOT_JRE_BIN_FILES)) @@ -175,7 +171,7 @@ $(PROFILE_1_JRE_LIB_FILES) \ $(PROFILE_2_JRE_LIB_FILES) \ $(PROFILE_3_JRE_LIB_FILES) \ - $(FULL_JRE_LIB_FILES) + $(FULL_JRE_LIB_FILES) NOT_JRE_LIB_FILES := $(filter-out $(ALL_JRE_LIB_FILES), $(NEW_ALL_LIB_LIST)) @@ -191,18 +187,18 @@ NOT_JRE_LIB_FILES += \ $(PROFILE_2_JRE_LIB_FILES) \ $(PROFILE_3_JRE_LIB_FILES) \ - $(FULL_JRE_LIB_FILES) + $(FULL_JRE_LIB_FILES) endif ifeq ($(PROFILE), profile_2) NOT_JRE_LIB_FILES += \ $(PROFILE_3_JRE_LIB_FILES) \ - $(FULL_JRE_LIB_FILES) + $(FULL_JRE_LIB_FILES) endif ifeq ($(PROFILE), profile_3) NOT_JRE_LIB_FILES += \ - $(FULL_JRE_LIB_FILES) + $(FULL_JRE_LIB_FILES) endif # Exclude the custom jar files as these will be added back via a special rule @@ -210,7 +206,7 @@ ############################################################################### # Customization of rt.jar file contents -# These are expressed as exclusions from everything found in the +# These are expressed as exclusions from everything found in the # JDK_OUTPUTDIR/classes directory ############################################################################### @@ -231,8 +227,8 @@ # # These are specific types that must be included within a package. # There are two cases: -# - individual types in a package that is otherwise excluded at this -# profile level. The only arises if there are split packages. +# - individual types in a package that is otherwise excluded at this +# profile level. The only arises if there are split packages. # # - A higher-level package is included in a high profile where a subpackage # is included in a lower profile. Including the package in the high profile @@ -247,7 +243,7 @@ # containing package is include. Again this occurs with split packges. # # So the exclude list for each profile consists of the include lists -# for all profiles above it, together with any explicitly excluded types. +# for all profiles above it, together with any explicitly excluded types. # This is then combined with the overall RT_JAR_EXCLUDES list (which covers # things that go into other jar files). # @@ -257,7 +253,7 @@ # profile 3 includes the entire package, but it is harmless to add them # explicitly, and complex to determine if we still need to include them. # -# Need a way to express: +# Need a way to express: # for (int i = profile+1; i < 4; i++) # RT_JAR_EXCLUDES += PROFILE_$i_RTJAR_INCLUDE_PACKAGES # @@ -267,7 +263,7 @@ # # These are META-INF/services/ entries found in resources.jar. Together # resources.jar and rt.jar hold the contents of the classes directory, (the -# classes in rt.jar and everything else in resources.jar).Hence the +# classes in rt.jar and everything else in resources.jar).Hence the # include/exclude information for resources.jar is tied to that of rt.jar include profile-rtjar-includes.txt @@ -324,7 +320,7 @@ # Filter out non-OpenJDK services ifdef OPENJDK - EXCLUDED_SERVICES := META-INF/services/javax.script.ScriptEngineFactory + EXCLUDED_SERVICES := META-INF/services/javax.script.ScriptEngineFactory PROFILE_INCLUDE_METAINF_SERVICES := $(filter-out $(EXCLUDED_SERVICES),$(PROFILE_INCLUDE_METAINF_SERVICES)) endif
--- a/jdk/makefiles/mapfiles/libjava/mapfile-vers Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/makefiles/mapfiles/libjava/mapfile-vers Tue Oct 08 14:57:32 2013 -0700 @@ -190,6 +190,8 @@ Java_java_lang_reflect_Array_setLong; Java_java_lang_reflect_Array_setShort; Java_java_lang_reflect_Executable_getParameters0; + Java_java_lang_reflect_Executable_getTypeAnnotationBytes0; + Java_java_lang_reflect_Field_getTypeAnnotationBytes0; Java_java_lang_Runtime_freeMemory; Java_java_lang_Runtime_maxMemory; Java_java_lang_Runtime_gc;
--- a/jdk/makefiles/profile-includes.txt Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/makefiles/profile-includes.txt Tue Oct 08 14:57:32 2013 -0700 @@ -107,14 +107,14 @@ rmid$(EXE_SUFFIX) \ rmiregistry$(EXE_SUFFIX) -PROFILE_2_JRE_LIB_FILES := +PROFILE_2_JRE_LIB_FILES := -PROFILE_2_JRE_OTHER_FILES := +PROFILE_2_JRE_OTHER_FILES := -PROFILE_2_JRE_JAR_FILES := +PROFILE_2_JRE_JAR_FILES := -PROFILE_3_JRE_BIN_FILES := +PROFILE_3_JRE_BIN_FILES := PROFILE_3_JRE_LIB_FILES := \ $(OPENJDK_TARGET_CPU_LEGACY_LIB)/$(LIBRARY_PREFIX)hprof$(SHARED_LIBRARY_SUFFIX) \ @@ -138,7 +138,7 @@ management/management.properties \ management/snmp.acl.template -PROFILE_3_JRE_OTHER_FILES := +PROFILE_3_JRE_OTHER_FILES := PROFILE_3_JRE_JAR_FILES := \ management-agent.jar @@ -171,7 +171,6 @@ $(OPENJDK_TARGET_CPU_LEGACY_LIB)/$(LIBRARY_PREFIX)splashscreen$(SHARED_LIBRARY_SUFFIX) \ $(OPENJDK_TARGET_CPU_LEGACY_LIB)/$(LIBRARY_PREFIX)t2k$(SHARED_LIBRARY_SUFFIX) \ $(OPENJDK_TARGET_CPU_LEGACY_LIB)/$(LIBRARY_PREFIX)unpack$(SHARED_LIBRARY_SUFFIX) \ - alt-rt.jar \ charsets.jar \ cmm/CIEXYZ.pf \ cmm/GRAY.pf \ @@ -248,7 +247,6 @@ man/man1/unpack200.1 FULL_JRE_JAR_FILES := \ - alt-rt.jar \ charsets.jar \ ext/cldrdata.jar \ ext/dnsns.jar \
--- a/jdk/src/macosx/classes/apple/security/AppleProvider.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/macosx/classes/apple/security/AppleProvider.java Tue Oct 08 14:57:32 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ public AppleProvider() { /* We are the Apple provider */ - super("Apple", 1.1, info); + super("Apple", 1.8d, info); AccessController.<Object>doPrivileged(new java.security.PrivilegedAction<Object>() { public Object run() {
--- a/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java Tue Oct 08 14:57:32 2013 -0700 @@ -104,7 +104,7 @@ public SunJCE() { /* We are the "SunJCE" provider */ - super("SunJCE", 1.7d, info); + super("SunJCE", 1.8d, info); final String BLOCK_MODES = "ECB|CBC|PCBC|CTR|CTS|CFB|OFB" + "|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" +
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java Tue Oct 08 14:57:32 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -303,7 +303,7 @@ } else { if (!packfile.toLowerCase().endsWith(".pack") && !packfile.toLowerCase().endsWith(".pac")) { - System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.WIRTE_PACKGZ_FILE),packfile)); + System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.WRITE_PACKGZ_FILE),packfile)); printUsage(doPack, false, System.err); System.exit(2); }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/DriverResource.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/DriverResource.java Tue Oct 08 14:57:32 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,110 +22,99 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package com.sun.java.util.jar.pack; import java.util.ListResourceBundle; public class DriverResource extends ListResourceBundle { - public static final String VERSION ="VERSION"; - public static final String BAD_ARGUMENT ="BAD_ARGUMENT"; - public static final String BAD_OPTION ="BAD_OPTION"; - public static final String BAD_REPACK_OUTPUT="BAD_REPACK_OUTPUT"; - public static final String DETECTED_ZIP_COMMENT="DETECTED_ZIP_COMMENT"; - public static final String SKIP_FOR_REPACKED ="SKIP_FOR_REPACKED"; - public static final String WRITE_PACK_FILE ="WRITE_PACK_FILE"; - public static final String WIRTE_PACKGZ_FILE="WIRTE_PACKGZ_FILE"; - public static final String SKIP_FOR_MOVE_FAILED="SKIP_FOR_MOVE_FAILED"; - public static final String PACK_HELP="PACK_HELP"; - public static final String UNPACK_HELP ="UNPACK_HELP"; - public static final String MORE_INFO = "MORE_INFO"; - public static final String DUPLICATE_OPTION = "DUPLICATE_OPTION"; - public static final String BAD_SPEC = "BAD_SPEC"; - //The following string is duplicate in PACK and UNPACK comment,which was draw out to ruduce translation work. - private static final String PARAMETER_V = " -v, --verbose increase program verbosity"; - private static final String PARAMETER_Q = " -q, --quiet set verbosity to lowest level"; - private static final String PARAMETER_LF = " -l{F}, --log-file={F} output to the given log file, or '-' for System.out"; - private static final String PARAMETER_H = " -?, -h, --help print this message"; - private static final String PARAMETER_VER = " -V, --version print program version"; - private static final String PARAMETER_J = " -J{X} pass option X to underlying Java VM"; - + public static final String VERSION = "VERSION"; + public static final String BAD_ARGUMENT = "BAD_ARGUMENT"; + public static final String BAD_OPTION = "BAD_OPTION"; + public static final String BAD_REPACK_OUTPUT = "BAD_REPACK_OUTPUT"; + public static final String DETECTED_ZIP_COMMENT = "DETECTED_ZIP_COMMENT"; + public static final String SKIP_FOR_REPACKED = "SKIP_FOR_REPACKED"; + public static final String WRITE_PACK_FILE = "WRITE_PACK_FILE"; + public static final String WRITE_PACKGZ_FILE = "WRITE_PACKGZ_FILE"; + public static final String SKIP_FOR_MOVE_FAILED = "SKIP_FOR_MOVE_FAILED"; + public static final String PACK_HELP = "PACK_HELP"; + public static final String UNPACK_HELP = "UNPACK_HELP"; + public static final String MORE_INFO = "MORE_INFO"; + public static final String DUPLICATE_OPTION = "DUPLICATE_OPTION"; + public static final String BAD_SPEC = "BAD_SPEC"; - //The following are outputs of command 'pack200' and 'unpack200'. - //Don't translate command arguments ,words with a prefix of '-' or '--'. - // - private static final Object[][] resource= { - {VERSION,"{0} version {1}"},//parameter 0:class name;parameter 1: version value - {BAD_ARGUMENT,"Bad argument: {0}"}, - {BAD_OPTION,"Bad option: {0}={1}"},//parameter 0:option name;parameter 1:option value - {BAD_REPACK_OUTPUT,"Bad --repack output: {0}"},//parameter 0:filename - {DETECTED_ZIP_COMMENT,"Detected ZIP comment: {0}"},//parameter 0:comment - {SKIP_FOR_REPACKED,"Skipping because already repacked: {0}"},//parameter 0:filename - {WRITE_PACK_FILE,"To write a *.pack file, specify --no-gzip: {0}"},//parameter 0:filename - {WIRTE_PACKGZ_FILE,"To write a *.pack.gz file, specify --gzip: {0}"},//parameter 0:filename - {SKIP_FOR_MOVE_FAILED,"Skipping unpack because move failed: {0}"},//parameter 0:filename - {PACK_HELP,new String[]{ - "Usage: pack200 [-opt... | --option=value]... x.pack[.gz] y.jar", - "", - "Packing Options", - " -g, --no-gzip output a plain *.pack file with no zipping", - " --gzip (default) post-process the pack output with gzip", - " -G, --strip-debug remove debugging attributes while packing", - " -O, --no-keep-file-order do not transmit file ordering information", - " --keep-file-order (default) preserve input file ordering", - " -S{N}, --segment-limit={N} output segment limit (default N=1Mb)", - " -E{N}, --effort={N} packing effort (default N=5)", - " -H{h}, --deflate-hint={h} transmit deflate hint: true, false, or keep (default)", - " -m{V}, --modification-time={V} transmit modtimes: latest or keep (default)", - " -P{F}, --pass-file={F} transmit the given input element(s) uncompressed", - " -U{a}, --unknown-attribute={a} unknown attribute action: error, strip, or pass (default)", - " -C{N}={L}, --class-attribute={N}={L} (user-defined attribute)", - " -F{N}={L}, --field-attribute={N}={L} (user-defined attribute)", - " -M{N}={L}, --method-attribute={N}={L} (user-defined attribute)", - " -D{N}={L}, --code-attribute={N}={L} (user-defined attribute)", - " -f{F}, --config-file={F} read file F for Pack200.Packer properties", - PARAMETER_V , - PARAMETER_Q , - PARAMETER_LF , - PARAMETER_H , - PARAMETER_VER , - PARAMETER_J, - "", - "Notes:", - " The -P, -C, -F, -M, and -D options accumulate.", - " Example attribute definition: -C SourceFile=RUH .", - " Config. file properties are defined by the Pack200 API.", - " For meaning of -S, -E, -H-, -m, -U values, see Pack200 API.", - " Layout definitions (like RUH) are defined by JSR 200.", - "", - "Repacking mode updates the JAR file with a pack/unpack cycle:", - " pack200 [-r|--repack] [-opt | --option=value]... [repackedy.jar] y.jar\n" - } - }, - {UNPACK_HELP,new String[]{ - "Usage: unpack200 [-opt... | --option=value]... x.pack[.gz] y.jar\n", - "", - "Unpacking Options", - " -H{h}, --deflate-hint={h} override transmitted deflate hint: true, false, or keep (default)", - " -r, --remove-pack-file remove input file after unpacking", - PARAMETER_V , - PARAMETER_Q , - PARAMETER_LF , - PARAMETER_H , - PARAMETER_VER , - PARAMETER_J, - } - }, + /* + * The following are the output of 'pack200' and 'unpack200' commands. + * Do not translate command arguments and words with a prefix of '-' or '--'. + */ + private static final Object[][] resource = { + {VERSION, "{0} version {1}"}, // parameter 0:class name;parameter 1: version value + {BAD_ARGUMENT, "Bad argument: {0}"}, + {BAD_OPTION, "Bad option: {0}={1}"}, // parameter 0:option name;parameter 1:option value + {BAD_REPACK_OUTPUT, "Bad --repack output: {0}"}, // parameter 0:filename + {DETECTED_ZIP_COMMENT, "Detected ZIP comment: {0}"}, // parameter 0:comment + {SKIP_FOR_REPACKED, "Skipping because already repacked: {0}"}, // parameter 0:filename + {WRITE_PACK_FILE, "To write a *.pack file, specify --no-gzip: {0}"}, // parameter 0:filename + {WRITE_PACKGZ_FILE, "To write a *.pack.gz file, specify --gzip: {0}"}, // parameter 0:filename + {SKIP_FOR_MOVE_FAILED, "Skipping unpack because move failed: {0}"}, // parameter 0:filename + {PACK_HELP, new String[] { + "Usage: pack200 [-opt... | --option=value]... x.pack[.gz] y.jar", + "", + "Packing Options", + " -g, --no-gzip output a plain *.pack file with no zipping", + " --gzip (default) post-process the pack output with gzip", + " -G, --strip-debug remove debugging attributes while packing", + " -O, --no-keep-file-order do not transmit file ordering information", + " --keep-file-order (default) preserve input file ordering", + " -S{N}, --segment-limit={N} output segment limit (default N=1Mb)", + " -E{N}, --effort={N} packing effort (default N=5)", + " -H{h}, --deflate-hint={h} transmit deflate hint: true, false, or keep (default)", + " -m{V}, --modification-time={V} transmit modtimes: latest or keep (default)", + " -P{F}, --pass-file={F} transmit the given input element(s) uncompressed", + " -U{a}, --unknown-attribute={a} unknown attribute action: error, strip, or pass (default)", + " -C{N}={L}, --class-attribute={N}={L} (user-defined attribute)", + " -F{N}={L}, --field-attribute={N}={L} (user-defined attribute)", + " -M{N}={L}, --method-attribute={N}={L} (user-defined attribute)", + " -D{N}={L}, --code-attribute={N}={L} (user-defined attribute)", + " -f{F}, --config-file={F} read file F for Pack200.Packer properties", + " -v, --verbose increase program verbosity", + " -q, --quiet set verbosity to lowest level", + " -l{F}, --log-file={F} output to the given log file, or '-' for System.out", + " -?, -h, --help print this message", + " -V, --version print program version", + " -J{X} pass option X to underlying Java VM", + "", + "Notes:", + " The -P, -C, -F, -M, and -D options accumulate.", + " Example attribute definition: -C SourceFile=RUH .", + " Config. file properties are defined by the Pack200 API.", + " For meaning of -S, -E, -H-, -m, -U values, see Pack200 API.", + " Layout definitions (like RUH) are defined by JSR 200.", + "", + "Repacking mode updates the JAR file with a pack/unpack cycle:", + " pack200 [-r|--repack] [-opt | --option=value]... [repackedy.jar] y.jar\n" + } + }, + {UNPACK_HELP, new String[] { + "Usage: unpack200 [-opt... | --option=value]... x.pack[.gz] y.jar\n", + "", + "Unpacking Options", + " -H{h}, --deflate-hint={h} override transmitted deflate hint: true, false, or keep (default)", + " -r, --remove-pack-file remove input file after unpacking", + " -v, --verbose increase program verbosity", + " -q, --quiet set verbosity to lowest level", + " -l{F}, --log-file={F} output to the given log file, or '-' for System.out", + " -?, -h, --help print this message", + " -V, --version print program version", + " -J{X} pass option X to underlying Java VM" + } + }, + {MORE_INFO, "(For more information, run {0} --help .)"}, // parameter 0:command name + {DUPLICATE_OPTION, "duplicate option: {0}"}, // parameter 0:option + {BAD_SPEC, "bad spec for {0}: {1}"}, // parameter 0:option;parameter 1:specifier + }; - {MORE_INFO,"(For more information, run {0} --help .)"},//parameter 0:command name - {DUPLICATE_OPTION,"duplicate option: {0}"},//parameter 0:option - {BAD_SPEC,"bad spec for {0}: {1}"},//parameter 0:option;parameter 1:specifier - }; - - protected Object[][] getContents() { - return resource; - } - - + protected Object[][] getContents() { + return resource; + } }
--- a/jdk/src/share/classes/com/sun/security/sasl/Provider.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/com/sun/security/sasl/Provider.java Tue Oct 08 14:57:32 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,7 +53,7 @@ " server mechanisms for: DIGEST-MD5, GSSAPI, CRAM-MD5, NTLM)"; public Provider() { - super("SunSASL", 1.7d, info); + super("SunSASL", 1.8d, info); AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() {
--- a/jdk/src/share/classes/java/io/File.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/io/File.java Tue Oct 08 14:57:32 2013 -0700 @@ -1908,10 +1908,18 @@ } else { n = Math.abs(n); } + + // Use only the file name from the supplied prefix + prefix = (new File(prefix)).getName(); + String name = prefix + Long.toString(n) + suffix; File f = new File(dir, name); - if (!name.equals(f.getName()) || f.isInvalid()) - throw new IOException("Unable to create temporary file"); + if (!name.equals(f.getName()) || f.isInvalid()) { + if (System.getSecurityManager() != null) + throw new IOException("Unable to create temporary file"); + else + throw new IOException("Unable to create temporary file, " + f); + } return f; } }
--- a/jdk/src/share/classes/java/lang/Class.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/Class.java Tue Oct 08 14:57:32 2013 -0700 @@ -3414,16 +3414,20 @@ transient ClassValue.ClassValueMap classValueMap; /** - * Returns an AnnotatedType object that represents the use of a type to specify - * the superclass of the entity represented by this Class. (The <em>use</em> of type - * Foo to specify the superclass in '... extends Foo' is distinct from the - * <em>declaration</em> of type Foo.) + * Returns an {@code AnnotatedType} object that represents the use of a + * type to specify the superclass of the entity represented by this {@code + * Class} object. (The <em>use</em> of type Foo to specify the superclass + * in '... extends Foo' is distinct from the <em>declaration</em> of type + * Foo.) * - * If this Class represents a class type whose declaration does not explicitly - * indicate an annotated superclass, the return value is null. + * <p> If this {@code Class} object represents a type whose declaration + * does not explicitly indicate an annotated superclass, then the return + * value is an {@code AnnotatedType} object representing an element with no + * annotations. * - * If this Class represents either the Object class, an interface type, an - * array type, a primitive type, or void, the return value is null. + * <p> If this {@code Class} represents either the {@code Object} class, an + * interface type, an array type, a primitive type, or void, the return + * value is {@code null}. * * @return an object representing the superclass * @since 1.8 @@ -3441,30 +3445,33 @@ } /** - * Returns an array of AnnotatedType objects that represent the use of types to - * specify superinterfaces of the entity represented by this Class. (The <em>use</em> - * of type Foo to specify a superinterface in '... implements Foo' is - * distinct from the <em>declaration</em> of type Foo.) + * Returns an array of {@code AnnotatedType} objects that represent the use + * of types to specify superinterfaces of the entity represented by this + * {@code Class} object. (The <em>use</em> of type Foo to specify a + * superinterface in '... implements Foo' is distinct from the + * <em>declaration</em> of type Foo.) * - * If this Class represents a class, the return value is an array - * containing objects representing the uses of interface types to specify - * interfaces implemented by the class. The order of the objects in the - * array corresponds to the order of the interface types used in the - * 'implements' clause of the declaration of this Class. + * <p> If this {@code Class} object represents a class, the return value is + * an array containing objects representing the uses of interface types to + * specify interfaces implemented by the class. The order of the objects in + * the array corresponds to the order of the interface types used in the + * 'implements' clause of the declaration of this {@code Class} object. * - * If this Class represents an interface, the return value is an array - * containing objects representing the uses of interface types to specify - * interfaces directly extended by the interface. The order of the objects in - * the array corresponds to the order of the interface types used in the - * 'extends' clause of the declaration of this Class. + * <p> If this {@code Class} object represents an interface, the return + * value is an array containing objects representing the uses of interface + * types to specify interfaces directly extended by the interface. The + * order of the objects in the array corresponds to the order of the + * interface types used in the 'extends' clause of the declaration of this + * {@code Class} object. * - * If this Class represents a class or interface whose declaration does not - * explicitly indicate any annotated superinterfaces, the return value is an + * <p> If this {@code Class} object represents a class or interface whose + * declaration does not explicitly indicate any annotated superinterfaces, + * the return value is an array of length 0. + * + * <p> If this {@code Class} object represents either the {@code Object} + * class, an array type, a primitive type, or void, the return value is an * array of length 0. * - * If this Class represents either the Object class, an array type, a - * primitive type, or void, the return value is an array of length 0. - * * @return an array representing the superinterfaces * @since 1.8 */
--- a/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Tue Oct 08 14:57:32 2013 -0700 @@ -125,10 +125,7 @@ this.implMethod = implMethod; this.implInfo = caller.revealDirect(implMethod); - // @@@ Temporary work-around pending resolution of 8005119 - this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial) - ? MethodHandleInfo.REF_invokeVirtual - : implInfo.getReferenceKind(); + this.implKind = implInfo.getReferenceKind(); this.implIsInstanceMethod = implKind == MethodHandleInfo.REF_invokeVirtual || implKind == MethodHandleInfo.REF_invokeSpecial ||
--- a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java Tue Oct 08 14:57:32 2013 -0700 @@ -360,6 +360,10 @@ return new Name(mh, mhName); } + NamedFunction getterFunction(int i) { + return new NamedFunction(getters[i]); + } + static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) { @@ -394,6 +398,7 @@ private boolean isPlaceholder() { return clazz == null; } private static final HashMap<String, SpeciesData> CACHE = new HashMap<>(); + static { CACHE.put("", EMPTY); } // make bootstrap predictable private static final boolean INIT_DONE; // set after <clinit> finishes... SpeciesData extendWithType(char type) { @@ -524,18 +529,18 @@ * A concrete BMH species adheres to the following schema: * * <pre> - * class Species_<<types>> extends BoundMethodHandle { - * <<fields>> - * final SpeciesData speciesData() { return SpeciesData.get("<<types>>"); } + * class Species_[[types]] extends BoundMethodHandle { + * [[fields]] + * final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); } * } * </pre> * - * The {@code <<types>>} signature is precisely the string that is passed to this + * The {@code [[types]]} signature is precisely the string that is passed to this * method. * - * The {@code <<fields>>} section consists of one field definition per character in + * The {@code [[fields]]} section consists of one field definition per character in * the type signature, adhering to the naming schema described in the definition of - * {@link #makeFieldName()}. + * {@link #makeFieldName}. * * For example, a concrete BMH species for two reference and one integral bound values * would have the following shape: @@ -817,7 +822,7 @@ * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through * {@link MethodHandle#linkToSpecial}. * - * The last {@link LambdaForm#Name Name} in the argument's form is expected to be the {@code void} + * The last {@link LambdaForm.Name Name} in the argument's form is expected to be the {@code void} * result of the {@code <init>} invocation. This entry is replaced. */ private static MethodHandle linkConstructor(MethodHandle cmh) {
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java Tue Oct 08 14:57:32 2013 -0700 @@ -60,7 +60,7 @@ * <p> * Here is a sample use of call sites and bootstrap methods which links every * dynamic call site to print its arguments: -<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java --> +<blockquote><pre>{@code static void test() throws Throwable { // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14); @@ -79,7 +79,7 @@ // ignore caller and name, but match the type: return new ConstantCallSite(printArgs.asType(type)); } -</pre></blockquote> +}</pre></blockquote> * @author John Rose, JSR 292 EG */ abstract @@ -199,12 +199,12 @@ * which has been linked to this call site. * <p> * This method is equivalent to the following code: - * <blockquote><pre> + * <blockquote><pre>{@code * MethodHandle getTarget, invoker, result; * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class)); * invoker = MethodHandles.exactInvoker(this.type()); * result = MethodHandles.foldArguments(invoker, getTarget) - * </pre></blockquote> + * }</pre></blockquote> * * @return a method handle which always invokes this call site's current target */ @@ -261,7 +261,7 @@ Object info, // Caller information: Class<?> callerClass) { - Object caller = IMPL_LOOKUP.in(callerClass); + MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass); CallSite site; try { Object binding; @@ -273,14 +273,44 @@ } else { Object[] argv = (Object[]) info; maybeReBoxElements(argv); - if (3 + argv.length > 255) - throw new BootstrapMethodError("too many bootstrap method arguments"); - MethodType bsmType = bootstrapMethod.type(); - if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class) - binding = bootstrapMethod.invoke(caller, name, type, argv); - else - binding = MethodHandles.spreadInvoker(bsmType, 3) - .invoke(bootstrapMethod, caller, name, type, argv); + switch (argv.length) { + case 0: + binding = bootstrapMethod.invoke(caller, name, type); + break; + case 1: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0]); + break; + case 2: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1]); + break; + case 3: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2]); + break; + case 4: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3]); + break; + case 5: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3], argv[4]); + break; + case 6: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); + break; + default: + final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) + if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY) + throw new BootstrapMethodError("too many bootstrap method arguments"); + MethodType bsmType = bootstrapMethod.type(); + MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); + MethodHandle typedBSM = bootstrapMethod.asType(invocationType); + MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); + binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv); + } } //System.out.println("BSM for "+name+type+" => "+binding); if (binding instanceof CallSite) {
--- a/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java Tue Oct 08 14:57:32 2013 -0700 @@ -257,12 +257,12 @@ assert(names.length == nameCursor); if (doesAlloc) { // names = { argx,y,z,... new C, init method } - names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]); - names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]); + names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]); + names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]); } else if (needsInit) { - names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]); + names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]); } else { - names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]); + names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]); } Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class); assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args! @@ -637,18 +637,18 @@ final int RESULT = nameCursor-1; // either the call or the cast Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); if (needsInit) - names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]); + names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]); if (needsCast && !isGetter) - names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]); + names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]); Object[] outArgs = new Object[1 + linkerType.parameterCount()]; assert(outArgs.length == (isGetter ? 3 : 4)); outArgs[0] = UNSAFE; if (isStatic) { - outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]); - outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]); + outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]); + outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]); } else { - outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]); - outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]); + outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]); + outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]); } if (!isGetter) { outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]); @@ -656,7 +656,7 @@ for (Object a : outArgs) assert(a != null); names[LINKER_CALL] = new Name(linker, outArgs); if (needsCast && isGetter) - names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); + names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); for (Name n : names) assert(n != null); String fieldOrStatic = (isStatic ? "Static" : "Field"); String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging @@ -665,48 +665,54 @@ return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT); } - private static final NamedFunction - NF_internalMemberName, - NF_internalMemberNameEnsureInit, - NF_ensureInitialized, - NF_fieldOffset, - NF_checkBase, - NF_staticBase, - NF_staticOffset, - NF_checkCast, - NF_allocateInstance, - NF_constructorMethod; - static { - try { - NamedFunction nfs[] = { - NF_internalMemberName = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("internalMemberName", Object.class)), - NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), - NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("ensureInitialized", Object.class)), - NF_fieldOffset = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("fieldOffset", Object.class)), - NF_checkBase = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("checkBase", Object.class)), - NF_staticBase = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("staticBase", Object.class)), - NF_staticOffset = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("staticOffset", Object.class)), - NF_checkCast = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("checkCast", Object.class, Object.class)), - NF_allocateInstance = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("allocateInstance", Object.class)), - NF_constructorMethod = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("constructorMethod", Object.class)) - }; - for (NamedFunction nf : nfs) { - // Each nf must be statically invocable or we get tied up in our bootstraps. - assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; - nf.resolve(); + /** + * Pre-initialized NamedFunctions for bootstrapping purposes. + * Factored in an inner class to delay initialization until first usage. + */ + private static class Lazy { + static final NamedFunction + NF_internalMemberName, + NF_internalMemberNameEnsureInit, + NF_ensureInitialized, + NF_fieldOffset, + NF_checkBase, + NF_staticBase, + NF_staticOffset, + NF_checkCast, + NF_allocateInstance, + NF_constructorMethod; + static { + try { + NamedFunction nfs[] = { + NF_internalMemberName = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberName", Object.class)), + NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), + NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("ensureInitialized", Object.class)), + NF_fieldOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("fieldOffset", Object.class)), + NF_checkBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkBase", Object.class)), + NF_staticBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticBase", Object.class)), + NF_staticOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticOffset", Object.class)), + NF_checkCast = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkCast", Object.class, Object.class)), + NF_allocateInstance = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("allocateInstance", Object.class)), + NF_constructorMethod = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("constructorMethod", Object.class)) + }; + for (NamedFunction nf : nfs) { + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; + nf.resolve(); + } + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); } - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); } } }
--- a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java Tue Oct 08 14:53:14 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.invoke; - -import sun.invoke.util.*; -import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; - -/** - * Adapters which manage inexact MethodHandle.invoke calls. - * The JVM calls one of these when the exact type match fails. - * @author jrose - */ -class InvokeGeneric { - // erased type for the call, which originates from an inexact invoke site - private final MethodType erasedCallerType; - // an invoker of type (MT, MH; A...) -> R - private final MethodHandle initialInvoker; - - /** Compute and cache information for this adapter, so that it can - * call out to targets of the erasure-family of the given erased type. - */ - /*non-public*/ InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException { - assert(erasedCallerType.equals(erasedCallerType.erase())); - this.erasedCallerType = erasedCallerType; - this.initialInvoker = makeInitialInvoker(); - assert initialInvoker.type().equals(erasedCallerType - .insertParameterTypes(0, MethodType.class, MethodHandle.class)) - : initialInvoker.type(); - } - - private static MethodHandles.Lookup lookup() { - return IMPL_LOOKUP; - } - - /** Return the adapter information for this type's erasure. */ - /*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { - InvokeGeneric gen = new InvokeGeneric(erasedCallerType); - return gen.initialInvoker; - } - - private MethodHandle makeInitialInvoker() throws ReflectiveOperationException { - // postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)} - MethodHandle postDispatch = makePostDispatchInvoker(); - MethodHandle invoker; - if (returnConversionPossible()) { - invoker = MethodHandles.foldArguments(postDispatch, - dispatcher("dispatchWithConversion")); - } else { - invoker = MethodHandles.foldArguments(postDispatch, dispatcher("dispatch")); - } - return invoker; - } - - private static final Class<?>[] EXTRA_ARGS = { MethodType.class, MethodHandle.class }; - private MethodHandle makePostDispatchInvoker() { - // Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...). - MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS); - return invokerType.invokers().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 ReflectiveOperationException { - return lookup().bind(this, dispatchName, - MethodType.methodType(MethodHandle.class, - MethodType.class, MethodHandle.class)); - } - - static final boolean USE_AS_TYPE_PATH = true; - - /** Return a method handle to invoke on the callerType, target, and remaining arguments. - * The method handle must finish the call. - * This is the first look at the caller type and target. - */ - private MethodHandle dispatch(MethodType callerType, MethodHandle target) { - MethodType targetType = target.type(); - if (USE_AS_TYPE_PATH || target.isVarargsCollector()) { - MethodHandle newTarget = target.asType(callerType); - targetType = callerType; - Invokers invokers = targetType.invokers(); - MethodHandle invoker = invokers.erasedInvokerWithDrops; - if (invoker == null) { - invokers.erasedInvokerWithDrops = invoker = - dropDispatchArguments(invokers.erasedInvoker()); - } - return invoker.bindTo(newTarget); - } - throw new RuntimeException("NYI"); - } - - private MethodHandle dispatchWithConversion(MethodType callerType, MethodHandle target) { - MethodHandle finisher = dispatch(callerType, target); - if (returnConversionNeeded(callerType, target)) - finisher = addReturnConversion(finisher, callerType.returnType()); //FIXME: slow - return finisher; - } - - private boolean returnConversionPossible() { - Class<?> needType = erasedCallerType.returnType(); - return !needType.isPrimitive(); - } - private boolean returnConversionNeeded(MethodType callerType, MethodHandle target) { - Class<?> needType = callerType.returnType(); - if (needType == erasedCallerType.returnType()) - return false; // no conversions possible, since must be primitive or Object - Class<?> haveType = target.type().returnType(); - if (VerifyType.isNullConversion(haveType, needType) && !needType.isInterface()) - return false; - return true; - } - private MethodHandle addReturnConversion(MethodHandle finisher, Class<?> type) { - // FIXME: This is slow because it creates a closure node on every call that requires a return cast. - MethodType finisherType = finisher.type(); - MethodHandle caster = ValueConversions.identity(type); - caster = caster.asType(caster.type().changeParameterType(0, finisherType.returnType())); - finisher = MethodHandles.filterReturnValue(finisher, caster); - return finisher.asType(finisherType); - } - - public String toString() { - return "InvokeGeneric"+erasedCallerType; - } -}
--- a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Tue Oct 08 14:57:32 2013 -0700 @@ -242,9 +242,6 @@ /** * Extract the MemberName of a newly-defined method. - * - * @param classFile - * @return */ private MemberName loadMethod(byte[] classFile) { Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile)); @@ -253,10 +250,6 @@ /** * Define a given class as anonymous class in the runtime system. - * - * @param classBytes - * @param patches - * @return */ private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) { Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches); @@ -264,14 +257,6 @@ return invokerClass; } - /** - * TODO - * - * @param invokerClass - * @param name - * @param type - * @return - */ private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) { MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); //System.out.println("resolveInvokerMember => "+member); @@ -499,10 +484,6 @@ /** * Generate customized bytecode for a given LambdaForm. - * - * @param form - * @param invokerType - * @return */ static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType); @@ -565,8 +546,6 @@ /** * Emit an invoke for the given name. - * - * @param name */ void emitInvoke(Name name) { if (true) { @@ -645,8 +624,6 @@ /** * Emit an invoke for the given name, using the MemberName directly. - * - * @param name */ void emitStaticInvoke(MemberName member, Name name) { assert(member.equals(name.function.member())); @@ -690,9 +667,6 @@ /** * Check if MemberName is a call to MethodHandleImpl.selectAlternative. - * - * @param member - * @return true if member is a call to MethodHandleImpl.selectAlternative */ private boolean isSelectAlternative(MemberName member) { return member != null && @@ -704,14 +678,12 @@ * Emit bytecode for the selectAlternative idiom. * * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): - * + * <blockquote><pre>{@code * Lambda(a0:L,a1:I)=>{ * t2:I=foo.test(a1:I); * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} - * - * @param selectAlternativeName - * @param invokeBasicName + * }</pre></blockquote> */ private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { MethodType type = selectAlternativeName.function.methodType(); @@ -750,11 +722,6 @@ mv.visitLabel(L_done); } - /** - * - * @param name - * @param paramIndex - */ private void emitPushArgument(Name name, int paramIndex) { Object arg = name.arguments[paramIndex]; char ptype = name.function.parameterType(paramIndex); @@ -923,9 +890,6 @@ /** * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments. - * - * @param sig - * @return */ static MemberName generateLambdaFormInterpreterEntryPoint(String sig) { assert(LambdaForm.isValidSignature(sig)); @@ -993,10 +957,6 @@ /** * Generate bytecode for a NamedFunction invoker. - * - * @param srcType - * @param dstType - * @return */ static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) { MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java Tue Oct 08 14:57:32 2013 -0700 @@ -44,6 +44,7 @@ // exact invoker for the outgoing call private /*lazy*/ MethodHandle exactInvoker; + private /*lazy*/ MethodHandle basicInvoker; // invokeBasic (unchecked exact) // erased (partially untyped but with primitives) invoker for the outgoing call // FIXME: get rid of @@ -74,21 +75,7 @@ /*non-public*/ MethodHandle exactInvoker() { MethodHandle invoker = exactInvoker; if (invoker != null) return invoker; - MethodType mtype = targetType; - MethodType invokerType = mtype.invokerType(); - LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY - MTYPE_ARG_APPENDED) { - lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_INVOKER); - invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); - } else { - // At maximum arity, we cannot afford an extra mtype argument, - // so build a fully customized (non-cached) invoker form. - lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_INVOKER); - invoker = SimpleMethodHandle.make(invokerType, lform); - } - invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype)); - assert(checkInvoker(invoker)); + invoker = makeExactOrGeneralInvoker(true); exactInvoker = invoker; return invoker; } @@ -96,43 +83,56 @@ /*non-public*/ MethodHandle generalInvoker() { MethodHandle invoker = generalInvoker; if (invoker != null) return invoker; - MethodType mtype = targetType; - MethodType invokerType = mtype.invokerType(); - LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - assert(GENERIC_INVOKER_SLOP >= MTYPE_ARG_APPENDED); - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY - GENERIC_INVOKER_SLOP) { - prepareForGenericCall(mtype); - lform = invokeForm(mtype, false, MethodTypeForm.LF_GEN_INVOKER); - invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); - } else { - // At maximum arity, we cannot afford an extra mtype argument, - // so build a fully customized (non-cached) invoker form. - lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_INVOKER); - invoker = SimpleMethodHandle.make(invokerType, lform); - } - invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype)); - assert(checkInvoker(invoker)); + invoker = makeExactOrGeneralInvoker(false); generalInvoker = invoker; return invoker; } - /*non-public*/ MethodHandle makeBasicInvoker() { - MethodHandle invoker = DirectMethodHandle.make(invokeBasicMethod(targetType)); - assert(targetType == targetType.basicType()); - // Note: This is not cached here. It is cached by the calling MethodTypeForm. + private MethodHandle makeExactOrGeneralInvoker(boolean isExact) { + MethodType mtype = targetType; + MethodType invokerType = mtype.invokerType(); + int which = (isExact ? MethodTypeForm.LF_EX_INVOKER : MethodTypeForm.LF_GEN_INVOKER); + LambdaForm lform = invokeHandleForm(mtype, false, which); + MethodHandle invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); + String whichName = (isExact ? "invokeExact" : "invoke"); + invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke(whichName, mtype)); assert(checkInvoker(invoker)); + maybeCompileToBytecode(invoker); return invoker; } - static MemberName invokeBasicMethod(MethodType type) { - type = type.basicType(); - String name = "invokeBasic"; + /** If the target type seems to be common enough, eagerly compile the invoker to bytecodes. */ + private void maybeCompileToBytecode(MethodHandle invoker) { + final int EAGER_COMPILE_ARITY_LIMIT = 10; + if (targetType == targetType.erase() && + targetType.parameterCount() < EAGER_COMPILE_ARITY_LIMIT) { + invoker.form.compileToBytecode(); + } + } + + /*non-public*/ MethodHandle basicInvoker() { + MethodHandle invoker = basicInvoker; + if (invoker != null) return invoker; + MethodType basicType = targetType.basicType(); + if (basicType != targetType) { + // double cache; not used significantly + return basicInvoker = basicType.invokers().basicInvoker(); + } + MemberName method = invokeBasicMethod(basicType); + invoker = DirectMethodHandle.make(method); + assert(checkInvoker(invoker)); + basicInvoker = invoker; + return invoker; + } + + // This next one is called from LambdaForm.NamedFunction.<init>. + /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { + assert(basicType == basicType.basicType()); try { //Lookup.findVirtual(MethodHandle.class, name, type); - return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, name, type); + return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType); } catch (ReflectiveOperationException ex) { - throw newInternalError("JVM cannot find invoker for "+type, ex); + throw newInternalError("JVM cannot find invoker for "+basicType, ex); } } @@ -184,6 +184,7 @@ vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader); } assert(vaInvoker.type().equals(spreadInvokerType.invokerType())); + maybeCompileToBytecode(vaInvoker); spreadInvokers[leadingArgCount] = vaInvoker; return vaInvoker; } @@ -231,32 +232,38 @@ return "Invokers"+targetType; } - static MemberName exactInvokerMethod(MethodType mtype, Object[] appendixResult) { + static MemberName methodHandleInvokeLinkerMethod(String name, + MethodType mtype, + Object[] appendixResult) { + int which; + switch (name) { + case "invokeExact": which = MethodTypeForm.LF_EX_LINKER; break; + case "invoke": which = MethodTypeForm.LF_GEN_LINKER; break; + default: throw new InternalError("not invoker: "+name); + } LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MTYPE_ARG_APPENDED) { - lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_LINKER); + if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MH_LINKER_ARG_APPENDED) { + lform = invokeHandleForm(mtype, false, which); appendixResult[0] = mtype; } else { - lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_LINKER); + lform = invokeHandleForm(mtype, true, which); } return lform.vmentry; } - static MemberName genericInvokerMethod(MethodType mtype, Object[] appendixResult) { - LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - (MTYPE_ARG_APPENDED + GENERIC_INVOKER_SLOP)) { - lform = invokeForm(mtype, false, MethodTypeForm.LF_GEN_LINKER); - appendixResult[0] = mtype; - prepareForGenericCall(mtype); - } else { - lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_LINKER); - } - return lform.vmentry; - } + // argument count to account for trailing "appendix value" (typically the mtype) + private static final int MH_LINKER_ARG_APPENDED = 1; - private static LambdaForm invokeForm(MethodType mtype, boolean customized, int which) { + /** Returns an adapter for invokeExact or generic invoke, as a MH or constant pool linker. + * If !customized, caller is responsible for supplying, during adapter execution, + * a copy of the exact mtype. This is because the adapter might be generalized to + * a basic type. + * @param mtype the caller's method type (either basic or full-custom) + * @param customized whether to use a trailing appendix argument (to carry the mtype) + * @param which bit-encoded 0x01 whether it is a CP adapter ("linker") or MHs.invoker value ("invoker"); + * 0x02 whether it is for invokeExact or generic invoke + */ + private static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) { boolean isCached; if (!customized) { mtype = mtype.basicType(); // normalize Z to I, String to Object, etc. @@ -301,41 +308,24 @@ : Arrays.asList(mtype, customized, which, nameCursor, names.length); if (MTYPE_ARG >= INARG_LIMIT) { assert(names[MTYPE_ARG] == null); - names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0); + NamedFunction getter = BoundMethodHandle.getSpeciesData("L").getterFunction(0); + names[MTYPE_ARG] = new Name(getter, names[THIS_MH]); // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM) } // Make the final call. If isGeneric, then prepend the result of type checking. - MethodType outCallType; - Object[] outArgs; + MethodType outCallType = mtype.basicType(); + Object[] outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); Object mtypeArg = (customized ? mtype : names[MTYPE_ARG]); if (!isGeneric) { names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], mtypeArg); // mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*) - outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); - outCallType = mtype; - } else if (customized) { - names[CHECK_TYPE] = new Name(NF_asType, names[CALL_MH], mtypeArg); - // mh.invokeGeneric(a*):R => - // let mt=TYPEOF(a*:R), tmh=asType(mh, mt); - // tmh.invokeBasic(a*) - outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); - outCallType = mtype; } else { names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], mtypeArg); - // mh.invokeGeneric(a*):R => - // let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt); - // gamh.invokeBasic(mt, mh, a*) - final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2; - assert(GENERIC_INVOKER_SLOP == PREPEND_COUNT); - outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class); - // prepend arguments: - System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); - outArgs[PREPEND_GAMH] = names[CHECK_TYPE]; - outArgs[PREPEND_MT] = mtypeArg; - outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class); + // mh.invokeGeneric(a*):R => checkGenericType(mh, TYPEOF(a*:R)).invokeBasic(a*) + outArgs[0] = names[CHECK_TYPE]; } - names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs); + names[LINKER_CALL] = new Name(outCallType, outArgs); lform = new LambdaForm(debugName, INARG_LIMIT, names); if (isLinker) lform.compileToBytecode(); // JVM needs a real methodOop @@ -343,7 +333,6 @@ lform = mtype.form().setCachedLambdaForm(which, lform); return lform; } - private static final int GENERIC_INVOKER_SLOP = 2; // used elsewhere to avoid arity problems /*non-public*/ static WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) { @@ -362,47 +351,53 @@ throw newWrongMethodTypeException(expected, actual); } - /** Static definition of MethodHandle.invokeGeneric checking code. */ + /** Static definition of MethodHandle.invokeGeneric checking code. + * Directly returns the type-adjusted MH to invoke, as follows: + * {@code (R)MH.invoke(a*) => MH.asType(TYPEOF(a*:R)).invokeBasic(a*)} + */ /*non-public*/ static @ForceInline Object checkGenericType(Object mhObj, Object expectedObj) { MethodHandle mh = (MethodHandle) mhObj; MethodType expected = (MethodType) expectedObj; - //MethodType actual = mh.type(); - MethodHandle gamh = expected.form().genericInvoker; - if (gamh != null) return gamh; - return prepareForGenericCall(expected); - } - - /** - * Returns an adapter GA for invoking a MH with type adjustments. - * The MethodType of the generic invocation site is prepended to MH - * and its arguments as follows: - * {@code (R)MH.invoke(A*) => GA.invokeBasic(TYPEOF<A*,R>, MH, A*)} - */ - /*non-public*/ static MethodHandle prepareForGenericCall(MethodType mtype) { - // force any needed adapters to be preconstructed - MethodTypeForm form = mtype.form(); - MethodHandle gamh = form.genericInvoker; - if (gamh != null) return gamh; - try { - // Trigger adapter creation. - gamh = InvokeGeneric.generalInvokerOf(form.erasedType); - form.genericInvoker = gamh; - return gamh; - } catch (Exception ex) { - throw newInternalError("Exception while resolving inexact invoke", ex); - } + if (mh.type() == expected) return mh; + MethodHandle atc = mh.asTypeCache; + if (atc != null && atc.type() == expected) return atc; + return mh.asType(expected); + /* Maybe add more paths here. Possible optimizations: + * for (R)MH.invoke(a*), + * let MT0 = TYPEOF(a*:R), MT1 = MH.type + * + * if MT0==MT1 or MT1 can be safely called by MT0 + * => MH.invokeBasic(a*) + * if MT1 can be safely called by MT0[R := Object] + * => MH.invokeBasic(a*) & checkcast(R) + * if MT1 can be safely called by MT0[* := Object] + * => checkcast(A)* & MH.invokeBasic(a*) & checkcast(R) + * if a big adapter BA can be pulled out of (MT0,MT1) + * => BA.invokeBasic(MT0,MH,a*) + * if a local adapter LA can cached on static CS0 = new GICS(MT0) + * => CS0.LA.invokeBasic(MH,a*) + * else + * => MH.asType(MT0).invokeBasic(A*) + */ } static MemberName linkToCallSiteMethod(MethodType mtype) { - LambdaForm lform = callSiteForm(mtype); + LambdaForm lform = callSiteForm(mtype, false); return lform.vmentry; } - private static LambdaForm callSiteForm(MethodType mtype) { + static MemberName linkToTargetMethod(MethodType mtype) { + LambdaForm lform = callSiteForm(mtype, true); + return lform.vmentry; + } + + // skipCallSite is true if we are optimizing a ConstantCallSite + private static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) { mtype = mtype.basicType(); // normalize Z to I, String to Object, etc. - LambdaForm lform = mtype.form().cachedLambdaForm(MethodTypeForm.LF_CS_LINKER); + final int which = (skipCallSite ? MethodTypeForm.LF_MH_LINKER : MethodTypeForm.LF_CS_LINKER); + LambdaForm lform = mtype.form().cachedLambdaForm(which); if (lform != null) return lform; // exactInvokerForm (Object,Object)Object // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial @@ -410,24 +405,26 @@ final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount(); final int INARG_LIMIT = OUTARG_LIMIT + 1; int nameCursor = OUTARG_LIMIT; - final int CSITE_ARG = nameCursor++; // the last in-argument - final int CALL_MH = nameCursor++; // result of getTarget + final int APPENDIX_ARG = nameCursor++; // the last in-argument + final int CSITE_ARG = skipCallSite ? -1 : APPENDIX_ARG; + final int CALL_MH = skipCallSite ? APPENDIX_ARG : nameCursor++; // result of getTarget final int LINKER_CALL = nameCursor++; - MethodType invokerFormType = mtype.appendParameterTypes(CallSite.class); + MethodType invokerFormType = mtype.appendParameterTypes(skipCallSite ? MethodHandle.class : CallSite.class); Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType); assert(names.length == nameCursor); - assert(names[CSITE_ARG] != null); - names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]); + assert(names[APPENDIX_ARG] != null); + if (!skipCallSite) + names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]); // (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*) final int PREPEND_MH = 0, PREPEND_COUNT = 1; Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class); // prepend MH argument: System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); outArgs[PREPEND_MH] = names[CALL_MH]; - names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs); - lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names); + names[LINKER_CALL] = new Name(mtype, outArgs); + lform = new LambdaForm((skipCallSite ? "linkToTargetMethod" : "linkToCallSite"), INARG_LIMIT, names); lform.compileToBytecode(); // JVM needs a real methodOop - lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform); + lform = mtype.form().setCachedLambdaForm(which, lform); return lform; }
--- a/jdk/src/share/classes/java/lang/invoke/LambdaForm.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java Tue Oct 08 14:57:32 2013 -0700 @@ -50,7 +50,7 @@ * The result of the lambda is defined as one of the names, often the last one. * <p> * Here is an approximate grammar: - * <pre> + * <blockquote><pre>{@code * LambdaForm = "(" ArgName* ")=>{" TempName* Result "}" * ArgName = "a" N ":" T * TempName = "t" N ":" T "=" Function "(" Argument* ");" @@ -60,7 +60,7 @@ * NameRef = "a" N | "t" N * N = (any whole number) * T = "L" | "I" | "J" | "F" | "D" | "V" - * </pre> + * }</pre></blockquote> * Names are numbered consecutively from left to right starting at zero. * (The letters are merely a taste of syntax sugar.) * Thus, the first temporary (if any) is always numbered N (where N=arity). @@ -69,7 +69,7 @@ * A lambda has a void result if and only if its result index is -1. * If a temporary has the type "V", it cannot be the subject of a NameRef, * even though possesses a number. - * Note that all reference types are erased to "L", which stands for {@code Object). + * Note that all reference types are erased to "L", which stands for {@code Object}. * All subword types (boolean, byte, short, char) are erased to "I" which is {@code int}. * The other types stand for the usual primitive types. * <p> @@ -89,7 +89,7 @@ * encoded by using temporary expressions which call type-transformed identity functions. * <p> * Examples: - * <pre> + * <blockquote><pre>{@code * (a0:J)=>{ a0 } * == identity(long) * (a0:I)=>{ t1:V = System.out#println(a0); void } @@ -113,14 +113,14 @@ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0); * t3:L = Class#cast(t2,a1); t3 } * == invoker for identity method handle which performs cast - * </pre> + * }</pre></blockquote> * <p> * @author John Rose, JSR 292 EG */ class LambdaForm { final int arity; final int result; - final Name[] names; + @Stable final Name[] names; final String debugName; MemberName vmentry; // low-level behavior, or null if not yet prepared private boolean isCompiled; @@ -457,7 +457,7 @@ isCompiled = true; return vmentry; } catch (Error | Exception ex) { - throw newInternalError(this.toString(), ex); + throw newInternalError("compileToBytecode", ex); } } @@ -683,8 +683,9 @@ */ static void traceInterpreter(String event, Object obj, Object... args) { - if (!TRACE_INTERPRETER) return; - System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : "")); + if (TRACE_INTERPRETER) { + System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : "")); + } } static void traceInterpreter(String event, Object obj) { traceInterpreter(event, obj, (Object[])null); @@ -971,8 +972,8 @@ static class NamedFunction { final MemberName member; - MethodHandle resolvedHandle; - MethodHandle invoker; + @Stable MethodHandle resolvedHandle; + @Stable MethodHandle invoker; NamedFunction(MethodHandle resolvedHandle) { this(resolvedHandle.internalMemberName(), resolvedHandle); @@ -982,6 +983,16 @@ //resolvedHandle = eraseSubwordTypes(resolvedHandle); this.resolvedHandle = resolvedHandle; } + NamedFunction(MethodType basicInvokerType) { + assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType; + if (basicInvokerType.parameterSlotCount() < MethodType.MAX_MH_INVOKER_ARITY) { + this.resolvedHandle = basicInvokerType.invokers().basicInvoker(); + this.member = resolvedHandle.internalMemberName(); + } else { + // necessary to pass BigArityTest + this.member = Invokers.invokeBasicMethod(basicInvokerType); + } + } // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc. // Any LambdaForm containing such a member is not interpretable. @@ -1229,7 +1240,7 @@ } public String toString() { - if (member == null) return resolvedHandle.toString(); + if (member == null) return String.valueOf(resolvedHandle); return member.getDeclaringClass().getSimpleName()+"."+member.getName(); } } @@ -1267,7 +1278,7 @@ final char type; private short index; final NamedFunction function; - final Object[] arguments; + @Stable final Object[] arguments; private Name(int index, char type, NamedFunction function, Object[] arguments) { this.index = (short)index; @@ -1279,6 +1290,10 @@ Name(MethodHandle function, Object... arguments) { this(new NamedFunction(function), arguments); } + Name(MethodType functionType, Object... arguments) { + this(new NamedFunction(functionType), arguments); + assert(arguments[0] instanceof Name && ((Name)arguments[0]).type == 'L'); + } Name(MemberName function, Object... arguments) { this(new NamedFunction(function), arguments); } @@ -1426,8 +1441,6 @@ * Does this Name precede the given binding node in some canonical order? * This predicate is used to order data bindings (via insertion sort) * with some stability. - * @param binding - * @return */ boolean isSiblingBindingBefore(Name binding) { assert(!binding.isParam()); @@ -1622,4 +1635,12 @@ */ static { NamedFunction.initializeInvokers(); } + + // The following hack is necessary in order to suppress TRACE_INTERPRETER + // during execution of the static initializes of this class. + // Turning on TRACE_INTERPRETER too early will cause + // stack overflows and other misbehavior during attempts to trace events + // that occur during LambdaForm.<clinit>. + // Therefore, do not move this line higher in this file, and do not remove. + private static final boolean TRACE_INTERPRETER = MethodHandleStatics.TRACE_INTERPRETER; }
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java Tue Oct 08 14:57:32 2013 -0700 @@ -70,13 +70,13 @@ * @author jrose */ /*non-public*/ final class MemberName implements Member, Cloneable { - private Class<?> clazz; // class in which the method is defined - private String name; // may be null if not yet materialized - private Object type; // may be null if not yet materialized - private int flags; // modifier bits; see reflect.Modifier + private Class<?> clazz; // class in which the method is defined + private String name; // may be null if not yet materialized + private Object type; // may be null if not yet materialized + private int flags; // modifier bits; see reflect.Modifier //@Injected JVM_Method* vmtarget; //@Injected int vmindex; - private Object resolution; // if null, this guy is resolved + private Object resolution; // if null, this guy is resolved /** Return the declaring class of this member. * In the case of a bare name and type, the declaring class will be null. @@ -98,7 +98,9 @@ public String getName() { if (name == null) { expandFromVM(); - if (name == null) return null; + if (name == null) { + return null; + } } return name; } @@ -119,28 +121,39 @@ public MethodType getMethodType() { if (type == null) { expandFromVM(); - if (type == null) return null; + if (type == null) { + return null; + } + } + if (!isInvocable()) { + throw newIllegalArgumentException("not invocable, no method type"); } - if (!isInvocable()) - throw newIllegalArgumentException("not invocable, no method type"); - if (type instanceof MethodType) { - return (MethodType) type; + + { + // Get a snapshot of type which doesn't get changed by racing threads. + final Object type = this.type; + if (type instanceof MethodType) { + return (MethodType) type; + } } - if (type instanceof String) { - String sig = (String) type; - MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader()); - this.type = res; - return res; + + // type is not a MethodType yet. Convert it thread-safely. + synchronized (this) { + if (type instanceof String) { + String sig = (String) type; + MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader()); + type = res; + } else if (type instanceof Object[]) { + Object[] typeInfo = (Object[]) type; + Class<?>[] ptypes = (Class<?>[]) typeInfo[1]; + Class<?> rtype = (Class<?>) typeInfo[0]; + MethodType res = MethodType.methodType(rtype, ptypes); + type = res; + } + // Make sure type is a MethodType for racing threads. + assert type instanceof MethodType : "bad method type " + type; } - if (type instanceof Object[]) { - Object[] typeInfo = (Object[]) type; - Class<?>[] ptypes = (Class<?>[]) typeInfo[1]; - Class<?> rtype = (Class<?>) typeInfo[0]; - MethodType res = MethodType.methodType(rtype, ptypes); - this.type = res; - return res; - } - throw new InternalError("bad method type "+type); + return (MethodType) type; } /** Return the actual type under which this method or constructor must be invoked. @@ -173,21 +186,34 @@ public Class<?> getFieldType() { if (type == null) { expandFromVM(); - if (type == null) return null; + if (type == null) { + return null; + } } - if (isInvocable()) + if (isInvocable()) { throw newIllegalArgumentException("not a field or nested class, no simple type"); - if (type instanceof Class<?>) { - return (Class<?>) type; } - if (type instanceof String) { - String sig = (String) type; - MethodType mtype = MethodType.fromMethodDescriptorString("()"+sig, getClassLoader()); - Class<?> res = mtype.returnType(); - this.type = res; - return res; + + { + // Get a snapshot of type which doesn't get changed by racing threads. + final Object type = this.type; + if (type instanceof Class<?>) { + return (Class<?>) type; + } } - throw new InternalError("bad field type "+type); + + // type is not a Class yet. Convert it thread-safely. + synchronized (this) { + if (type instanceof String) { + String sig = (String) type; + MethodType mtype = MethodType.fromMethodDescriptorString("()"+sig, getClassLoader()); + Class<?> res = mtype.returnType(); + type = res; + } + // Make sure type is a Class for racing threads. + assert type instanceof Class<?> : "bad field type " + type; + } + return (Class<?>) type; } /** Utility method to produce either the method type or field type of this member. */ @@ -201,10 +227,10 @@ public String getSignature() { if (type == null) { expandFromVM(); - if (type == null) return null; + if (type == null) { + return null; + } } - if (type instanceof String) - return (String) type; if (isInvocable()) return BytecodeDescriptor.unparse(getMethodType()); else @@ -463,10 +489,17 @@ //assert(referenceKindIsConsistent()); // do this after resolution } + /** + * Calls down to the VM to fill in the fields. This method is + * synchronized to avoid racing calls. + */ private void expandFromVM() { - if (!isResolved()) return; - if (type instanceof Object[]) - type = null; // don't saddle JVM w/ typeInfo + if (type != null) { + return; + } + if (!isResolved()) { + return; + } MethodHandleNatives.expand(this); } @@ -523,6 +556,9 @@ } throw new IllegalArgumentException(this.toString()); } + /** If this MN is not REF_newInvokeSpecial, return a clone with that ref. kind. + * In that case it must already be REF_invokeSpecial. + */ public MemberName asConstructor() { switch (getReferenceKind()) { case REF_invokeSpecial: return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial); @@ -530,6 +566,32 @@ } throw new IllegalArgumentException(this.toString()); } + /** If this MN is a REF_invokeSpecial, return a clone with the "normal" kind + * REF_invokeVirtual; also switch either to REF_invokeInterface if clazz.isInterface. + * The end result is to get a fully virtualized version of the MN. + * (Note that resolving in the JVM will sometimes devirtualize, changing + * REF_invokeVirtual of a final to REF_invokeSpecial, and REF_invokeInterface + * in some corner cases to either of the previous two; this transform + * undoes that change under the assumption that it occurred.) + */ + public MemberName asNormalOriginal() { + byte normalVirtual = clazz.isInterface() ? REF_invokeInterface : REF_invokeVirtual; + byte refKind = getReferenceKind(); + byte newRefKind = refKind; + MemberName result = this; + switch (refKind) { + case REF_invokeInterface: + case REF_invokeVirtual: + case REF_invokeSpecial: + newRefKind = normalVirtual; + break; + } + if (newRefKind == refKind) + return this; + result = clone().changeReferenceKind(newRefKind, refKind); + assert(this.referenceKindIsConsistentWith(result.getReferenceKind())); + return result; + } /** Create a name for the given reflected constructor. The resulting name will be in a resolved state. */ @SuppressWarnings("LeakingThisInConstructor") public MemberName(Constructor<?> ctor) { @@ -627,7 +689,7 @@ @Override public int hashCode() { - return Objects.hash(clazz, flags, name, getType()); + return Objects.hash(clazz, getReferenceKind(), name, getType()); } @Override public boolean equals(Object that) { @@ -643,13 +705,14 @@ if (this == that) return true; if (that == null) return false; return this.clazz == that.clazz - && this.flags == that.flags + && this.getReferenceKind() == that.getReferenceKind() && Objects.equals(this.name, that.name) && Objects.equals(this.getType(), that.getType()); } // Construction from symbolic parts, for queries: - /** Create a field or type name from the given components: Declaring class, name, type, reference kind. + /** Create a field or type name from the given components: + * Declaring class, name, type, reference kind. * The declaring class may be supplied as null if this is to be a bare name and type. * The resulting name will in an unresolved state. */ @@ -673,21 +736,34 @@ * The resulting name will in an unresolved state. */ public MemberName(Class<?> defClass, String name, MethodType type, byte refKind) { - @SuppressWarnings("LocalVariableHidesMemberVariable") - int flags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); - init(defClass, name, type, flagsMods(flags, 0, refKind)); + int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); + init(defClass, name, type, flagsMods(initFlags, 0, refKind)); initResolved(false); } -// /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers. -// * It will be a constructor if and only if the name is {@code "<init>"}. -// * The declaring class may be supplied as null if this is to be a bare name and type. -// * The modifier flags default to zero. -// * The resulting name will in an unresolved state. -// */ -// public MemberName(Class<?> defClass, String name, MethodType type, Void unused) { -// this(defClass, name, type, REF_NONE); -// } - + /** Create a method, constructor, or field name from the given components: + * Reference kind, declaring class, name, type. + */ + public MemberName(byte refKind, Class<?> defClass, String name, Object type) { + int kindFlags; + if (MethodHandleNatives.refKindIsField(refKind)) { + kindFlags = IS_FIELD; + if (!(type instanceof Class)) + throw newIllegalArgumentException("not a field type"); + } else if (MethodHandleNatives.refKindIsMethod(refKind)) { + kindFlags = IS_METHOD; + if (!(type instanceof MethodType)) + throw newIllegalArgumentException("not a method type"); + } else if (refKind == REF_newInvokeSpecial) { + kindFlags = IS_CONSTRUCTOR; + if (!(type instanceof MethodType) || + !CONSTRUCTOR_NAME.equals(name)) + throw newIllegalArgumentException("not a constructor type or name"); + } else { + throw newIllegalArgumentException("bad reference kind "+refKind); + } + init(defClass, name, type, flagsMods(kindFlags, 0, refKind)); + initResolved(false); + } /** Query whether this member name is resolved to a non-static, non-final method. */ public boolean hasReceiverTypeDispatch() {
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Tue Oct 08 14:57:32 2013 -0700 @@ -292,7 +292,7 @@ * generates a single invokevirtual instruction with * the symbolic type descriptor indicated in the following comment. * In these examples, the helper method {@code assertEquals} is assumed to - * be a method which calls {@link java.util.Objects#equals(Object,Object) Objects.equals } + * be a method which calls {@link java.util.Objects#equals(Object,Object) Objects.equals} * on its arguments, and asserts that the result is true. * * <h1>Exceptions</h1> @@ -392,7 +392,7 @@ * Java types. * <ul> * <li>Method types range over all possible arities, - * from no arguments to up to 255 of arguments (a limit imposed by the JVM). + * from no arguments to up to the <a href="MethodHandle.html#maxarity">maximum number</a> of allowed arguments. * Generics are not variadic, and so cannot represent this.</li> * <li>Method types can specify arguments of primitive types, * which Java generic types cannot range over.</li> @@ -402,6 +402,22 @@ * genericity with a Java type parameter.</li> * </ul> * + * <h1><a name="maxarity"></a>Arity limits</h1> + * The JVM imposes on all methods and constructors of any kind an absolute + * limit of 255 stacked arguments. This limit can appear more restrictive + * in certain cases: + * <ul> + * <li>A {@code long} or {@code double} argument counts (for purposes of arity limits) as two argument slots. + * <li>A non-static method consumes an extra argument for the object on which the method is called. + * <li>A constructor consumes an extra argument for the object which is being constructed. + * <li>Since a method handle’s {@code invoke} method (or other signature-polymorphic method) is non-virtual, + * it consumes an extra argument for the method handle itself, in addition to any non-virtual receiver object. + * </ul> + * These limits imply that certain method handles cannot be created, solely because of the JVM limit on stacked arguments. + * For example, if a static JVM method accepts exactly 255 arguments, a method handle cannot be created for it. + * Attempts to create method handles with impossible method types lead to an {@link IllegalArgumentException}. + * In particular, a method handle’s type must not have an arity of the exact maximum 255. + * * @see MethodType * @see MethodHandles * @author John Rose, JSR 292 EG @@ -420,6 +436,8 @@ private final MethodType type; /*private*/ final LambdaForm form; // form is not private so that invokers can easily fetch it + /*private*/ MethodHandle asTypeCache; + // asTypeCache is not private so that invokers can easily fetch it /** * Reports the type of this method handle. @@ -557,10 +575,10 @@ /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable; /** - * Performs a variable arity invocation, passing the arguments in the given array + * Performs a variable arity invocation, passing the arguments in the given list * to the method handle, as if via an inexact {@link #invoke invoke} from a call site * which mentions only the type {@code Object}, and whose arity is the length - * of the argument array. + * of the argument list. * <p> * Specifically, execution proceeds as if by the following steps, * although the methods are not guaranteed to be called if the JVM @@ -590,10 +608,10 @@ * or forced to null if the return type is void. * <p> * This call is equivalent to the following code: - * <p><blockquote><pre> + * <p><blockquote><pre>{@code * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0); * Object result = invoker.invokeExact(this, arguments); - * </pre></blockquote> + * }</pre></blockquote> * <p> * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke}, * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI. @@ -626,7 +644,7 @@ * <p> * This method is also equivalent to the following code: * <p><blockquote><pre> - * {@link #invokeWithArguments(Object...) invokeWithArguments}(arguments.toArray()) + * {@link #invokeWithArguments(Object...) invokeWithArguments}{@code(arguments.toArray())} * </pre></blockquote> * * @param arguments the arguments to pass to the target @@ -739,10 +757,24 @@ * @see MethodHandles#explicitCastArguments */ public MethodHandle asType(MethodType newType) { - if (!type.isConvertibleTo(newType)) { + // Fast path alternative to a heavyweight {@code asType} call. + // Return 'this' if the conversion will be a no-op. + if (newType == type) { + return this; + } + // Return 'this.asTypeCache' if the conversion is already memoized. + MethodHandle atc = asTypeCache; + if (atc != null && newType == atc.type) { + return atc; + } + return asTypeUncached(newType); + } + + /** Override this to change asType behavior. */ + /*non-public*/ MethodHandle asTypeUncached(MethodType newType) { + if (!type.isConvertibleTo(newType)) throw new WrongMethodTypeException("cannot convert "+this+" to "+newType); - } - return convertArguments(newType); + return asTypeCache = convertArguments(newType); } /** @@ -772,6 +804,10 @@ * to the target method handle. * (The array may also be null when zero elements are required.) * <p> + * If, when the adapter is called, the supplied array argument does + * not have the correct number of elements, the adapter will throw + * an {@link IllegalArgumentException} instead of invoking the target. + * <p> * Here are some simple examples of array-spreading method handles: * <blockquote><pre>{@code MethodHandle equals = publicLookup() @@ -782,6 +818,12 @@ MethodHandle eq2 = equals.asSpreader(Object[].class, 2); assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" })); assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" })); +// try to spread from anything but a 2-array: +for (int n = 0; n <= 10; n++) { + Object[] badArityArgs = (n == 2 ? null : new Object[n]); + try { assert((boolean) eq2.invokeExact(badArityArgs) && false); } + catch (IllegalArgumentException ex) { } // OK +} // spread both arguments from a String array: MethodHandle eq2s = equals.asSpreader(String[].class, 2); assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" })); @@ -815,10 +857,12 @@ * @return a new method handle which spreads its final array argument, * before calling the original method handle * @throws NullPointerException if {@code arrayType} is a null reference - * @throws IllegalArgumentException if {@code arrayType} is not an array type - * @throws IllegalArgumentException if target does not have at least + * @throws IllegalArgumentException if {@code arrayType} is not an array type, + * or if target does not have at least * {@code arrayLength} parameter types, - * or if {@code arrayLength} is negative + * or if {@code arrayLength} is negative, + * or if the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asCollector */ @@ -931,7 +975,9 @@ * @throws NullPointerException if {@code arrayType} is a null reference * @throws IllegalArgumentException if {@code arrayType} is not an array type * or {@code arrayType} is not assignable to this method handle's trailing parameter type, - * or {@code arrayLength} is not a legal array size + * or {@code arrayLength} is not a legal array size, + * or the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> * @throws WrongMethodTypeException if the implied {@code asType} call fails * @see #asSpreader * @see #asVarargsCollector @@ -1226,9 +1272,9 @@ * 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: - * <blockquote><pre> + * <blockquote><pre>{@code * "MethodHandle" + type().toString() - * </pre></blockquote> + * }</pre></blockquote> * <p> * (<em>Note:</em> Future releases of this API may add further information * to the string representation. @@ -1285,6 +1331,11 @@ } /*non-public*/ + Class<?> internalCallerClass() { + return null; // caller-bound MH for @CallerSensitive method returns caller + } + + /*non-public*/ MethodHandle withInternalMemberName(MemberName member) { if (member != null) { return MethodHandleImpl.makeWrappedMember(this, member); @@ -1434,7 +1485,6 @@ * Threads may continue running the old form indefinitely, * but it is likely that the new one will be preferred for new executions. * Use with discretion. - * @param newForm */ /*non-public*/ void updateForm(LambdaForm newForm) {
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Oct 08 14:57:32 2013 -0700 @@ -314,13 +314,13 @@ static class AsVarargsCollector extends MethodHandle { private final MethodHandle target; private final Class<?> arrayType; - private MethodHandle cache; + private /*@Stable*/ MethodHandle asCollectorCache; AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) { super(type, reinvokerForm(target)); this.target = target; this.arrayType = arrayType; - this.cache = target.asCollector(arrayType, 0); + this.asCollectorCache = target.asCollector(arrayType, 0); } @Override MethodHandle reinvokerTarget() { return target; } @@ -336,18 +336,19 @@ } @Override - public MethodHandle asType(MethodType newType) { + public MethodHandle asTypeUncached(MethodType newType) { 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 asFixedArity().asType(newType); + return asTypeCache = asFixedArity().asType(newType); } // check cache - if (cache.type().parameterCount() == newArity) - return cache.asType(newType); + MethodHandle acc = asCollectorCache; + if (acc != null && acc.type().parameterCount() == newArity) + return asTypeCache = acc.asType(newType); // build and cache a collector int arrayLength = newArity - collectArg; MethodHandle collector; @@ -357,8 +358,8 @@ } catch (IllegalArgumentException ex) { throw new WrongMethodTypeException("cannot build collector", ex); } - cache = collector; - return collector.asType(newType); + asCollectorCache = collector; + return asTypeCache = collector.asType(newType); } @Override @@ -380,6 +381,10 @@ MemberName internalMemberName() { return asFixedArity().internalMemberName(); } + @Override + Class<?> internalCallerClass() { + return asFixedArity().internalCallerClass(); + } /*non-public*/ @Override @@ -435,7 +440,7 @@ // Spread the array. MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType); Name array = names[argIndex]; - names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount); + names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount); for (int j = 0; j < spreadArgCount; i++, j++) { indexes[i] = nameCursor; names[nameCursor++] = new Name(aload, array, j); @@ -459,14 +464,8 @@ } static void checkSpreadArgument(Object av, int n) { - // FIXME: regression test for bug 7141637 erroneously expects an NPE, and other tests may expect IAE - // but the actual exception raised by an arity mismatch should be WMTE - final boolean RAISE_RANDOM_EXCEPTIONS = true; // FIXME: delete in JSR 292 M1 if (av == null) { if (n == 0) return; - int len; - if (RAISE_RANDOM_EXCEPTIONS) - len = ((Object[])av).length; // throw NPE; but delete this after tests are fixed } else if (av instanceof Object[]) { int len = ((Object[])av).length; if (len == n) return; @@ -475,19 +474,23 @@ if (len == n) return; } // fall through to error: - if (RAISE_RANDOM_EXCEPTIONS) - throw newIllegalArgumentException("Array is not of length "+n); - throw new WrongMethodTypeException("Array is not of length "+n); + throw newIllegalArgumentException("array is not of length "+n); } - private static final NamedFunction NF_checkSpreadArgument; - static { - try { - NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class - .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); - NF_checkSpreadArgument.resolve(); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); + /** + * Pre-initialized NamedFunctions for bootstrapping purposes. + * Factored in an inner class to delay initialization until first usage. + */ + private static class Lazy { + static final NamedFunction NF_checkSpreadArgument; + static { + try { + NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); + NF_checkSpreadArgument.resolve(); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } } } @@ -832,7 +835,7 @@ MethodHandle vamh = prepareForInvoker(mh); // Cache the result of makeInjectedInvoker once per argument class. MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass); - return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName()); + return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName(), hostClass); } private static MethodHandle makeInjectedInvoker(Class<?> hostClass) { @@ -887,10 +890,12 @@ } // Undo the adapter effect of prepareForInvoker: - private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, MemberName member) { + private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, + MemberName member, + Class<?> hostClass) { MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount()); mh = mh.asType(type); - mh = mh.withInternalMemberName(member); + mh = new WrappedMember(mh, type, member, hostClass); return mh; } @@ -959,11 +964,13 @@ static class WrappedMember extends MethodHandle { private final MethodHandle target; private final MemberName member; + private final Class<?> callerClass; - private WrappedMember(MethodHandle target, MethodType type, MemberName member) { + private WrappedMember(MethodHandle target, MethodType type, MemberName member, Class<?> callerClass) { super(type, reinvokerForm(target)); this.target = target; this.member = member; + this.callerClass = callerClass; } @Override @@ -971,23 +978,33 @@ return target; } @Override + public MethodHandle asTypeUncached(MethodType newType) { + // This MH is an alias for target, except for the MemberName + // Drop the MemberName if there is any conversion. + return asTypeCache = target.asType(newType); + } + @Override MemberName internalMemberName() { return member; } @Override + Class<?> internalCallerClass() { + return callerClass; + } + @Override boolean isInvokeSpecial() { return target.isInvokeSpecial(); } @Override MethodHandle viewAsType(MethodType newType) { - return new WrappedMember(target, newType, member); + return new WrappedMember(target, newType, member, callerClass); } } static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) { if (member.equals(target.internalMemberName())) return target; - return new WrappedMember(target, target.type(), member); + return new WrappedMember(target, target.type(), member, null); } }
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleInfo.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleInfo.java Tue Oct 08 14:57:32 2013 -0700 @@ -32,9 +32,10 @@ import static java.lang.invoke.MethodHandleStatics.*; /** - * A symbolic reference obtained by cracking a method handle into its consitutent symbolic parts. + * A symbolic reference obtained by cracking a direct method handle + * into its consitutent symbolic parts. * To crack a direct method handle, call {@link Lookup#revealDirect Lookup.revealDirect}. - * <p> + * <h1><a name="directmh"></a>Direct Method Handles</h1> * A <em>direct method handle</em> represents a method, constructor, or field without * any intervening argument bindings or other transformations. * The method, constructor, or field referred to by a direct method handle is called @@ -56,11 +57,25 @@ * or {@link Lookup#unreflectSetter Lookup.unreflectSetter} * to convert a {@link Field} into a method handle. * </ul> - * In all of these cases, it is possible to crack the resulting direct method handle + * + * <h1>Restrictions on Cracking</h1> + * Given a suitable {@code Lookup} object, it is possible to crack any direct method handle * to recover a symbolic reference for the underlying method, constructor, or field. * Cracking must be done via a {@code Lookup} object equivalent to that which created * the target method handle, or which has enough access permissions to recreate * an equivalent method handle. + * <p> + * If the underlying method is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a>, + * the direct method handle will have been "bound" to a particular caller class, the + * {@linkplain java.lang.invoke.MethodHandles.Lookup#lookupClass() lookup class} + * of the lookup object used to create it. + * Cracking this method handle with a different lookup class will fail + * even if the underlying method is public (like {@code Class.forName}). + * <p> + * The requirement of lookup object matching provides a "fast fail" behavior + * for programs which may otherwise trust erroneous revelation of a method + * handle with symbolic information (or caller binding) from an unexpected scope. + * Use {@link java.lang.invoke.MethodHandles#reflectAs} to override this limitation. * * <h1><a name="refkinds"></a>Reference kinds</h1> * The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a> @@ -190,7 +205,7 @@ * @return the Java language modifiers for underlying member, * or -1 if the member cannot be accessed * @see Modifier - * @see reflectAs + * @see #reflectAs */ public int getModifiers();
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Tue Oct 08 14:57:32 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -233,20 +233,19 @@ } static String refKindName(byte refKind) { assert(refKindIsValid(refKind)); - return REFERENCE_KIND_NAME[refKind]; + switch (refKind) { + case REF_getField: return "getField"; + case REF_getStatic: return "getStatic"; + case REF_putField: return "putField"; + case REF_putStatic: return "putStatic"; + case REF_invokeVirtual: return "invokeVirtual"; + case REF_invokeStatic: return "invokeStatic"; + case REF_invokeSpecial: return "invokeSpecial"; + case REF_newInvokeSpecial: return "newInvokeSpecial"; + case REF_invokeInterface: return "invokeInterface"; + default: return "REF_???"; + } } - private static String[] REFERENCE_KIND_NAME = { - null, - "getField", - "getStatic", - "putField", - "putStatic", - "invokeVirtual", - "invokeStatic", - "invokeSpecial", - "newInvokeSpecial", - "invokeInterface" - }; private static native int getNamedCon(int which, Object[] name); static boolean verifyConstants() { @@ -294,12 +293,18 @@ Class<?> caller = (Class<?>)callerObj; String name = nameObj.toString().intern(); MethodType type = (MethodType)typeObj; - appendixResult[0] = CallSite.makeSite(bootstrapMethod, + CallSite callSite = CallSite.makeSite(bootstrapMethod, name, type, staticArguments, caller); - return Invokers.linkToCallSiteMethod(type); + if (callSite instanceof ConstantCallSite) { + appendixResult[0] = callSite.dynamicInvoker(); + return Invokers.linkToTargetMethod(type); + } else { + appendixResult[0] = callSite; + return Invokers.linkToCallSiteMethod(type); + } } /** @@ -388,12 +393,7 @@ Object[] appendixResult) { try { if (defc == MethodHandle.class && refKind == REF_invokeVirtual) { - switch (name) { - case "invoke": - return Invokers.genericInvokerMethod(fixMethodType(callerClass, type), appendixResult); - case "invokeExact": - return Invokers.exactInvokerMethod(fixMethodType(callerClass, type), appendixResult); - } + return Invokers.methodHandleInvokeLinkerMethod(name, fixMethodType(callerClass, type), appendixResult); } } catch (Throwable ex) { if (ex instanceof LinkageError) @@ -440,14 +440,34 @@ Lookup lookup = IMPL_LOOKUP.in(callerClass); assert(refKindIsValid(refKind)); return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type); + } catch (IllegalAccessException ex) { + Error err = new IllegalAccessError(ex.getMessage()); + throw initCauseFrom(err, ex); + } catch (NoSuchMethodException ex) { + Error err = new NoSuchMethodError(ex.getMessage()); + throw initCauseFrom(err, ex); + } catch (NoSuchFieldException ex) { + Error err = new NoSuchFieldError(ex.getMessage()); + throw initCauseFrom(err, ex); } catch (ReflectiveOperationException ex) { Error err = new IncompatibleClassChangeError(); - err.initCause(ex); - throw err; + throw initCauseFrom(err, ex); } } /** + * Use best possible cause for err.initCause(), substituting the + * cause for err itself if the cause has the same (or better) type. + */ + static private Error initCauseFrom(Error err, Exception ex) { + Throwable th = ex.getCause(); + if (err.getClass().isInstance(th)) + return (Error) th; + err.initCause(th == null ? ex : th); + return err; + } + + /** * Is this method a caller-sensitive method? * I.e., does it call Reflection.getCallerClass or a similer method * to ask about the identity of its caller?
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java Tue Oct 08 14:57:32 2013 -0700 @@ -107,6 +107,11 @@ * 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. + * <p> + * If a security manager is installed, this method is caller sensitive. + * During any invocation of the target method handle via the returned wrapper, + * the original creator of the wrapper (the caller) will be visible + * to context checks requested by the security manager. * * @param <T> the desired type of the wrapper, a single-method interface * @param intfc a class object representing {@code T}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Tue Oct 08 14:57:32 2013 -0700 @@ -39,6 +39,7 @@ import sun.security.util.SecurityConstants; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; +import java.util.concurrent.ConcurrentHashMap; import sun.security.util.SecurityConstants; /** @@ -48,7 +49,6 @@ * <li>Lookup methods which help create method handles for methods and fields. * <li>Combinator methods, which combine or transform pre-existing method handles into new ones. * <li>Other factory methods to create method handles that emulate other common JVM operations or control flow patterns. - * <li>Wrapper methods which can convert between method handles and interface types. * </ul> * <p> * @author John Rose, JSR 292 EG @@ -65,12 +65,25 @@ //// Method handle creation from ordinary methods. /** - * Returns a {@link Lookup lookup object} on the caller, - * which has the capability to access any method handle that the caller has access to, - * including direct method handles to private fields and methods. + * Returns a {@link Lookup lookup object} with + * full capabilities to emulate all supported bytecode behaviors of the caller. + * These capabilities include <a href="MethodHandles.Lookup.html#privacc">private access</a> to the caller. + * Factory methods on the lookup object can create + * <a href="MethodHandleInfo.html#directmh">direct method handles</a> + * for any member that the caller has access to via bytecodes, + * including protected and private fields and methods. * This lookup object is a <em>capability</em> which may be delegated to trusted agents. * Do not store it in place where untrusted code can access it. - * @return a lookup object for the caller of this method + * <p> + * This method is caller sensitive, which means that it may return different + * values to different callers. + * <p> + * For any given caller class {@code C}, the lookup object returned by this call + * has equivalent capabilities to any lookup object + * supplied by the JVM to the bootstrap method of an + * <a href="package-summary.html#indyinsn">invokedynamic instruction</a> + * executing in the same caller class {@code C}. + * @return a lookup object for the caller of this method, with private access */ @CallerSensitive public static Lookup lookup() { @@ -84,11 +97,17 @@ * <p> * As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class} * of this lookup object will be {@link java.lang.Object}. - * <p> + * + * <p style="font-size:smaller;"> + * <em>Discussion:</em> * The lookup class can be changed to any other class {@code C} using an expression of the form - * {@linkplain Lookup#in <code>publicLookup().in(C.class)</code>}. + * {@link Lookup#in publicLookup().in(C.class)}. * Since all classes have equal access to public names, * such a change would confer no new access rights. + * A public lookup object is always subject to + * <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>. + * Also, it cannot access + * <a href="MethodHandles.Lookup.html#callsens">caller sensitive methods</a>. * @return a lookup object which is trusted minimally */ public static Lookup publicLookup() { @@ -96,7 +115,8 @@ } /** - * Performs an unchecked "crack" of a direct method handle. + * Performs an unchecked "crack" of a + * <a href="MethodHandleInfo.html#directmh">direct method handle</a>. * The result is as if the user had obtained a lookup object capable enough * to crack the target method handle, called * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect} @@ -149,10 +169,17 @@ * <h1><a name="lookups"></a>Lookup Factory Methods</h1> * The factory methods on a {@code Lookup} object correspond to all major * use cases for methods, constructors, and fields. + * Each method handle created by a factory method is the functional + * equivalent of a particular <em>bytecode behavior</em>. + * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.) * Here is a summary of the correspondence between these factory methods and * the behavior the resulting method handles: * <table border=1 cellpadding=5 summary="lookup method behaviors"> - * <tr><th>lookup expression</th><th>member</th><th>behavior</th></tr> + * <tr> + * <th><a name="equiv"></a>lookup expression</th> + * <th>member</th> + * <th>bytecode behavior</th> + * </tr> * <tr> * <td>{@link java.lang.invoke.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)}</td> * <td>{@code FT f;}</td><td>{@code (T) this.f;}</td> @@ -228,10 +255,12 @@ * In cases where the given member is of variable arity (i.e., a method or constructor) * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}. * In all other cases, the returned method handle will be of fixed arity. - * <p> + * <p style="font-size:smaller;"> + * <em>Discussion:</em> * The equivalence between looked-up method handles and underlying - * class members can break down in a few ways: - * <ul> + * class members and bytecode behaviors + * can break down in a few ways: + * <ul style="font-size:smaller;"> * <li>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. @@ -241,9 +270,13 @@ * For example, lookups for {@code MethodHandle.invokeExact} and * {@code MethodHandle.invoke} will always succeed, regardless of requested type. * <li>If there is a security manager installed, it can forbid the lookup - * on various grounds (<a href="#secmgr">see below</a>). - * By contrast, the {@code ldc} instruction is not subject to - * security manager checks. + * on various grounds (<a href="MethodHandles.Lookup.html#secmgr">see below</a>). + * By contrast, the {@code ldc} instruction on a {@code CONSTANT_MethodHandle} + * constant is not subject to security manager checks. + * <li>If the looked-up method has a + * <a href="MethodHandle.html#maxarity">very large arity</a>, + * the method handle creation may fail, due to the method handle + * type having too many parameters. * </ul> * * <h1><a name="access"></a>Access checking</h1> @@ -271,7 +304,8 @@ * 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. + * desired class member is not accessible to the lookup class, or + * because the lookup object is not trusted enough to access the member. * In any of these cases, a {@code ReflectiveOperationException} will be * thrown from the attempted lookup. The exact class will be one of * the following: @@ -282,11 +316,23 @@ * </ul> * <p> * In general, the conditions under which a method handle may be - * 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}. + * looked up for a method {@code M} are no more restrictive than the conditions + * under which the lookup class could have compiled, verified, and resolved a call to {@code M}. + * Where the JVM would raise exceptions like {@code NoSuchMethodError}, + * a method handle lookup will generally raise a corresponding + * checked exception, such as {@code NoSuchMethodException}. * 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}. + * is <a href="MethodHandles.Lookup.html#equiv">exactly equivalent</a> + * to executing the compiled, verified, and resolved call to {@code M}. * The same point is true of fields and constructors. + * <p style="font-size:smaller;"> + * <em>Discussion:</em> + * Access checks only apply to named and reflected methods, + * constructors, and fields. + * Other method handle creation methods, such as + * {@link MethodHandle#asType MethodHandle.asType}, + * do not require any access checks, and are used + * independently of any {@code Lookup} object. * <p> * If the desired member is {@code protected}, the usual JVM rules apply, * including the requirement that the lookup class must be either be in the @@ -300,6 +346,18 @@ * (which will necessarily be a superclass of the lookup class) * to the lookup class itself. * <p> + * The JVM imposes a similar requirement on {@code invokespecial} instruction, + * that the receiver argument must match both the resolved method <em>and</em> + * the current class. Again, this requirement is enforced by narrowing the + * type of the leading parameter to the resulting method handle. + * (See the Java Virtual Machine Specification, section 4.10.1.9.) + * <p> + * The JVM represents constructors and static initializer blocks as internal methods + * with special names ({@code "<init>"} and {@code "<clinit>"}). + * The internal syntax of invocation instructions allows them to refer to such internal + * methods as if they were normal methods, but the JVM bytecode verifier rejects them. + * A lookup of such an internal method will produce a {@code NoSuchMethodException}. + * <p> * 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. @@ -313,6 +371,43 @@ * which can transform a lookup on {@code C.E} into one on any of those other * classes, without special elevation of privilege. * <p> + * The accesses permitted to a given lookup object may be limited, + * according to its set of {@link #lookupModes lookupModes}, + * to a subset of members normally accessible to the lookup class. + * For example, the {@link MethodHandles#publicLookup publicLookup} + * method produces a lookup object which is only allowed to access + * public members in public classes. + * The caller sensitive method {@link MethodHandles#lookup lookup} + * produces a lookup object with full capabilities relative to + * its caller class, to emulate all supported bytecode behaviors. + * Also, the {@link Lookup#in Lookup.in} method may produce a lookup object + * with fewer access modes than the original lookup object. + * + * <p style="font-size:smaller;"> + * <a name="privacc"></a> + * <em>Discussion of private access:</em> + * We say that a lookup has <em>private access</em> + * if its {@linkplain #lookupModes lookup modes} + * include the possibility of accessing {@code private} members. + * As documented in the relevant methods elsewhere, + * only lookups with private access possess the following capabilities: + * <ul style="font-size:smaller;"> + * <li>access private fields, methods, and constructors of the lookup class + * <li>create method handles which invoke <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> methods, + * such as {@code Class.forName} + * <li>create method handles which {@link Lookup#findSpecial emulate invokespecial} instructions + * <li>avoid <a href="MethodHandles.Lookup.html#secmgr">package access checks</a> + * for classes accessible to the lookup class + * <li>create {@link Lookup#in delegated lookup objects} which have private access to other classes + * within the same package member + * </ul> + * <p style="font-size:smaller;"> + * Each of these permissions is a consequence of the fact that a lookup object + * with private access can be securely traced back to an originating class, + * whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions + * can be reliably determined and emulated by method handles. + * + * <h1><a name="secmgr"></a>Security manager interactions</h1> * 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 @@ -325,16 +420,6 @@ * and the Core Reflection API * (as found on {@link java.lang.Class Class}). * <p> - * Access checks only apply to named and reflected methods, - * constructors, and fields. - * Other method handle creation methods, such as - * {@link MethodHandle#asType MethodHandle.asType}, - * do not require any access checks, and are done - * with static methods of {@link MethodHandles}, - * independently of any {@code Lookup} object. - * - * <h1>Security manager interactions</h1> - * <a name="secmgr"></a> * If a security manager is present, member lookups are subject to * additional checks. * From one to three calls are made to the security manager. @@ -347,26 +432,79 @@ * member is actually defined. * The value {@code lookc} is defined as <em>not present</em> * if the current lookup object does not have - * {@linkplain java.lang.invoke.MethodHandles.Lookup#PRIVATE private access}. + * <a href="MethodHandles.Lookup.html#privacc">private access</a>. * The calls are made according to the following rules: * <ul> - * <li>If {@code lookc} is not present, or if its class loader is not + * <li><b>Step 1:</b> + * If {@code lookc} is not present, or if its class loader 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}. - * <li>If the retrieved member is not public and + * <li><b>Step 2:</b> + * If the retrieved member is not public and * {@code lookc} is not present, then * {@link SecurityManager#checkPermission smgr.checkPermission} * with {@code RuntimePermission("accessDeclaredMembers")} is called. - * <li>If the retrieved member is not public, + * <li><b>Step 3:</b> + * If the retrieved member is not public, + * and if {@code lookc} is not present, * and if {@code defc} and {@code refc} are different, * then {@link SecurityManager#checkPackageAccess * smgr.checkPackageAccess(defcPkg)} is called, * where {@code defcPkg} is the package of {@code defc}. * </ul> + * Security checks are performed after other access checks have passed. + * Therefore, the above rules presuppose a member that is public, + * or else that is being accessed from a lookup class that has + * rights to access the member. + * + * <h1><a name="callsens"></a>Caller sensitive methods</h1> + * A small number of Java methods have a special property called caller sensitivity. + * A <em>caller-sensitive</em> method can behave differently depending on the + * identity of its immediate caller. + * <p> + * If a method handle for a caller-sensitive method is requested, + * the general rules for <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> apply, + * but they take account of the lookup class in a special way. + * The resulting method handle behaves as if it were called + * from an instruction contained in the lookup class, + * so that the caller-sensitive method detects the lookup class. + * (By contrast, the invoker of the method handle is disregarded.) + * Thus, in the case of caller-sensitive methods, + * different lookup classes may give rise to + * differently behaving method handles. + * <p> + * In cases where the lookup object is + * {@link MethodHandles#publicLookup() publicLookup()}, + * or some other lookup object without + * <a href="MethodHandles.Lookup.html#privacc">private access</a>, + * the lookup class is disregarded. + * In such cases, no caller-sensitive method handle can be created, + * access is forbidden, and the lookup fails with an + * {@code IllegalAccessException}. + * <p style="font-size:smaller;"> + * <em>Discussion:</em> + * For example, the caller-sensitive method + * {@link java.lang.Class#forName(String) Class.forName(x)} + * can return varying classes or throw varying exceptions, + * depending on the class loader of the class that calls it. + * A public lookup of {@code Class.forName} will fail, because + * there is no reasonable way to determine its bytecode behavior. + * <p style="font-size:smaller;"> + * If an application caches method handles for broad sharing, + * it should use {@code publicLookup()} to create them. + * If there is a lookup of {@code Class.forName}, it will fail, + * and the application must take appropriate action in that case. + * It may be that a later lookup, perhaps during the invocation of a + * bootstrap method, can incorporate the specific identity + * of the caller, making the method accessible. + * <p style="font-size:smaller;"> + * The function {@code MethodHandles.lookup} is caller sensitive + * so that there can be a secure foundation for lookups. + * Nearly all other methods in the JSR 292 API rely on lookup + * objects to check access requests. */ - // FIXME in MR1: clarify that the bytecode behavior of a caller-ID method (like Class.forName) is relative to the lookupClass used to create the method handle, not the dynamic caller of the method handle public static final class Lookup { /** The class on behalf of whom the lookup is being performed. */ @@ -592,13 +730,23 @@ * (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 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 method and all its argument types must be accessible to the lookup object. * <p> * 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. + * <p> + * If the returned method handle is invoked, the method's class will + * be initialized, if it has not already been initialized. + * <p><b>Example:</b> + * <p><blockquote><pre>{@code +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; +... +MethodHandle MH_asList = publicLookup().findStatic(Arrays.class, + "asList", methodType(List.class, Object[].class)); +assertEquals("[x, y]", MH_asList.invoke("x", "y").toString()); + * }</pre></blockquote> * @param refc the class from which the method is accessed * @param name the name of the method * @param type the type of the method @@ -615,7 +763,6 @@ public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type); - checkSecurityManager(refc, method); return getDirectMethod(REF_invokeStatic, refc, method, findBoundCallerClass(method)); } @@ -623,7 +770,7 @@ * 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. + * The method and all its argument types must be accessible to the lookup object. * <p> * When called, the handle will treat the first argument as a receiver * and dispatch on the receiver's type to determine which method @@ -640,7 +787,7 @@ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if * the method's variable arity modifier bit ({@code 0x0080}) is set. * <p> - * Because of the general equivalence between {@code invokevirtual} + * Because of the general <a href="MethodHandles.Lookup.html#equiv">equivalence</a> 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 invoke}, the resulting @@ -649,6 +796,34 @@ * {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker} * with the same {@code type} argument. * + * <b>Example:</b> + * <p><blockquote><pre>{@code +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; +... +MethodHandle MH_concat = publicLookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class, + "hashCode", methodType(int.class)); +MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class, + "hashCode", methodType(int.class)); +assertEquals("xy", (String) MH_concat.invokeExact("x", "y")); +assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy")); +assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy")); +// interface method: +MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class, + "subSequence", methodType(CharSequence.class, int.class, int.class)); +assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString()); +// constructor "internal method" must be accessed differently: +MethodType MT_newString = methodType(void.class); //()V for new String() +try { assertEquals("impossible", lookup() + .findVirtual(String.class, "<init>", MT_newString)); + } catch (NoSuchMethodException ex) { } // OK +MethodHandle MH_newString = publicLookup() + .findConstructor(String.class, MT_newString); +assertEquals("", (String) MH_newString.invokeExact()); + * }</pre></blockquote> + * * @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 @@ -669,7 +844,6 @@ } byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual); MemberName method = resolveOrFail(refKind, refc, name, type); - checkSecurityManager(refc, method); return getDirectMethod(refKind, refc, method, findBoundCallerClass(method)); } private MethodHandle findVirtualForMH(String name, MethodType type) { @@ -687,16 +861,35 @@ * 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. - * The constructor and all its argument types must be accessible to the lookup class. - * If the constructor's class has not yet been initialized, that is done - * immediately, before the method handle is returned. + * The constructor and all its argument types must be accessible to the lookup object. * <p> - * Note: The requested type must have a return type of {@code void}. - * This is consistent with the JVM's treatment of constructor type descriptors. + * The requested type must have a return type of {@code void}. + * (This is consistent with the JVM's treatment of constructor type descriptors.) * <p> * 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. + * <p> + * If the returned method handle is invoked, the constructor's class will + * be initialized, if it has not already been initialized. + * <p><b>Example:</b> + * <p><blockquote><pre>{@code +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; +... +MethodHandle MH_newArrayList = publicLookup().findConstructor( + ArrayList.class, methodType(void.class, Collection.class)); +Collection orig = Arrays.asList("x", "y"); +Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig); +assert(orig != copy); +assertEquals(orig, copy); +// a variable-arity constructor: +MethodHandle MH_newProcessBuilder = publicLookup().findConstructor( + ProcessBuilder.class, methodType(void.class, String[].class)); +ProcessBuilder pb = (ProcessBuilder) + MH_newProcessBuilder.invoke("x", "y", "z"); +assertEquals("[x, y, z]", pb.command().toString()); + * }</pre></blockquote> * @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 @@ -711,31 +904,68 @@ public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException { String name = "<init>"; MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type); - checkSecurityManager(refc, ctor); return getDirectConstructor(refc, ctor); } /** - * Produces an early-bound method handle for a virtual method, - * as if called from an {@code invokespecial} - * instruction from {@code caller}. + * Produces an early-bound method handle for a virtual method. + * It will bypass checks for overriding methods on the receiver, + * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial} + * instruction from within the explicitly specified {@code specialCaller}. * The type of the method handle will be that of the method, - * with a suitably restricted receiver type (such as {@code caller}) prepended. + * with a suitably restricted receiver type prepended. + * (The receiver type will be {@code specialCaller} or a subtype.) * The method and all its argument types must be accessible - * to the caller. + * to the lookup object. * <p> - * When called, the handle will treat the first argument as a receiver, - * but will not dispatch on the receiver's type. - * (This direct invocation action is identical with that performed by an - * {@code invokespecial} instruction.) - * <p> - * If the explicitly specified caller class is not identical with the - * lookup class, or if this lookup object does not have private access + * Before method resolution, + * if the explicitly specified caller class is not identical with the + * lookup class, or if this lookup object does not have + * <a href="MethodHandles.Lookup.html#privacc">private access</a> * privileges, the access fails. * <p> * 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. + * <p style="font-size:smaller;"> + * <em>(Note: JVM internal methods named {@code "<init>"} are not visible to this API, + * even though the {@code invokespecial} instruction can refer to them + * in special circumstances. Use {@link #findConstructor findConstructor} + * to access instance initialization methods in a safe manner.)</em> + * <p><b>Example:</b> + * <p><blockquote><pre>{@code +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; +... +static class Listie extends ArrayList { + public String toString() { return "[wee Listie]"; } + static Lookup lookup() { return MethodHandles.lookup(); } +} +... +// no access to constructor via invokeSpecial: +MethodHandle MH_newListie = Listie.lookup() + .findConstructor(Listie.class, methodType(void.class)); +Listie l = (Listie) MH_newListie.invokeExact(); +try { assertEquals("impossible", Listie.lookup().findSpecial( + Listie.class, "<init>", methodType(void.class), Listie.class)); + } catch (NoSuchMethodException ex) { } // OK +// access to super and self methods via invokeSpecial: +MethodHandle MH_super = Listie.lookup().findSpecial( + ArrayList.class, "toString" , methodType(String.class), Listie.class); +MethodHandle MH_this = Listie.lookup().findSpecial( + Listie.class, "toString" , methodType(String.class), Listie.class); +MethodHandle MH_duper = Listie.lookup().findSpecial( + Object.class, "toString" , methodType(String.class), Listie.class); +assertEquals("[]", (String) MH_super.invokeExact(l)); +assertEquals(""+l, (String) MH_this.invokeExact(l)); +assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method +try { assertEquals("inaccessible", Listie.lookup().findSpecial( + String.class, "toString", methodType(String.class), Listie.class)); + } catch (IllegalAccessException ex) { } // OK +Listie subl = new Listie() { public String toString() { return "[subclass]"; } }; +assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method + * }</pre></blockquote> + * * @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 @@ -754,7 +984,6 @@ checkSpecialCaller(specialCaller); Lookup specialLookup = this.in(specialCaller); MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type); - checkSecurityManager(refc, method); return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method)); } @@ -777,7 +1006,6 @@ */ public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { MemberName field = resolveOrFail(REF_getField, refc, name, type); - checkSecurityManager(refc, field); return getDirectField(REF_getField, refc, field); } @@ -800,7 +1028,6 @@ */ public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { MemberName field = resolveOrFail(REF_putField, refc, name, type); - checkSecurityManager(refc, field); return getDirectField(REF_putField, refc, field); } @@ -810,6 +1037,9 @@ * value type. * The method handle will take no arguments. * Access checking is performed immediately on behalf of the lookup class. + * <p> + * If the returned method handle is invoked, the field's class will + * be initialized, if it has not already been initialized. * @param refc the class or interface from which the method is accessed * @param name the field's name * @param type the field's type @@ -822,7 +1052,6 @@ */ public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { MemberName field = resolveOrFail(REF_getStatic, refc, name, type); - checkSecurityManager(refc, field); return getDirectField(REF_getStatic, refc, field); } @@ -832,6 +1061,9 @@ * 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. + * <p> + * If the returned method handle is invoked, the field's class will + * be initialized, if it has not already been initialized. * @param refc the class or interface from which the method is accessed * @param name the field's name * @param type the field's type @@ -844,7 +1076,6 @@ */ public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { MemberName field = resolveOrFail(REF_putStatic, refc, name, type); - checkSecurityManager(refc, field); return getDirectField(REF_putStatic, refc, field); } @@ -852,7 +1083,7 @@ * 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. + * The method and all its argument types must be accessible to the lookup object. * The type of the method handle will be that of the method, * without any insertion of an additional receiver parameter. * The given receiver will be bound into the method handle, @@ -867,17 +1098,17 @@ * the given receiver value will be bound to it.) * <p> * This is equivalent to the following code: - * <blockquote><pre> + * <blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... -MethodHandle mh0 = lookup().{@link #findVirtual findVirtual}(defc, name, type); -MethodHandle mh1 = mh0.{@link MethodHandle#bindTo bindTo}(receiver); +MethodHandle mh0 = lookup().findVirtual(defc, name, type); +MethodHandle mh1 = mh0.bindTo(receiver); MethodType mt1 = mh1.type(); if (mh0.isVarargsCollector()) mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1)); return mh1; - * </pre></blockquote> + * }</pre></blockquote> * 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. @@ -893,17 +1124,19 @@ * @exception SecurityException if a security manager is present and it * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> * @throws NullPointerException if any argument is null + * @see MethodHandle#bindTo + * @see #findVirtual */ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { Class<? extends Object> refc = receiver.getClass(); // may get NPE MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type); - checkSecurityManager(refc, method); MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, findBoundCallerClass(method)); return mh.bindReceiver(receiver).setVarargs(method); } /** - * Makes a direct method handle to <i>m</i>, if the lookup class has permission. + * Makes a <a href="MethodHandleInfo.html#directmh">direct method handle</a> + * to <i>m</i>, if the lookup class has permission. * If <i>m</i> is non-static, the receiver argument is treated as an initial argument. * If <i>m</i> is virtual, overriding is respected on every call. * Unlike the Core Reflection API, exceptions are <em>not</em> wrapped. @@ -916,6 +1149,10 @@ * 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. + * <p> + * If <i>m</i> is static, and + * if the returned method handle is invoked, the method's class will + * be initialized, if it has not already been initialized. * @param m the reflected method * @return a method handle which can invoke the reflected method * @throws IllegalAccessException if access checking fails @@ -934,7 +1171,7 @@ refKind = REF_invokeVirtual; assert(method.isMethod()); Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this; - return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method)); + return lookup.getDirectMethodNoSecurityManager(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method)); } private MethodHandle unreflectForMH(Method m) { // these names require special lookups because they throw UnsupportedOperationException @@ -946,13 +1183,21 @@ /** * 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}. + * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial} + * instruction from within the explicitly specified {@code specialCaller}. * The type of the method handle will be that of the method, - * with the special caller type prepended (and <em>not</em> the receiver of the method). + * with a suitably restricted receiver type prepended. + * (The receiver type will be {@code specialCaller} or a subtype.) * 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. * <p> + * Before method resolution, + * if the explicitly specified caller class is not identical with the + * lookup class, or if this lookup object does not have + * <a href="MethodHandles.Lookup.html#privacc">private access</a> + * privileges, the access fails. + * <p> * 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. @@ -970,7 +1215,7 @@ MemberName method = new MemberName(m, true); assert(method.isMethod()); // ignore m.isAccessible: this is a new kind of access - return specialLookup.getDirectMethod(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerClass(method)); + return specialLookup.getDirectMethodNoSecurityManager(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerClass(method)); } /** @@ -987,6 +1232,9 @@ * 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. + * <p> + * If the returned method handle is invoked, the constructor's class will + * be initialized, if it has not already been initialized. * @param c the reflected constructor * @return a method handle which can invoke the reflected constructor * @throws IllegalAccessException if access checking fails @@ -994,12 +1242,11 @@ * is set and {@code asVarargsCollector} fails * @throws NullPointerException if the argument is null */ - @SuppressWarnings("rawtypes") // Will be Constructor<?> after JSR 292 MR - public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException { + public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException { MemberName ctor = new MemberName(c); assert(ctor.isConstructor()); Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this; - return lookup.getDirectConstructor(ctor.getDeclaringClass(), ctor); + return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor); } /** @@ -1011,6 +1258,10 @@ * the field. * If the field's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class. + * <p> + * If the field is static, and + * if the returned method handle is invoked, the field's class will + * be initialized, if it has not already been initialized. * @param f the reflected field * @return a method handle which can load values from the reflected field * @throws IllegalAccessException if access checking fails @@ -1025,7 +1276,7 @@ ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind()) : MethodHandleNatives.refKindIsGetter(field.getReferenceKind())); Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this; - return lookup.getDirectField(field.getReferenceKind(), f.getDeclaringClass(), field); + return lookup.getDirectFieldNoSecurityManager(field.getReferenceKind(), f.getDeclaringClass(), field); } /** @@ -1037,6 +1288,10 @@ * the field, and the value to be stored. * If the field's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class. + * <p> + * If the field is static, and + * if the returned method handle is invoked, the field's class will + * be initialized, if it has not already been initialized. * @param f the reflected field * @return a method handle which can store values into the reflected field * @throws IllegalAccessException if access checking fails @@ -1047,17 +1302,21 @@ } /** - * Cracks a direct method handle created by this lookup object or a similar one. + * Cracks a <a href="MethodHandleInfo.html#directmh">direct method handle</a> + * created by this lookup object or a similar one. * Security and access checks are performed to ensure that this lookup object * is capable of reproducing the target method handle. * This means that the cracking may fail if target is a direct method handle * but was created by an unrelated lookup object. + * This can happen if the method handle is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> + * and was created by a lookup object for a different class. * @param target a direct method handle to crack into symbolic reference components * @return a symbolic reference which can be used to reconstruct this method handle from this lookup object * @exception SecurityException if a security manager is present and it * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> * @throws IllegalArgumentException if the target is not a direct method handle or if access checking fails * @exception NullPointerException if the target is {@code null} + * @see MethodHandleInfo * @since 1.8 */ public MethodHandleInfo revealDirect(MethodHandle target) { @@ -1077,11 +1336,16 @@ refKind = REF_invokeInterface; // Check SM permissions and member access before cracking. try { + checkAccess(refKind, defc, member); checkSecurityManager(defc, member); - checkAccess(refKind, defc, member); } catch (IllegalAccessException ex) { throw new IllegalArgumentException(ex); } + if (allowedModes != TRUSTED && member.isCallerSensitive()) { + Class<?> callerClass = target.internalCallerClass(); + if (!hasPrivateAccess() || callerClass != lookupClass()) + throw new IllegalArgumentException("method handle is caller sensitive: "+callerClass); + } // Produce the handle to the results. return new InfoFromMemberName(this, member, refKind); } @@ -1090,24 +1354,43 @@ MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve - name.getClass(); type.getClass(); // NPE + name.getClass(); // NPE + type.getClass(); // NPE return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(), NoSuchFieldException.class); } MemberName resolveOrFail(byte refKind, Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve - name.getClass(); type.getClass(); // NPE + name.getClass(); // NPE + type.getClass(); // NPE + checkMethodName(refKind, name); // NPE check on name return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(), NoSuchMethodException.class); } + MemberName resolveOrFail(byte refKind, MemberName member) throws ReflectiveOperationException { + checkSymbolicClass(member.getDeclaringClass()); // do this before attempting to resolve + member.getName().getClass(); // NPE + member.getType().getClass(); // NPE + return IMPL_NAMES.resolveOrFail(refKind, member, lookupClassOrNull(), + ReflectiveOperationException.class); + } + void checkSymbolicClass(Class<?> refc) throws IllegalAccessException { + refc.getClass(); // NPE Class<?> caller = lookupClassOrNull(); if (caller != null && !VerifyAccess.isClassAccessible(refc, caller, allowedModes)) throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this); } + /** Check name for an illegal leading "<" character. */ + void checkMethodName(byte refKind, String name) throws NoSuchMethodException { + if (name.startsWith("<") && refKind != REF_newInvokeSpecial) + throw new NoSuchMethodException("illegal method name: "+name); + } + + /** * Find my trustable caller class if m is a caller sensitive method. * If this lookup object has private access, then the caller class is the lookupClass. @@ -1116,8 +1399,8 @@ Class<?> findBoundCallerClass(MemberName m) throws IllegalAccessException { Class<?> callerClass = null; if (MethodHandleNatives.isCallerSensitive(m)) { - // Only full-power lookup is allowed to resolve caller-sensitive methods - if (isFullPowerLookup()) { + // Only lookups with private access are allowed to resolve caller-sensitive methods + if (hasPrivateAccess()) { callerClass = lookupClass; } else { throw new IllegalAccessException("Attempt to lookup caller-sensitive method using restricted lookup object"); @@ -1126,7 +1409,7 @@ return callerClass; } - private boolean isFullPowerLookup() { + private boolean hasPrivateAccess() { return (allowedModes & PRIVATE) != 0; } @@ -1141,22 +1424,21 @@ if (allowedModes == TRUSTED) return; // Step 1: - if (!isFullPowerLookup() || + boolean fullPowerLookup = hasPrivateAccess(); + if (!fullPowerLookup || !VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) { ReflectUtil.checkPackageAccess(refc); } // Step 2: if (m.isPublic()) return; - Class<?> defc = m.getDeclaringClass(); - { - if (!isFullPowerLookup()) { - smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); - } + if (!fullPowerLookup) { + smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); } // Step 3: - if (defc != refc) { + Class<?> defc = m.getDeclaringClass(); + if (!fullPowerLookup && defc != refc) { ReflectUtil.checkPackageAccess(defc); } } @@ -1185,6 +1467,7 @@ throw m.makeAccessException(message, this); } + /** Check public/protected/private bits on the symbolic reference class and its member. */ void checkAccess(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException { assert(m.referenceKindIsConsistentWith(refKind) && MethodHandleNatives.refKindIsValid(refKind) && @@ -1192,6 +1475,26 @@ int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; int mods = m.getModifiers(); + if (Modifier.isProtected(mods) && + refKind == REF_invokeVirtual && + m.getDeclaringClass() == Object.class && + m.getName().equals("clone") && + refc.isArray()) { + // The JVM does this hack also. + // (See ClassVerifier::verify_invoke_instructions + // and LinkResolver::check_method_accessability.) + // Because the JVM does not allow separate methods on array types, + // there is no separate method for int[].clone. + // All arrays simply inherit Object.clone. + // But for access checking logic, we make Object.clone + // (normally protected) appear to be public. + // Later on, when the DirectMethodHandle is created, + // its leading argument will be restricted to the + // requested array type. + // N.B. The return type is not adjusted, because + // that is *not* the bytecode behavior. + mods ^= Modifier.PROTECTED | Modifier.PUBLIC; + } if (Modifier.isFinal(mods) && MethodHandleNatives.refKindIsSetter(refKind)) throw m.makeAccessException("unexpected set of a final field", this); @@ -1239,7 +1542,7 @@ private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException { int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; - if ((allowedModes & PRIVATE) == 0 + if (!hasPrivateAccess() || (specialCaller != lookupClass() && !(ALLOW_NESTMATE_ACCESS && VerifyAccess.isSamePackageMember(specialCaller, lookupClass())))) @@ -1271,18 +1574,32 @@ return mh.viewAsType(narrowType); } + /** Check access and get the requested method. */ private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException { - return getDirectMethodCommon(refKind, refc, method, - (refKind == REF_invokeSpecial || - (MethodHandleNatives.refKindHasReceiver(refKind) && - restrictProtectedReceiver(method))), callerClass); + final boolean doRestrict = true; + final boolean checkSecurity = true; + return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass); + } + /** Check access and get the requested method, eliding receiver narrowing rules. */ + private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException { + final boolean doRestrict = false; + final boolean checkSecurity = true; + return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass); } - private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException { - return getDirectMethodCommon(refKind, refc, method, false, callerClass); + /** Check access and get the requested method, eliding security manager checks. */ + private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException { + final boolean doRestrict = true; + final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants + return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass); } + /** Common code for all methods; do not call directly except from immediately above. */ private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method, + boolean checkSecurity, boolean doRestrict, Class<?> callerClass) throws IllegalAccessException { checkMethod(refKind, refc, method); + // Optionally check with the security manager; this isn't needed for unreflect* calls. + if (checkSecurity) + checkSecurityManager(refc, method); assert(!method.isMethodHandleInvoke()); Class<?> refcAsSuper; @@ -1312,7 +1629,11 @@ MethodHandle mh = DirectMethodHandle.make(refKind, refc, method); mh = maybeBindCaller(method, mh, callerClass); mh = mh.setVarargs(method); - if (doRestrict) + // Optionally narrow the receiver argument to refc using restrictReceiver. + if (doRestrict && + (refKind == REF_invokeSpecial || + (MethodHandleNatives.refKindHasReceiver(refKind) && + restrictProtectedReceiver(method)))) mh = restrictReceiver(method, mh, lookupClass()); return mh; } @@ -1322,14 +1643,29 @@ if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method)) return mh; Class<?> hostClass = lookupClass; - if ((allowedModes & PRIVATE) == 0) // caller must use full-power lookup + if (!hasPrivateAccess()) // caller must have private access hostClass = callerClass; // callerClass came from a security manager style stack walk MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass); // Note: caller will apply varargs after this step happens. return cbmh; } + /** Check access and get the requested field. */ private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException { + final boolean checkSecurity = true; + return getDirectFieldCommon(refKind, refc, field, checkSecurity); + } + /** Check access and get the requested field, eliding security manager checks. */ + private MethodHandle getDirectFieldNoSecurityManager(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException { + final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants + return getDirectFieldCommon(refKind, refc, field, checkSecurity); + } + /** Common code for all fields; do not call directly except from immediately above. */ + private MethodHandle getDirectFieldCommon(byte refKind, Class<?> refc, MemberName field, + boolean checkSecurity) throws IllegalAccessException { checkField(refKind, refc, field); + // Optionally check with the security manager; this isn't needed for unreflect* calls. + if (checkSecurity) + checkSecurityManager(refc, field); MethodHandle mh = DirectMethodHandle.make(refc, field); boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(field)); @@ -1337,9 +1673,24 @@ mh = restrictReceiver(field, mh, lookupClass()); return mh; } + /** Check access and get the requested constructor. */ private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException { + final boolean checkSecurity = true; + return getDirectConstructorCommon(refc, ctor, checkSecurity); + } + /** Check access and get the requested constructor, eliding security manager checks. */ + private MethodHandle getDirectConstructorNoSecurityManager(Class<?> refc, MemberName ctor) throws IllegalAccessException { + final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants + return getDirectConstructorCommon(refc, ctor, checkSecurity); + } + /** Common code for all constructors; do not call directly except from immediately above. */ + private MethodHandle getDirectConstructorCommon(Class<?> refc, MemberName ctor, + boolean checkSecurity) throws IllegalAccessException { assert(ctor.isConstructor()); checkAccess(REF_newInvokeSpecial, refc, ctor); + // Optionally check with the security manager; this isn't needed for unreflect* calls. + if (checkSecurity) + checkSecurityManager(refc, ctor); assert(!MethodHandleNatives.isCallerSensitive(ctor)); // maybeBindCaller not relevant here return DirectMethodHandle.make(ctor).setVarargs(ctor); } @@ -1348,29 +1699,75 @@ */ /*non-public*/ MethodHandle linkMethodHandleConstant(byte refKind, Class<?> defc, String name, Object type) throws ReflectiveOperationException { - MemberName resolved = null; - if (type instanceof MemberName) { - resolved = (MemberName) type; - if (!resolved.isResolved()) throw new InternalError("unresolved MemberName"); - assert(name == null || name.equals(resolved.getName())); + if (!(type instanceof Class || type instanceof MethodType)) + throw new InternalError("unresolved MemberName"); + MemberName member = new MemberName(refKind, defc, name, type); + MethodHandle mh = LOOKASIDE_TABLE.get(member); + if (mh != null) { + checkSymbolicClass(defc); + return mh; + } + MemberName resolved = resolveOrFail(refKind, member); + mh = getDirectMethodForConstant(refKind, defc, resolved); + if (mh instanceof DirectMethodHandle + && canBeCached(refKind, defc, resolved)) { + MemberName key = mh.internalMemberName(); + if (key != null) { + key = key.asNormalOriginal(); + } + if (member.equals(key)) { // better safe than sorry + LOOKASIDE_TABLE.put(key, (DirectMethodHandle) mh); + } + } + return mh; + } + private + boolean canBeCached(byte refKind, Class<?> defc, MemberName member) { + if (refKind == REF_invokeSpecial) { + return false; } + if (!Modifier.isPublic(defc.getModifiers()) || + !Modifier.isPublic(member.getDeclaringClass().getModifiers()) || + !member.isPublic() || + member.isCallerSensitive()) { + return false; + } + ClassLoader loader = defc.getClassLoader(); + if (!sun.misc.VM.isSystemDomainLoader(loader)) { + ClassLoader sysl = ClassLoader.getSystemClassLoader(); + boolean found = false; + while (sysl != null) { + if (loader == sysl) { found = true; break; } + sysl = sysl.getParent(); + } + if (!found) { + return false; + } + } + try { + MemberName resolved2 = publicLookup().resolveOrFail(refKind, + new MemberName(refKind, defc, member.getName(), member.getType())); + checkSecurityManager(defc, resolved2); + } catch (ReflectiveOperationException | SecurityException ex) { + return false; + } + return true; + } + private + MethodHandle getDirectMethodForConstant(byte refKind, Class<?> defc, MemberName member) + throws ReflectiveOperationException { if (MethodHandleNatives.refKindIsField(refKind)) { - MemberName field = (resolved != null) ? resolved - : resolveOrFail(refKind, defc, name, (Class<?>) type); - return getDirectField(refKind, defc, field); + return getDirectFieldNoSecurityManager(refKind, defc, member); } else if (MethodHandleNatives.refKindIsMethod(refKind)) { - MemberName method = (resolved != null) ? resolved - : resolveOrFail(refKind, defc, name, (MethodType) type); - return getDirectMethod(refKind, defc, method, lookupClass); + return getDirectMethodNoSecurityManager(refKind, defc, member, lookupClass); } else if (refKind == REF_newInvokeSpecial) { - assert(name == null || name.equals("<init>")); - MemberName ctor = (resolved != null) ? resolved - : resolveOrFail(REF_newInvokeSpecial, defc, name, (MethodType) type); - return getDirectConstructor(defc, ctor); + return getDirectConstructorNoSecurityManager(defc, member); } // oops - throw new ReflectiveOperationException("bad MethodHandle constant #"+refKind+" "+name+" : "+type); + throw newIllegalArgumentException("bad MethodHandle constant #"+member); } + + static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>(); } /** @@ -1430,22 +1827,26 @@ * <p> * Before invoking its target, the invoker will spread the final array, apply * reference casts as necessary, and unbox and widen primitive arguments. + * If, when the invoker is called, the supplied array argument does + * not have the correct number of elements, the invoker will throw + * an {@link IllegalArgumentException} instead of invoking the target. * <p> * This method is equivalent to the following code (though it may be more efficient): - * <p><blockquote><pre> + * <blockquote><pre>{@code MethodHandle invoker = MethodHandles.invoker(type); int spreadArgCount = type.parameterCount() - leadingArgCount; invoker = invoker.asSpreader(Object[].class, spreadArgCount); return invoker; - * </pre></blockquote> - * <p> + * }</pre></blockquote> * This method throws no reflective or security exceptions. * @param type the desired target type * @param leadingArgCount number of fixed arguments, to be passed unchanged to the target * @return a method handle suitable for invoking any method handle of the given type * @throws NullPointerException if {@code type} is null * @throws IllegalArgumentException if {@code leadingArgCount} is not in - * the range from 0 to {@code type.parameterCount()} inclusive + * the range from 0 to {@code type.parameterCount()} inclusive, + * or if the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> */ static public MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { @@ -1462,9 +1863,7 @@ * an additional leading argument of type {@code MethodHandle}. * <p> * This method is equivalent to the following code (though it may be more efficient): - * <p><blockquote><pre> -publicLookup().findVirtual(MethodHandle.class, "invokeExact", type) - * </pre></blockquote> + * {@code publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)} * * <p style="font-size:smaller;"> * <em>Discussion:</em> @@ -1479,7 +1878,7 @@ * 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}. - * <p> + * <p style="font-size:smaller;"> * <em>(Note: The invoker method is not available via the Core Reflection API. * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke} * on the declared {@code invokeExact} or {@code invoke} method will raise an @@ -1488,6 +1887,8 @@ * 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 + * @throws IllegalArgumentException if the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> */ static public MethodHandle exactInvoker(MethodType type) { @@ -1508,19 +1909,25 @@ * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle}, * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}. * <p> - * A {@linkplain MethodType#genericMethodType general method type}, + * This method is equivalent to the following code (though it may be more efficient): + * {@code publicLookup().findVirtual(MethodHandle.class, "invoke", type)} + * <p style="font-size:smaller;"> + * <em>Discussion:</em> + * A {@linkplain MethodType#genericMethodType general method type} is one which * mentions only {@code Object} arguments and return values. * An invoker for such a type is capable of calling any method handle * of the same arity as the general type. - * <p> - * This method is equivalent to the following code (though it may be more efficient): - * <p><blockquote><pre> -publicLookup().findVirtual(MethodHandle.class, "invoke", type) - * </pre></blockquote> + * <p style="font-size:smaller;"> + * <em>(Note: The invoker method is not available via the Core Reflection API. + * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke} + * on the declared {@code invokeExact} or {@code invoke} method will raise an + * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em> * <p> * 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 + * @throws IllegalArgumentException if the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> */ static public MethodHandle invoker(MethodType type) { @@ -1801,7 +2208,7 @@ * they will come after. * <p> * <b>Example:</b> - * <p><blockquote><pre> + * <p><blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... @@ -1812,11 +2219,11 @@ MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2)); assertEquals(bigType, d0.type()); assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z")); - * </pre></blockquote> + * }</pre></blockquote> * <p> * This method is also equivalent to the following code: * <p><blockquote><pre> - * {@link #dropArguments(MethodHandle,int,Class...) dropArguments}(target, pos, valueTypes.toArray(new Class[0])) + * {@link #dropArguments(MethodHandle,int,Class...) dropArguments}{@code (target, pos, valueTypes.toArray(new Class[0]))} * </pre></blockquote> * @param target the method handle to invoke after the arguments are dropped * @param valueTypes the type(s) of the argument(s) to drop @@ -1859,7 +2266,7 @@ * they will come after. * <p> * <b>Example:</b> - * <p><blockquote><pre> + * <p><blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... @@ -1874,11 +2281,11 @@ assertEquals("xy", (String) d2.invokeExact("x", "y", "z")); MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); - * </pre></blockquote> + * }</pre></blockquote> * <p> * This method is also equivalent to the following code: * <p><blockquote><pre> - * {@link #dropArguments(MethodHandle,int,List) dropArguments}(target, pos, Arrays.asList(valueTypes)) + * {@link #dropArguments(MethodHandle,int,List) dropArguments}{@code (target, pos, Arrays.asList(valueTypes))} * </pre></blockquote> * @param target the method handle to invoke after the arguments are dropped * @param valueTypes the type(s) of the argument(s) to drop @@ -1889,7 +2296,8 @@ * or if the {@code valueTypes} array or any of its elements is null * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class}, * or if {@code pos} is negative or greater than the arity of the target, - * or if the new method handle's type would have too many parameters + * or if the new method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> */ public static MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) { @@ -1923,8 +2331,8 @@ * It is an error if there are elements of {@code filters} * (null or not) * which do not correspond to argument positions in the target. - * <b>Example:</b> - * <p><blockquote><pre> + * <p><b>Example:</b> + * <p><blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... @@ -1939,15 +2347,15 @@ assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY MethodHandle f2 = filterArguments(cat, 0, upcase, upcase); assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY - * </pre></blockquote> + * }</pre></blockquote> * <p> Here is pseudocode for the resulting adapter: - * <blockquote><pre> + * <blockquote><pre>{@code * V target(P... p, A[i]... a[i], B... b); * A[i] filter[i](V[i]); * T adapter(P... p, V[i]... v[i], B... b) { * return target(p..., f[i](v[i])..., b...); * } - * </pre></blockquote> + * }</pre></blockquote> * * @param target the method handle to invoke after arguments are filtered * @param pos the position of the first argument to filter @@ -1957,7 +2365,9 @@ * 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 target as described above, - * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()} + * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()}, + * or if the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> */ public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { @@ -1989,15 +2399,120 @@ return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); } - // FIXME: Make this public in M1. - /*non-public*/ static - MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle collector) { + /** + * Adapts a target method handle by pre-processing + * a sub-sequence of its arguments with a filter (another method handle). + * The pre-processed arguments are replaced by the result (if any) of the + * filter function. + * The target is then called on the modified (usually shortened) argument list. + * <p> + * If the filter returns a value, the target must accept that value as + * its argument in position {@code pos}, preceded and/or followed by + * any arguments not passed to the filter. + * If the filter returns void, the target must accept all arguments + * not passed to the filter. + * No arguments are reordered, and a result returned from the filter + * replaces (in order) the whole subsequence of arguments originally + * passed to the adapter. + * <p> + * The argument types (if any) of the filter + * replace zero or one argument types of the target, at position {@code pos}, + * in the resulting adapted method handle. + * The return type of the filter (if any) must be identical to the + * argument type of the target at position {@code pos}, and that target argument + * is supplied by the return value of the filter. + * <p> + * In all cases, {@code pos} must be greater than or equal to zero, and + * {@code pos} must also be less than or equal to the target's arity. + * <p><b>Example:</b> + * <p><blockquote><pre>{@code +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; +... +MethodHandle deepToString = publicLookup() + .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); + +MethodHandle ts1 = deepToString.asCollector(String[].class, 1); +assertEquals("[strange]", (String) ts1.invokeExact("strange")); + +MethodHandle ts2 = deepToString.asCollector(String[].class, 2); +assertEquals("[up, down]", (String) ts2.invokeExact("up", "down")); + +MethodHandle ts3 = deepToString.asCollector(String[].class, 3); +MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2); +assertEquals("[top, [up, down], strange]", + (String) ts3_ts2.invokeExact("top", "up", "down", "strange")); + +MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1); +assertEquals("[top, [up, down], [strange]]", + (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange")); + +MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3); +assertEquals("[top, [[up, down, strange], charm], bottom]", + (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom")); + * }</pre></blockquote> + * <p> Here is pseudocode for the resulting adapter: + * <blockquote><pre>{@code + * T target(A...,V,C...); + * V filter(B...); + * T adapter(A... a,B... b,C... c) { + * V v = filter(b...); + * return target(a...,v,c...); + * } + * // and if the filter has no arguments: + * T target2(A...,V,C...); + * V filter2(); + * T adapter2(A... a,C... c) { + * V v = filter2(); + * return target2(a...,v,c...); + * } + * // and if the filter has a void return: + * T target3(A...,C...); + * void filter3(B...); + * void adapter3(A... a,B... b,C... c) { + * filter3(b...); + * return target3(a...,c...); + * } + * }</pre></blockquote> + * <p> + * A collection adapter {@code collectArguments(mh, 0, coll)} is equivalent to + * one which first "folds" the affected arguments, and then drops them, in separate + * steps as follows: + * <blockquote><pre>{@code + * mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //step 2 + * mh = MethodHandles.foldArguments(mh, coll); //step 1 + * }</pre></blockquote> + * If the target method handle consumes no arguments besides than the result + * (if any) of the filter {@code coll}, then {@code collectArguments(mh, 0, coll)} + * is equivalent to {@code filterReturnValue(coll, mh)}. + * If the filter method handle {@code coll} consumes one argument and produces + * a non-void result, then {@code collectArguments(mh, N, coll)} + * is equivalent to {@code filterArguments(mh, N, coll)}. + * Other equivalences are possible but would require argument permutation. + * + * @param target the method handle to invoke after filtering the subsequence of arguments + * @param pos the position of the first adapter argument to pass to the filter, + * and/or the target argument which receives the result of the filter + * @param filter method handle to call on the subsequence of arguments + * @return method handle which incorporates the specified argument subsequence filtering logic + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if the return type of {@code filter} + * is non-void and is not the same as the {@code pos} argument of the target, + * or if {@code pos} is not between 0 and the target's arity, inclusive, + * or if the resulting method handle's type would have + * <a href="MethodHandle.html#maxarity">too many parameters</a> + * @see MethodHandles#foldArguments + * @see MethodHandles#filterArguments + * @see MethodHandles#filterReturnValue + */ + public static + MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) { MethodType targetType = target.type(); - MethodType filterType = collector.type(); + MethodType filterType = filter.type(); if (filterType.returnType() != void.class && filterType.returnType() != targetType.parameterType(pos)) throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); - return MethodHandleImpl.makeCollectArguments(target, collector, pos, false); + return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); } /** @@ -2014,8 +2529,8 @@ * in the resulting adapted method handle. * The argument type of the filter (if any) must be identical to the * return type of the target. - * <b>Example:</b> - * <p><blockquote><pre> + * <p><b>Example:</b> + * <p><blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... @@ -2026,9 +2541,9 @@ System.out.println((String) cat.invokeExact("x", "y")); // xy MethodHandle f0 = filterReturnValue(cat, length); System.out.println((int) f0.invokeExact("x", "y")); // 2 - * </pre></blockquote> + * }</pre></blockquote> * <p> Here is pseudocode for the resulting adapter: - * <blockquote><pre> + * <blockquote><pre>{@code * V target(A...); * T filter(V); * T adapter(A... a) { @@ -2049,7 +2564,7 @@ * V v = target3(a...); * filter3(v); * } - * </pre></blockquote> + * }</pre></blockquote> * @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 @@ -2105,8 +2620,8 @@ * consider using {@link MethodHandle#asCollector asCollector} instead, since those * arguments will not need to be live on the stack on entry to the * target.) - * <b>Example:</b> - * <p><blockquote><pre> + * <p><b>Example:</b> + * <p><blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... @@ -2119,9 +2634,9 @@ MethodHandle catTrace = foldArguments(cat, trace); // also prints "boo": assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); - * </pre></blockquote> + * }</pre></blockquote> * <p> Here is pseudocode for the resulting adapter: - * <blockquote><pre> + * <blockquote><pre>{@code * // there are N arguments in A... * T target(V, A[N]..., B...); * V combiner(A...); @@ -2136,7 +2651,7 @@ * combiner2(a...); * return target2(a..., b...); * } - * </pre></blockquote> + * }</pre></blockquote> * @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 @@ -2179,7 +2694,7 @@ * of the test must be boolean, and the test is allowed * to have fewer arguments than the other two method handles. * <p> Here is pseudocode for the resulting adapter: - * <blockquote><pre> + * <blockquote><pre>{@code * boolean test(A...); * T target(A...,B...); * T fallback(A...,B...); @@ -2189,7 +2704,7 @@ * else * return fallback(a..., b...); * } - * </pre></blockquote> + * }</pre></blockquote> * Note that the test arguments ({@code a...} in the pseudocode) cannot * be modified by execution of the test, and so are passed unchanged * from the caller to the target or fallback as appropriate. @@ -2241,7 +2756,7 @@ * (similarly to the predicate in {@link #guardWithTest guardWithTest}). * Also, the handler must have an extra leading parameter of {@code exType} or a supertype. * <p> Here is pseudocode for the resulting adapter: - * <blockquote><pre> + * <blockquote><pre>{@code * T target(A..., B...); * T handler(ExType, A...); * T adapter(A... a, B... b) { @@ -2251,7 +2766,7 @@ * return handler(ex, a...); * } * } - * </pre></blockquote> + * }</pre></blockquote> * Note that the saved arguments ({@code a...} in the pseudocode) cannot * be modified by execution of the target, and so are passed unchanged * from the caller to the handler, if the handler is invoked.
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java Tue Oct 08 14:57:32 2013 -0700 @@ -77,7 +77,8 @@ * 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 <a href="package-summary.html#mtcon">package summary</a>. + * (For full details on method type constants, + * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.) * <p> * When the JVM materializes a {@code MethodType} from a descriptor string, * all classes named in the descriptor must be accessible, and will be loaded. @@ -94,9 +95,9 @@ 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 + private @Stable MethodTypeForm form; // erased form, plus cached data about primitives + private @Stable MethodType wrapAlt; // alternative wrapped/unwrapped version + private @Stable Invokers invokers; // cache of handy higher-order adapters /** * Check the given parameters for validity and store them into the final fields. @@ -940,10 +941,10 @@ * 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: - * <blockquote><pre> + * <blockquote><pre>{@code s.writeObject(this.returnType()); s.writeObject(this.parameterArray()); - * </pre></blockquote> + * }</pre></blockquote> * <p> * The deserialized field values are checked as if they were * provided to the factory method {@link #methodType(Class,Class[]) methodType}.
--- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Tue Oct 08 14:57:32 2013 -0700 @@ -28,6 +28,7 @@ import sun.invoke.util.Wrapper; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; + import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** * Shared information for a group of method types, which differ @@ -51,12 +52,13 @@ final MethodType basicType; // the canonical erasure, with primitives simplified // Cached adapter information: - /*lazy*/ MethodHandle genericInvoker; // JVM hook for inexact invoke - /*lazy*/ MethodHandle basicInvoker; // cached instance of MH.invokeBasic - /*lazy*/ MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction + @Stable String typeString; // argument type signature characters + @Stable MethodHandle genericInvoker; // JVM hook for inexact invoke + @Stable MethodHandle basicInvoker; // cached instance of MH.invokeBasic + @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction // Cached lambda form information, for basic types only: - final LambdaForm[] lambdaForms; + final @Stable LambdaForm[] lambdaForms; // Indexes into lambdaForms: static final int LF_INVVIRTUAL = 0, // DMH invokeVirtual @@ -73,7 +75,8 @@ LF_GEN_LINKER = 11, LF_GEN_INVOKER = 12, LF_CS_LINKER = 13, // linkToCallSite_CS - LF_LIMIT = 14; + LF_MH_LINKER = 14, // linkToCallSite_MH + LF_LIMIT = 15; public MethodType erasedType() { return erasedType; @@ -96,11 +99,24 @@ assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also MethodHandle invoker = basicInvoker; if (invoker != null) return invoker; - invoker = basicType.invokers().makeBasicInvoker(); + invoker = DirectMethodHandle.make(invokeBasicMethod(basicType)); basicInvoker = invoker; return invoker; } + // This next one is called from LambdaForm.NamedFunction.<init>. + /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { + assert(basicType == basicType.basicType()); + try { + // Do approximately the same as this public API call: + // Lookup.findVirtual(MethodHandle.class, name, type); + // But bypass access and corner case checks, since we know exactly what we need. + return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType); + } catch (ReflectiveOperationException ex) { + throw newInternalError("JVM cannot find invoker for "+basicType, ex); + } + } + /** * Build an MTF for a given type, which must have all references erased to Object. * This MTF will stand for that type and all un-erased variations.
--- a/jdk/src/share/classes/java/lang/invoke/MutableCallSite.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/MutableCallSite.java Tue Oct 08 14:57:32 2013 -0700 @@ -38,7 +38,7 @@ * Here is an example of a mutable call site which introduces a * state variable into a method handle chain. * <!-- JavaDocExamplesTest.testMutableCallSite --> - * <blockquote><pre> + * <blockquote><pre>{@code MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class)); MethodHandle MH_name = name.dynamicInvoker(); MethodType MT_str1 = MethodType.methodType(String.class); @@ -50,10 +50,10 @@ name.setTarget(MethodHandles.constant(String.class, "Fred")); assertEquals("FRED", (String) worker1.invokeExact()); // (mutation can be continued indefinitely) - * </pre></blockquote> + * }</pre></blockquote> * <p> * The same call site may be used in several places at once. - * <blockquote><pre> + * <blockquote><pre>{@code MethodType MT_str2 = MethodType.methodType(String.class, String.class); MethodHandle MH_cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); @@ -63,7 +63,7 @@ name.setTarget(MethodHandles.constant(String.class, "Wilma")); assertEquals("WILMA", (String) worker1.invokeExact()); assertEquals("Wilma, dear?", (String) worker2.invokeExact()); - * </pre></blockquote> + * }</pre></blockquote> * <p> * <em>Non-synchronization of target values:</em> * A write to a mutable call site's target does not force other threads
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/lang/invoke/Stable.java Tue Oct 08 14:57:32 2013 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import java.lang.annotation.*; + +/** + * A field may be annotated as stable if all of its component variables + * changes value at most once. + * A field's value counts as its component value. + * If the field is typed as an array, then all the non-null components + * of the array, of depth up to the rank of the field's array type, + * also count as component values. + * By extension, any variable (either array or field) which has annotated + * as stable is called a stable variable, and its non-null or non-zero + * value is called a stable value. + * <p> + * Since all fields begin with a default value of null for references + * (resp., zero for primitives), it follows that this annotation indicates + * that the first non-null (resp., non-zero) value stored in the field + * will never be changed. + * <p> + * If the field is not of an array type, there are no array elements, + * then the value indicated as stable is simply the value of the field. + * If the dynamic type of the field value is an array but the static type + * is not, the components of the array are <em>not</em> regarded as stable. + * <p> + * If the field is an array type, then both the field value and + * all the components of the field value (if the field value is non-null) + * are indicated to be stable. + * If the field type is an array type with rank {@code N > 1}, + * then each component of the field value (if the field value is non-null), + * is regarded as a stable array of rank {@code N-1}. + * <p> + * Fields which are declared {@code final} may also be annotated as stable. + * Since final fields already behave as stable values, such an annotation + * indicates no additional information, unless the type of the field is + * an array type. + * <p> + * It is (currently) undefined what happens if a field annotated as stable + * is given a third value. In practice, if the JVM relies on this annotation + * to promote a field reference to a constant, it may be that the Java memory + * model would appear to be broken, if such a constant (the second value of the field) + * is used as the value of the field even after the field value has changed. + */ +/* package-private */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@interface Stable { +}
--- a/jdk/src/share/classes/java/lang/invoke/SwitchPoint.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/invoke/SwitchPoint.java Tue Oct 08 14:57:32 2013 -0700 @@ -55,7 +55,7 @@ * At that point {@code guardWithTest} may ignore {@code T} and return {@code F}. * <p> * Here is an example of a switch point in action: - * <blockquote><pre> + * <blockquote><pre>{@code MethodHandle MH_strcat = MethodHandles.lookup() .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class)); SwitchPoint spt = new SwitchPoint(); @@ -68,7 +68,7 @@ SwitchPoint.invalidateAll(new SwitchPoint[]{ spt }); assert(spt.hasBeenInvalidated()); assertEquals("hodmet", (String) worker.invokeExact("met", "hod")); - * </pre></blockquote> + * }</pre></blockquote> * <p style="font-size:smaller;"> * <em>Discussion:</em> * Switch points are useful without subclassing. They may also be subclassed. @@ -82,7 +82,7 @@ * <em>Implementation Note:</em> * A switch point behaves as if implemented on top of {@link MutableCallSite}, * approximately as follows: - * <blockquote><pre> + * <blockquote><pre>{@code public class SwitchPoint { private static final MethodHandle K_true = MethodHandles.constant(boolean.class, true), @@ -106,7 +106,7 @@ MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0])); } } - * </pre></blockquote> + * }</pre></blockquote> * @author Remi Forax, JSR 292 EG */ public class SwitchPoint {
--- a/jdk/src/share/classes/java/lang/reflect/AnnotatedArrayType.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/AnnotatedArrayType.java Tue Oct 08 14:57:32 2013 -0700 @@ -27,17 +27,18 @@ /** - * AnnotatedArrayType represents the use of an array type, whose component - * type may itself represent the annotated use of a type. + * {@code AnnotatedArrayType} represents the potentially annotated use of an + * array type, whose component type may itself represent the annotated use of a + * type. * * @since 1.8 */ public interface AnnotatedArrayType extends AnnotatedType { /** - * Returns the annotated generic component type of this array type. + * Returns the potentially annotated generic component type of this array type. * - * @return the annotated generic component type of this array type + * @return the potentially annotated generic component type of this array type */ AnnotatedType getAnnotatedGenericComponentType(); }
--- a/jdk/src/share/classes/java/lang/reflect/AnnotatedParameterizedType.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/AnnotatedParameterizedType.java Tue Oct 08 14:57:32 2013 -0700 @@ -26,17 +26,18 @@ package java.lang.reflect; /** - * AnnotatedParameterizedType represents the use of a parameterized type, - * whose type arguments may themselves represent annotated uses of types. + * {@code AnnotatedParameterizedType} represents the potentially annotated use + * of a parameterized type, whose type arguments may themselves represent + * annotated uses of types. * * @since 1.8 */ public interface AnnotatedParameterizedType extends AnnotatedType { /** - * Returns the annotated actual type arguments of this parameterized type. + * Returns the potentially annotated actual type arguments of this parameterized type. * - * @return the annotated actual type arguments of this parameterized type + * @return the potentially annotated actual type arguments of this parameterized type */ AnnotatedType[] getAnnotatedActualTypeArguments(); }
--- a/jdk/src/share/classes/java/lang/reflect/AnnotatedType.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/AnnotatedType.java Tue Oct 08 14:57:32 2013 -0700 @@ -26,10 +26,10 @@ package java.lang.reflect; /** - * AnnotatedType represents the annotated use of a type in the program - * currently running in this VM. The use may be of any type in the Java - * programming language, including an array type, a parameterized type, a type - * variable, or a wildcard type. + * {@code AnnotatedType} represents the potentially annotated use of a type in + * the program currently running in this VM. The use may be of any type in the + * Java programming language, including an array type, a parameterized type, a + * type variable, or a wildcard type. * * @since 1.8 */
--- a/jdk/src/share/classes/java/lang/reflect/AnnotatedTypeVariable.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/AnnotatedTypeVariable.java Tue Oct 08 14:57:32 2013 -0700 @@ -26,18 +26,18 @@ package java.lang.reflect; /** - * AnnotatedTypeVariable represents the use of a type variable, whose - * declaration may have bounds which themselves represent annotated uses of - * types. + * {@code AnnotatedTypeVariable} represents the potentially annotated use of a + * type variable, whose declaration may have bounds which themselves represent + * annotated uses of types. * * @since 1.8 */ public interface AnnotatedTypeVariable extends AnnotatedType { /** - * Returns the annotated bounds of this type variable. + * Returns the potentially annotated bounds of this type variable. * - * @return the annotated bounds of this type variable + * @return the potentially annotated bounds of this type variable */ AnnotatedType[] getAnnotatedBounds(); }
--- a/jdk/src/share/classes/java/lang/reflect/AnnotatedWildcardType.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/AnnotatedWildcardType.java Tue Oct 08 14:57:32 2013 -0700 @@ -26,24 +26,25 @@ package java.lang.reflect; /** - * AnnotatedWildcardType represents the use of a wildcard type argument, whose - * upper or lower bounds may themselves represent annotated uses of types. + * {@code AnnotatedWildcardType} represents the potentially annotated use of a + * wildcard type argument, whose upper or lower bounds may themselves represent + * annotated uses of types. * * @since 1.8 */ public interface AnnotatedWildcardType extends AnnotatedType { /** - * Returns the annotated lower bounds of this wildcard type. + * Returns the potentially annotated lower bounds of this wildcard type. * - * @return the annotated lower bounds of this wildcard type + * @return the potentially annotated lower bounds of this wildcard type */ AnnotatedType[] getAnnotatedLowerBounds(); /** - * Returns the annotated upper bounds of this wildcard type. + * Returns the potentially annotated upper bounds of this wildcard type. * - * @return the annotated upper bounds of this wildcard type + * @return the potentially annotated upper bounds of this wildcard type */ AnnotatedType[] getAnnotatedUpperBounds(); }
--- a/jdk/src/share/classes/java/lang/reflect/Constructor.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/Constructor.java Tue Oct 08 14:57:32 2013 -0700 @@ -67,8 +67,6 @@ private transient ConstructorRepository genericInfo; private byte[] annotations; private byte[] parameterAnnotations; - // This is set by the vm at Constructor creation - private byte[] typeAnnotations; // Generics infrastructure // Accessor for factory @@ -141,8 +139,6 @@ res.root = this; // Might as well eagerly propagate this if already present res.constructorAccessor = constructorAccessor; - - res.typeAnnotations = typeAnnotations; return res; } @@ -155,10 +151,6 @@ byte[] getAnnotationBytes() { return annotations; } - @Override - byte[] getTypeAnnotationBytes() { - return typeAnnotations; - } /** * {@inheritDoc}
--- a/jdk/src/share/classes/java/lang/reflect/Executable.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/Executable.java Tue Oct 08 14:57:32 2013 -0700 @@ -51,7 +51,6 @@ * Accessor method to allow code sharing */ abstract byte[] getAnnotationBytes(); - abstract byte[] getTypeAnnotationBytes(); /** * Does the Executable have generic information. @@ -287,12 +286,14 @@ * this object. Returns an array of length 0 if the executable * has no parameters. * - * The parameters of the underlying executable do not necessarily + * <p>The parameters of the underlying executable do not necessarily * have unique names, or names that are legal identifiers in the * Java programming language (JLS 3.8). * + * @throws MalformedParametersException if the class file contains + * a MethodParameters attribute that is improperly formatted. * @return an array of {@code Parameter} objects representing all - * the parameters to the executable this object represents + * the parameters to the executable this object represents. */ public Parameter[] getParameters() { // TODO: This may eventually need to be guarded by security @@ -316,6 +317,30 @@ return out; } + private void verifyParameters(final Parameter[] parameters) { + final int mask = Modifier.FINAL | Modifier.SYNTHETIC | Modifier.MANDATED; + + if (getParameterTypes().length != parameters.length) + throw new MalformedParametersException("Wrong number of parameters in MethodParameters attribute"); + + for (Parameter parameter : parameters) { + final String name = parameter.getRealName(); + final int mods = parameter.getModifiers(); + + if (name != null) { + if (name.isEmpty() || name.indexOf('.') != -1 || + name.indexOf(';') != -1 || name.indexOf('[') != -1 || + name.indexOf('/') != -1) { + throw new MalformedParametersException("Invalid parameter name \"" + name + "\""); + } + } + + if (mods != (mods & mask)) { + throw new MalformedParametersException("Invalid parameter modifiers"); + } + } + } + private Parameter[] privateGetParameters() { // Use tmp to avoid multiple writes to a volatile. Parameter[] tmp = parameters; @@ -323,7 +348,12 @@ if (tmp == null) { // Otherwise, go to the JVM to get them - tmp = getParameters0(); + try { + tmp = getParameters0(); + } catch(IllegalArgumentException e) { + // Rethrow ClassFormatErrors + throw new MalformedParametersException("Invalid constant pool index"); + } // If we get back nothing, then synthesize parameters if (tmp == null) { @@ -331,6 +361,7 @@ tmp = synthesizeAllParams(); } else { hasRealParameterData = true; + verifyParameters(tmp); } parameters = tmp; @@ -352,6 +383,12 @@ private transient volatile Parameter[] parameters; private native Parameter[] getParameters0(); + private native byte[] getTypeAnnotationBytes0(); + + // Needed by reflectaccess + byte[] getTypeAnnotationBytes() { + return getTypeAnnotationBytes0(); + } /** * Returns an array of {@code Class} objects that represent the @@ -514,18 +551,20 @@ } /** - * Returns an AnnotatedType object that represents the use of a type to + * Returns an {@code AnnotatedType} object that represents the use of a type to * specify the return type of the method/constructor represented by this * Executable. * - * If this Executable represents a constructor, the AnnotatedType object - * represents the type of the constructed object. + * If this {@code Executable} object represents a constructor, the {@code + * AnnotatedType} object represents the type of the constructed object. * - * If this Executable represents a method, the AnnotatedType object - * represents the use of a type to specify the return type of the method. + * If this {@code Executable} object represents a method, the {@code + * AnnotatedType} object represents the use of a type to specify the return + * type of the method. * - * @return an object representing the return type of this method - * or constructor + * @return an object representing the return type of the method + * or constructor represented by this {@code Executable} + * * @since 1.8 */ public abstract AnnotatedType getAnnotatedReturnType(); @@ -539,7 +578,7 @@ * @since 1.8 */ AnnotatedType getAnnotatedReturnType0(Type returnType) { - return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes(), + return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes0(), sun.misc.SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringClass()), this, @@ -549,25 +588,30 @@ } /** - * Returns an AnnotatedType object that represents the use of a type to - * specify the receiver type of the method/constructor represented by this - * Executable. The receiver type of a method/constructor is available only - * if the method/constructor declares a formal parameter called 'this'. + * Returns an {@code AnnotatedType} object that represents the use of a + * type to specify the receiver type of the method/constructor represented + * by this Executable object. The receiver type of a method/constructor is + * available only if the method/constructor has a <em>receiver + * parameter</em> (JLS 8.4.1). * - * Returns null if this Executable represents a constructor or instance - * method that either declares no formal parameter called 'this', or - * declares a formal parameter called 'this' with no annotations on its - * type. + * If this {@code Executable} object represents a constructor or instance + * method that does not have a receiver parameter, or has a receiver + * parameter with no annotations on its type, then the return value is an + * {@code AnnotatedType} object representing an element with no + * annotations. * - * Returns null if this Executable represents a static method. + * If this {@code Executable} object represents a static method, then the + * return value is null. * - * @return an object representing the receiver type of the - * method or constructor represented by this Executable + * @return an object representing the receiver type of the method or + * constructor represented by this {@code Executable} * * @since 1.8 */ public AnnotatedType getAnnotatedReceiverType() { - return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes(), + if (Modifier.isStatic(this.getModifiers())) + return null; + return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes0(), sun.misc.SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringClass()), this, @@ -577,8 +621,8 @@ } /** - * Returns an array of AnnotatedType objects that represent the use of - * types to specify formal parameter types of the method/constructor + * Returns an array of {@code AnnotatedType} objects that represent the use + * of types to specify formal parameter types of the method/constructor * represented by this Executable. The order of the objects in the array * corresponds to the order of the formal parameter types in the * declaration of the method/constructor. @@ -587,12 +631,13 @@ * parameters. * * @return an array of objects representing the types of the - * formal parameters of this method or constructor + * formal parameters of the method or constructor represented by this + * {@code Executable} * * @since 1.8 */ public AnnotatedType[] getAnnotatedParameterTypes() { - return TypeAnnotationParser.buildAnnotatedTypes(getTypeAnnotationBytes(), + return TypeAnnotationParser.buildAnnotatedTypes(getTypeAnnotationBytes0(), sun.misc.SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringClass()), this, @@ -602,8 +647,8 @@ } /** - * Returns an array of AnnotatedType objects that represent the use of - * types to specify the declared exceptions of the method/constructor + * Returns an array of {@code AnnotatedType} objects that represent the use + * of types to specify the declared exceptions of the method/constructor * represented by this Executable. The order of the objects in the array * corresponds to the order of the exception types in the declaration of * the method/constructor. @@ -612,12 +657,13 @@ * exceptions. * * @return an array of objects representing the declared - * exceptions of this method or constructor + * exceptions of the method or constructor represented by this {@code + * Executable} * * @since 1.8 */ public AnnotatedType[] getAnnotatedExceptionTypes() { - return TypeAnnotationParser.buildAnnotatedTypes(getTypeAnnotationBytes(), + return TypeAnnotationParser.buildAnnotatedTypes(getTypeAnnotationBytes0(), sun.misc.SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringClass()), this,
--- a/jdk/src/share/classes/java/lang/reflect/Field.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/Field.java Tue Oct 08 14:57:32 2013 -0700 @@ -82,8 +82,6 @@ // currently only two levels deep (i.e., one root Field and // potentially many Field objects pointing to it.) private Field root; - // This is set by the vm at Field creation - private byte[] typeAnnotations; // Generics infrastructure @@ -149,7 +147,6 @@ res.fieldAccessor = fieldAccessor; res.overrideFieldAccessor = overrideFieldAccessor; - res.typeAnnotations = typeAnnotations; return res; } @@ -1148,6 +1145,8 @@ return declaredAnnotations; } + private native byte[] getTypeAnnotationBytes0(); + /** * Returns an AnnotatedType object that represents the use of a type to specify * the declared type of the field represented by this Field. @@ -1157,7 +1156,7 @@ * @since 1.8 */ public AnnotatedType getAnnotatedType() { - return TypeAnnotationParser.buildAnnotatedType(typeAnnotations, + return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes0(), sun.misc.SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringClass()), this,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/lang/reflect/MalformedParametersException.java Tue Oct 08 14:57:32 2013 -0700 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + +/** + * Thrown when {@link java.lang.reflect.Executable#getParameters the + * java.lang.reflect package} attempts to read method parameters from + * a class file and determines that one or more parameters are + * malformed. + * + * <p>The following is a list of conditions under which this exception + * can be thrown: + * <ul> + * <li> The number of parameters (parameter_count) is wrong for the method + * <li> A constant pool index is out of bounds. + * <li> A constant pool index does not refer to a UTF-8 entry + * <li> A parameter's name is "", or contains an illegal character + * <li> The flags field contains an illegal flag (something other than + * FINAL, SYNTHETIC, or MANDATED) + * </ul> + * + * See {@link java.lang.reflect.Executable#getParameters} for more + * information. + * + * @see java.lang.reflect.Executable#getParameters + * @since 1.8 + */ +public class MalformedParametersException extends RuntimeException { + + private static final long serialVersionUID = 20130919L; + + public MalformedParametersException() {} + + public MalformedParametersException(String reason) { + super(reason); + } +}
--- a/jdk/src/share/classes/java/lang/reflect/Method.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/Method.java Tue Oct 08 14:57:32 2013 -0700 @@ -80,8 +80,6 @@ // currently only two levels deep (i.e., one root Method and // potentially many Method objects pointing to it.) private Method root; - // This is set by the vm at Method creation - private byte[] typeAnnotations; // Generics infrastructure private String getGenericSignature() {return signature;} @@ -152,8 +150,6 @@ res.root = this; // Might as well eagerly propagate this if already present res.methodAccessor = methodAccessor; - - res.typeAnnotations = typeAnnotations; return res; } @@ -166,10 +162,6 @@ byte[] getAnnotationBytes() { return annotations; } - @Override - byte[] getTypeAnnotationBytes() { - return typeAnnotations; - } /** * {@inheritDoc}
--- a/jdk/src/share/classes/java/lang/reflect/Parameter.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/Parameter.java Tue Oct 08 14:57:32 2013 -0700 @@ -104,7 +104,7 @@ * to the class file. */ public boolean isNamePresent() { - return executable.hasRealParameterData(); + return executable.hasRealParameterData() && name != null; } /** @@ -182,6 +182,11 @@ return name; } + // Package-private accessor to the real name field. + String getRealName() { + return name; + } + /** * Returns a {@code Type} object that identifies the parameterized * type for the parameter represented by this {@code Parameter}
--- a/jdk/src/share/classes/java/nio/file/Files.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/nio/file/Files.java Tue Oct 08 14:57:32 2013 -0700 @@ -2128,7 +2128,7 @@ /** * Tests whether a file is a symbolic link. * - * <p> Where is it required to distinguish an I/O exception from the case + * <p> Where it is required to distinguish an I/O exception from the case * that the file is not a symbolic link then the file attributes can be * read with the {@link #readAttributes(Path,Class,LinkOption[]) * readAttributes} method and the file type tested with the {@link @@ -2164,7 +2164,7 @@ * of the link is read. If the option {@link LinkOption#NOFOLLOW_LINKS * NOFOLLOW_LINKS} is present then symbolic links are not followed. * - * <p> Where is it required to distinguish an I/O exception from the case + * <p> Where it is required to distinguish an I/O exception from the case * that the file is not a directory then the file attributes can be * read with the {@link #readAttributes(Path,Class,LinkOption[]) * readAttributes} method and the file type tested with the {@link @@ -2201,7 +2201,7 @@ * of the link is read. If the option {@link LinkOption#NOFOLLOW_LINKS * NOFOLLOW_LINKS} is present then symbolic links are not followed. * - * <p> Where is it required to distinguish an I/O exception from the case + * <p> Where it is required to distinguish an I/O exception from the case * that the file is not a regular file then the file attributes can be * read with the {@link #readAttributes(Path,Class,LinkOption[]) * readAttributes} method and the file type tested with the {@link @@ -3082,13 +3082,13 @@ * method is invoked to check read access to the file. */ public static byte[] readAllBytes(Path path) throws IOException { - try (FileChannel fc = FileChannel.open(path); - InputStream is = Channels.newInputStream(fc)) { - long size = fc.size(); + try (SeekableByteChannel sbc = Files.newByteChannel(path); + InputStream in = Channels.newInputStream(sbc)) { + long size = sbc.size(); if (size > (long)MAX_BUFFER_SIZE) throw new OutOfMemoryError("Required array size too large"); - return read(is, (int)size); + return read(in, (int)size); } }
--- a/jdk/src/share/classes/java/nio/file/Path.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/nio/file/Path.java Tue Oct 08 14:57:32 2013 -0700 @@ -315,7 +315,7 @@ * and parent directory. In such file systems all occurrences of "{@code .}" * are considered redundant. If a "{@code ..}" is preceded by a * non-"{@code ..}" name then both names are considered redundant (the - * process to identify such names is repeated until is it no longer + * process to identify such names is repeated until it is no longer * applicable). * * <p> This method does not access the file system; the path may not locate
--- a/jdk/src/share/classes/java/security/SecureRandom.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/security/SecureRandom.java Tue Oct 08 14:57:32 2013 -0700 @@ -578,39 +578,30 @@ /** * Returns a {@code SecureRandom} object that was selected by using * the algorithms/providers specified in the {@code - * securerandom.strongAlgorithms} Security property. + * securerandom.strongAlgorithms} {@link Security} property. * <p> * Some situations require strong random values, such as when * creating high-value/long-lived secrets like RSA public/private * keys. To help guide applications in selecting a suitable strong - * {@code SecureRandom} implementation, Java distributions should + * {@code SecureRandom} implementation, Java distributions * include a list of known strong {@code SecureRandom} * implementations in the {@code securerandom.strongAlgorithms} * Security property. - * - * <pre> - * SecureRandom sr = SecureRandom.getStrongSecureRandom(); - * - * if (sr == null) { - * // Decide if this is a problem, and whether to recover. - * sr = new SecureRandom(); - * if (!goodEnough(sr)) { - * return; - * } - * } - * - * keyPairGenerator.initialize(2048, sr); - * </pre> + * <p> + * Every implementation of the Java platform is required to + * support at least one strong {@code SecureRandom} implementation. * * @return a strong {@code SecureRandom} implementation as indicated - * by the {@code securerandom.strongAlgorithms} Security property, or - * null if none are available. + * by the {@code securerandom.strongAlgorithms} Security property + * + * @throws NoSuchAlgorithmException if no algorithm is available * * @see Security#getProperty(String) * * @since 1.8 */ - public static SecureRandom getStrongSecureRandom() { + public static SecureRandom getInstanceStrong() + throws NoSuchAlgorithmException { String property = AccessController.doPrivileged( new PrivilegedAction<String>() { @@ -622,7 +613,8 @@ }); if ((property == null) || (property.length() == 0)) { - return null; + throw new NoSuchAlgorithmException( + "Null/empty securerandom.strongAlgorithms Security Property"); } String remainder = property; @@ -649,7 +641,8 @@ } } - return null; + throw new NoSuchAlgorithmException( + "No strong SecureRandom impls available: " + property); } // Declare serialVersionUID to be compatible with JDK1.1
--- a/jdk/src/share/classes/java/time/Duration.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/Duration.java Tue Oct 08 14:57:32 2013 -0700 @@ -441,9 +441,13 @@ //----------------------------------------------------------------------- /** - * Obtains a {@code Duration} representing the duration between two instants. + * Obtains a {@code Duration} representing the duration between two temporal objects. * <p> - * This calculates the duration between two temporal objects of the same type. + * This calculates the duration between two temporal objects. If the objects + * are of different types, then the duration is calculated based on the type + * of the first object. For example, if the first argument is a {@code LocalTime} + * then the second argument is converted to a {@code LocalTime}. + * <p> * The specified temporal objects must support the {@link ChronoUnit#SECONDS SECONDS} unit. * For full accuracy, either the {@link ChronoUnit#NANOS NANOS} unit or the * {@link ChronoField#NANO_OF_SECOND NANO_OF_SECOND} field should be supported.
--- a/jdk/src/share/classes/java/time/Instant.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/Instant.java Tue Oct 08 14:57:32 2013 -0700 @@ -362,6 +362,10 @@ * @throws DateTimeException if unable to convert to an {@code Instant} */ public static Instant from(TemporalAccessor temporal) { + if (temporal instanceof Instant) { + return (Instant) temporal; + } + Objects.requireNonNull(temporal, "temporal"); long instantSecs = temporal.getLong(INSTANT_SECONDS); int nanoOfSecond = temporal.get(NANO_OF_SECOND); return Instant.ofEpochSecond(instantSecs, nanoOfSecond); @@ -370,7 +374,7 @@ //----------------------------------------------------------------------- /** * Obtains an instance of {@code Instant} from a text string such as - * {@code 2007-12-03T10:15:30:00}. + * {@code 2007-12-03T10:15:30.00Z}. * <p> * The string must represent a valid instant in UTC and is parsed using * {@link DateTimeFormatter#ISO_INSTANT}. @@ -1091,7 +1095,8 @@ * The result will be negative if the end is before the start. * The calculation returns a whole number, representing the number of * complete units between the two instants. - * The {@code Temporal} passed to this method must be an {@code Instant}. + * The {@code Temporal} passed to this method is converted to a + * {@code Instant} using {@link #from(TemporalAccessor)}. * For example, the amount in days between two dates can be calculated * using {@code startInstant.until(endInstant, SECONDS)}. * <p> @@ -1112,25 +1117,22 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endInstant the end date, which must be an {@code Instant}, not null + * @param endExclusive the end date, exclusive, which is converted to an {@code Instant}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this instant and the end instant - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to an {@code Instant} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endInstant, TemporalUnit unit) { - if (endInstant instanceof Instant == false) { - Objects.requireNonNull(endInstant, "endInstant"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - Instant end = (Instant) endInstant; + public long until(Temporal endExclusive, TemporalUnit unit) { + Instant end = Instant.from(endExclusive); if (unit instanceof ChronoUnit) { ChronoUnit f = (ChronoUnit) unit; switch (f) { @@ -1145,7 +1147,7 @@ } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return unit.between(this, endInstant); + return unit.between(this, end); } private long nanosUntil(Instant end) {
--- a/jdk/src/share/classes/java/time/LocalDate.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/LocalDate.java Tue Oct 08 14:57:32 2013 -0700 @@ -353,6 +353,7 @@ * @throws DateTimeException if unable to convert to a {@code LocalDate} */ public static LocalDate from(TemporalAccessor temporal) { + Objects.requireNonNull(temporal, "temporal"); LocalDate date = temporal.query(TemporalQuery.localDate()); if (date == null) { throw new DateTimeException("Unable to obtain LocalDate from TemporalAccessor: " + temporal.getClass()); @@ -1125,6 +1126,11 @@ */ @Override public LocalDate plus(TemporalAmount amountToAdd) { + if (amountToAdd instanceof Period) { + Period periodToAdd = (Period) amountToAdd; + return plusMonths(periodToAdd.toTotalMonths()).plusDays(periodToAdd.getDays()); + } + Objects.requireNonNull(amountToAdd, "amountToAdd"); return (LocalDate) amountToAdd.addTo(this); } @@ -1353,6 +1359,11 @@ */ @Override public LocalDate minus(TemporalAmount amountToSubtract) { + if (amountToSubtract instanceof Period) { + Period periodToSubtract = (Period) amountToSubtract; + return minusMonths(periodToSubtract.toTotalMonths()).minusDays(periodToSubtract.getDays()); + } + Objects.requireNonNull(amountToSubtract, "amountToSubtract"); return (LocalDate) amountToSubtract.subtractFrom(this); } @@ -1531,7 +1542,8 @@ * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date. * The result will be negative if the end is before the start. - * The {@code Temporal} passed to this method must be a {@code LocalDate}. + * The {@code Temporal} passed to this method is converted to a + * {@code LocalDate} using {@link #from(TemporalAccessor)}. * For example, the amount in days between two dates can be calculated * using {@code startDate.until(endDate, DAYS)}. * <p> @@ -1557,26 +1569,22 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endDate the end date, which must be a {@code LocalDate}, not null + * @param endExclusive the end date, exclusive, which is converted to a {@code LocalDate}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this date and the end date - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code LocalDate} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endDate, TemporalUnit unit) { - Objects.requireNonNull(unit, "unit"); - if (endDate instanceof LocalDate == false) { - Objects.requireNonNull(endDate, "endDate"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - LocalDate end = (LocalDate) endDate; + public long until(Temporal endExclusive, TemporalUnit unit) { + LocalDate end = LocalDate.from(endExclusive); if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case DAYS: return daysUntil(end); @@ -1590,7 +1598,7 @@ } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return unit.between(this, endDate); + return unit.between(this, end); } long daysUntil(LocalDate end) { @@ -1632,12 +1640,12 @@ * </pre> * The choice should be made based on which makes the code more readable. * - * @param endDate the end date, exclusive, which may be in any chronology, not null + * @param endDateExclusive the end date, exclusive, which may be in any chronology, not null * @return the period between this date and the end date, not null */ @Override - public Period until(ChronoLocalDate endDate) { - LocalDate end = LocalDate.from(endDate); + public Period until(ChronoLocalDate endDateExclusive) { + LocalDate end = LocalDate.from(endDateExclusive); long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe int days = end.day - this.day; if (totalMonths > 0 && days < 0) {
--- a/jdk/src/share/classes/java/time/LocalDateTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/LocalDateTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -1129,6 +1129,11 @@ */ @Override public LocalDateTime plus(TemporalAmount amountToAdd) { + if (amountToAdd instanceof Period) { + Period periodToAdd = (Period) amountToAdd; + return with(date.plus(periodToAdd), time); + } + Objects.requireNonNull(amountToAdd, "amountToAdd"); return (LocalDateTime) amountToAdd.addTo(this); } @@ -1343,6 +1348,11 @@ */ @Override public LocalDateTime minus(TemporalAmount amountToSubtract) { + if (amountToSubtract instanceof Period) { + Period periodToSubtract = (Period) amountToSubtract; + return with(date.minus(periodToSubtract), time); + } + Objects.requireNonNull(amountToSubtract, "amountToSubtract"); return (LocalDateTime) amountToSubtract.subtractFrom(this); } @@ -1611,7 +1621,8 @@ * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date-time. * The result will be negative if the end is before the start. - * The {@code Temporal} passed to this method must be a {@code LocalDateTime}. + * The {@code Temporal} passed to this method is converted to a + * {@code LocalDateTime} using {@link #from(TemporalAccessor)}. * For example, the amount in days between two date-times can be calculated * using {@code startDateTime.until(endDateTime, DAYS)}. * <p> @@ -1639,25 +1650,22 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endDateTime the end date-time, which must be a {@code LocalDateTime}, not null + * @param endExclusive the end date, exclusive, which is converted to a {@code LocalDateTime}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this date-time and the end date-time - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code LocalDateTime} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endDateTime, TemporalUnit unit) { - if (endDateTime instanceof LocalDateTime == false) { - Objects.requireNonNull(endDateTime, "endDateTime"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - LocalDateTime end = (LocalDateTime) endDateTime; + public long until(Temporal endExclusive, TemporalUnit unit) { + LocalDateTime end = LocalDateTime.from(endExclusive); if (unit instanceof ChronoUnit) { if (unit.isTimeBased()) { long amount = date.daysUntil(end.date); @@ -1711,7 +1719,7 @@ } return date.until(endDate, unit); } - return unit.between(this, endDateTime); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/LocalTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/LocalTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -394,6 +394,7 @@ * @throws DateTimeException if unable to convert to a {@code LocalTime} */ public static LocalTime from(TemporalAccessor temporal) { + Objects.requireNonNull(temporal, "temporal"); LocalTime time = temporal.query(TemporalQuery.localTime()); if (time == null) { throw new DateTimeException("Unable to obtain LocalTime from TemporalAccessor: " + temporal.getClass()); @@ -1330,7 +1331,8 @@ * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified time. * The result will be negative if the end is before the start. - * The {@code Temporal} passed to this method must be a {@code LocalTime}. + * The {@code Temporal} passed to this method is converted to a + * {@code LocalTime} using {@link #from(TemporalAccessor)}. * For example, the amount in hours between two times can be calculated * using {@code startTime.until(endTime, HOURS)}. * <p> @@ -1356,25 +1358,22 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endTime the end time, which must be a {@code LocalTime}, not null + * @param endExclusive the end time, exclusive, which is converted to a {@code LocalTime}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this time and the end time - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code LocalTime} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endTime, TemporalUnit unit) { - if (endTime instanceof LocalTime == false) { - Objects.requireNonNull(endTime, "endTime"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - LocalTime end = (LocalTime) endTime; + public long until(Temporal endExclusive, TemporalUnit unit) { + LocalTime end = LocalTime.from(endExclusive); if (unit instanceof ChronoUnit) { long nanosUntil = end.toNanoOfDay() - toNanoOfDay(); // no overflow switch ((ChronoUnit) unit) { @@ -1388,7 +1387,7 @@ } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return unit.between(this, endTime); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/MonthDay.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/MonthDay.java Tue Oct 08 14:57:32 2013 -0700 @@ -246,7 +246,8 @@ * <p> * The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} fields. - * The extraction is only permitted if the date-time has an ISO chronology. + * The extraction is only permitted if the temporal object has an ISO + * chronology, or can be converted to a {@code LocalDate}. * <p> * This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used in queries via method reference, {@code MonthDay::from}.
--- a/jdk/src/share/classes/java/time/OffsetDateTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/OffsetDateTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -1592,7 +1592,8 @@ * For example, the period in days between two date-times can be calculated * using {@code startDateTime.until(endDateTime, DAYS)}. * <p> - * The {@code Temporal} passed to this method must be an {@code OffsetDateTime}. + * The {@code Temporal} passed to this method is converted to a + * {@code OffsetDateTime} using {@link #from(TemporalAccessor)}. * If the offset differs between the two date-times, the specified * end date-time is normalized to have the same offset as this date-time. * <p> @@ -1620,30 +1621,27 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endDateTime the end date-time, which must be an {@code OffsetDateTime}, not null + * @param endExclusive the end date, exclusive, which is converted to an {@code OffsetDateTime}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this date-time and the end date-time - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to an {@code OffsetDateTime} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endDateTime, TemporalUnit unit) { - if (endDateTime instanceof OffsetDateTime == false) { - Objects.requireNonNull(endDateTime, "endDateTime"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } + public long until(Temporal endExclusive, TemporalUnit unit) { + OffsetDateTime end = OffsetDateTime.from(endExclusive); if (unit instanceof ChronoUnit) { - OffsetDateTime end = (OffsetDateTime) endDateTime; end = end.withOffsetSameInstant(offset); return dateTime.until(end.dateTime, unit); } - return unit.between(this, endDateTime); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/OffsetTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/OffsetTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -1124,7 +1124,8 @@ * For example, the period in hours between two times can be calculated * using {@code startTime.until(endTime, HOURS)}. * <p> - * The {@code Temporal} passed to this method must be an {@code OffsetTime}. + * The {@code Temporal} passed to this method is converted to a + * {@code OffsetTime} using {@link #from(TemporalAccessor)}. * If the offset differs between the two times, then the specified * end time is normalized to have the same offset as this time. * <p> @@ -1150,26 +1151,23 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endTime the end time, which must be an {@code OffsetTime}, not null + * @param endExclusive the end date, exclusive, which is converted to an {@code OffsetTime}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this time and the end time - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to an {@code OffsetTime} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endTime, TemporalUnit unit) { - if (endTime instanceof OffsetTime == false) { - Objects.requireNonNull(endTime, "endTime"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } + public long until(Temporal endExclusive, TemporalUnit unit) { + OffsetTime end = OffsetTime.from(endExclusive); if (unit instanceof ChronoUnit) { - OffsetTime end = (OffsetTime) endTime; long nanosUntil = end.toEpochNano() - toEpochNano(); // no overflow switch ((ChronoUnit) unit) { case NANOS: return nanosUntil; @@ -1182,7 +1180,7 @@ } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return unit.between(this, endTime); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/Period.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/Period.java Tue Oct 08 14:57:32 2013 -0700 @@ -61,7 +61,6 @@ */ package java.time; -import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.YEARS; @@ -70,17 +69,19 @@ import java.io.DataOutput; import java.io.IOException; import java.io.InvalidObjectException; -import java.io.InvalidObjectException; import java.io.Serializable; import java.time.chrono.ChronoLocalDate; +import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; +import java.time.chrono.IsoChronology; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; -import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -89,12 +90,13 @@ import java.util.regex.Pattern; /** - * A date-based amount of time, such as '2 years, 3 months and 4 days'. + * A date-based amount of time in the ISO-8601 calendar system, + * such as '2 years, 3 months and 4 days'. * <p> * This class models a quantity or amount of time in terms of years, months and days. * See {@link Duration} for the time-based equivalent to this class. * <p> - * Durations and period differ in their treatment of daylight savings time + * Durations and periods differ in their treatment of daylight savings time * when added to {@link ZonedDateTime}. A {@code Duration} will add an exact * number of seconds, thus a duration of one day is always exactly 24 hours. * By contrast, a {@code Period} will add a conceptual day, trying to maintain @@ -110,14 +112,12 @@ * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}. * All three fields are always present, but may be set to zero. * <p> - * The period may be used with any calendar system. - * The meaning of a "year" or "month" is only applied when the object is added to a date. + * The ISO-8601 calendar system is the modern civil calendar system used today + * in most of the world. It is equivalent to the proleptic Gregorian calendar + * system, in which today's rules for leap years are applied for all time. * <p> * The period is modeled as a directed amount of time, meaning that individual parts of the * period may be negative. - * <p> - * The months and years fields may be {@linkplain #normalized() normalized}. - * The normalization assumes a 12 month year, so is not appropriate for all calendar systems. * * @implSpec * This class is immutable and thread-safe. @@ -125,7 +125,7 @@ * @since 1.8 */ public final class Period - implements TemporalAmount, Serializable { + implements ChronoPeriod, Serializable { /** * A constant for a period of zero. @@ -140,6 +140,7 @@ */ private final static Pattern PATTERN = Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE); + /** * The set of supported units. */ @@ -234,12 +235,14 @@ * <p> * This obtains a period based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be - * date-based or time-based, which this factory extracts to a period. + * date-based or time-based, which this factory extracts to a {@code Period}. * <p> * The conversion loops around the set of units from the amount and uses * the {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} * and {@link ChronoUnit#DAYS DAYS} units to create a period. * If any other units are found then an exception is thrown. + * <p> + * If the amount is a {@code ChronoPeriod} then it must use the ISO chronology. * * @param amount the temporal amount to convert, not null * @return the equivalent period, not null @@ -247,6 +250,14 @@ * @throws ArithmeticException if the amount of years, months or days exceeds an int */ public static Period from(TemporalAmount amount) { + if (amount instanceof Period) { + return (Period) amount; + } + if (amount instanceof ChronoPeriod) { + if (IsoChronology.INSTANCE.equals(((ChronoPeriod) amount).getChronology()) == false) { + throw new DateTimeException("Period requires ISO chronology: " + amount); + } + } Objects.requireNonNull(amount, "amount"); int years = 0; int months = 0; @@ -358,13 +369,13 @@ * The result of this method can be a negative period if the end is before the start. * The negative sign will be the same in each of year, month and day. * - * @param startDate the start date, inclusive, not null - * @param endDate the end date, exclusive, not null + * @param startDateInclusive the start date, inclusive, not null + * @param endDateExclusive the end date, exclusive, not null * @return the period between this date and the end date, not null * @see ChronoLocalDate#until(ChronoLocalDate) */ - public static Period between(LocalDate startDate, LocalDate endDate) { - return startDate.until(endDate); + public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) { + return startDateInclusive.until(endDateExclusive); } //----------------------------------------------------------------------- @@ -439,6 +450,21 @@ return SUPPORTED_UNITS; } + /** + * Gets the chronology of this period, which is the ISO calendar system. + * <p> + * The {@code Chronology} represents the calendar system in use. + * The ISO-8601 calendar system is the modern civil calendar system used today + * in most of the world. It is equivalent to the proleptic Gregorian calendar + * system, in which today's rules for leap years are applied for all time. + * + * @return the ISO chronology, not null + */ + @Override + public IsoChronology getChronology() { + return IsoChronology.INSTANCE; + } + //----------------------------------------------------------------------- /** * Checks if all three units of this period are zero. @@ -468,7 +494,7 @@ * <p> * This returns the years unit. * <p> - * The months unit is not normalized with the years unit. + * The months unit is not automatically normalized with the years unit. * This means that a period of "15 months" is different to a period * of "1 year and 3 months". * @@ -483,7 +509,7 @@ * <p> * This returns the months unit. * <p> - * The months unit is not normalized with the years unit. + * The months unit is not automatically normalized with the years unit. * This means that a period of "15 months" is different to a period * of "1 year and 3 months". * @@ -511,7 +537,7 @@ * This sets the amount of the years unit in a copy of this period. * The months and days units are unaffected. * <p> - * The months unit is not normalized with the years unit. + * The months unit is not automatically normalized with the years unit. * This means that a period of "15 months" is different to a period * of "1 year and 3 months". * <p> @@ -533,7 +559,7 @@ * This sets the amount of the months unit in a copy of this period. * The years and days units are unaffected. * <p> - * The months unit is not normalized with the years unit. + * The months unit is not automatically normalized with the years unit. * This means that a period of "15 months" is different to a period * of "1 year and 3 months". * <p> @@ -572,21 +598,28 @@ * Returns a copy of this period with the specified period added. * <p> * This operates separately on the years, months and days. + * No normalization is performed. * <p> * For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days" * returns "3 years, 8 months and 5 days". * <p> + * The specified amount is typically an instance of {@code Period}. + * Other types are interpreted using {@link Period#from(TemporalAmount)}. + * <p> * This instance is immutable and unaffected by this method call. * * @param amountToAdd the period to add, not null * @return a {@code Period} based on this period with the requested period added, not null + * @throws DateTimeException if the specified amount has a non-ISO chronology or + * contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ - public Period plus(Period amountToAdd) { + public Period plus(TemporalAmount amountToAdd) { + Period isoAmount = Period.from(amountToAdd); return create( - Math.addExact(years, amountToAdd.years), - Math.addExact(months, amountToAdd.months), - Math.addExact(days, amountToAdd.days)); + Math.addExact(years, isoAmount.years), + Math.addExact(months, isoAmount.months), + Math.addExact(days, isoAmount.days)); } /** @@ -654,21 +687,28 @@ * Returns a copy of this period with the specified period subtracted. * <p> * This operates separately on the years, months and days. + * No normalization is performed. * <p> * For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days" * returns "-1 years, 4 months and 1 day". * <p> + * The specified amount is typically an instance of {@code Period}. + * Other types are interpreted using {@link Period#from(TemporalAmount)}. + * <p> * This instance is immutable and unaffected by this method call. * * @param amountToSubtract the period to subtract, not null * @return a {@code Period} based on this period with the requested period subtracted, not null + * @throws DateTimeException if the specified amount has a non-ISO chronology or + * contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ - public Period minus(Period amountToSubtract) { + public Period minus(TemporalAmount amountToSubtract) { + Period isoAmount = Period.from(amountToSubtract); return create( - Math.subtractExact(years, amountToSubtract.years), - Math.subtractExact(months, amountToSubtract.months), - Math.subtractExact(days, amountToSubtract.days)); + Math.subtractExact(years, isoAmount.years), + Math.subtractExact(months, isoAmount.months), + Math.subtractExact(days, isoAmount.days)); } /** @@ -766,8 +806,7 @@ //----------------------------------------------------------------------- /** - * Returns a copy of this period with the years and months normalized - * using a 12 month year. + * Returns a copy of this period with the years and months normalized. * <p> * This normalizes the years and months units, leaving the days unit unchanged. * The months unit is adjusted to have an absolute value less than 11, @@ -778,8 +817,6 @@ * For example, a period of "1 year and -25 months" will be normalized to * "-1 year and -1 month". * <p> - * This normalization uses a 12 month year which is not valid for all calendar systems. - * <p> * This instance is immutable and unaffected by this method call. * * @return a {@code Period} based on this period with excess months normalized to years, not null @@ -796,13 +833,11 @@ } /** - * Gets the total number of months in this period using a 12 month year. + * Gets the total number of months in this period. * <p> * This returns the total number of months in the period by multiplying the * number of years by 12 and adding the number of months. * <p> - * This uses a 12 month year which is not valid for all calendar systems. - * <p> * This instance is immutable and unaffected by this method call. * * @return the total number of months in the period, may be negative @@ -817,6 +852,7 @@ * <p> * This returns a temporal object of the same observable type as the input * with this period added. + * If the temporal has a chronology, it must be the ISO chronology. * <p> * In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. @@ -826,10 +862,17 @@ * dateTime = dateTime.plus(thisPeriod); * </pre> * <p> - * The calculation will add the years, then months, then days. - * Only non-zero amounts will be added. - * If the date-time has a calendar system with a fixed number of months in a - * year, then the years and months will be combined before being added. + * The calculation operates as follows. + * First, the chronology of the temporal is checked to ensure it is ISO chronology or null. + * Second, if the months are zero, the years are added if non-zero, otherwise + * the combination of years and months is added if non-zero. + * Finally, any days are added. + * <p> + * This approach ensures that a partial period can be added to a partial date. + * For example, a period of years and/or months can be added to a {@code YearMonth}, + * but a period including days cannot. + * The approach also adds years and months together when necessary, which ensures + * correct behaviour at the end of the month. * <p> * This instance is immutable and unaffected by this method call. * @@ -840,18 +883,15 @@ */ @Override public Temporal addTo(Temporal temporal) { - Objects.requireNonNull(temporal, "temporal"); - if ((years | months) != 0) { - long monthRange = monthRange(temporal); - if (monthRange >= 0) { - temporal = temporal.plus(years * monthRange + months, MONTHS); - } else { - if (years != 0) { - temporal = temporal.plus(years, YEARS); - } - if (months != 0) { - temporal = temporal.plus(months, MONTHS); - } + validateChrono(temporal); + if (months == 0) { + if (years != 0) { + temporal = temporal.plus(years, YEARS); + } + } else { + long totalMonths = toTotalMonths(); + if (totalMonths != 0) { + temporal = temporal.plus(totalMonths, MONTHS); } } if (days != 0) { @@ -865,6 +905,7 @@ * <p> * This returns a temporal object of the same observable type as the input * with this period subtracted. + * If the temporal has a chronology, it must be the ISO chronology. * <p> * In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. @@ -874,10 +915,17 @@ * dateTime = dateTime.minus(thisPeriod); * </pre> * <p> - * The calculation will subtract the years, then months, then days. - * Only non-zero amounts will be subtracted. - * If the date-time has a calendar system with a fixed number of months in a - * year, then the years and months will be combined before being subtracted. + * The calculation operates as follows. + * First, the chronology of the temporal is checked to ensure it is ISO chronology or null. + * Second, if the months are zero, the years are subtracted if non-zero, otherwise + * the combination of years and months is subtracted if non-zero. + * Finally, any days are subtracted. + * <p> + * This approach ensures that a partial period can be subtracted from a partial date. + * For example, a period of years and/or months can be subtracted from a {@code YearMonth}, + * but a period including days cannot. + * The approach also subtracts years and months together when necessary, which ensures + * correct behaviour at the end of the month. * <p> * This instance is immutable and unaffected by this method call. * @@ -888,18 +936,15 @@ */ @Override public Temporal subtractFrom(Temporal temporal) { - Objects.requireNonNull(temporal, "temporal"); - if ((years | months) != 0) { - long monthRange = monthRange(temporal); - if (monthRange >= 0) { - temporal = temporal.minus(years * monthRange + months, MONTHS); - } else { - if (years != 0) { - temporal = temporal.minus(years, YEARS); - } - if (months != 0) { - temporal = temporal.minus(months, MONTHS); - } + validateChrono(temporal); + if (months == 0) { + if (years != 0) { + temporal = temporal.minus(years, YEARS); + } + } else { + long totalMonths = toTotalMonths(); + if (totalMonths != 0) { + temporal = temporal.minus(totalMonths, MONTHS); } } if (days != 0) { @@ -909,26 +954,21 @@ } /** - * Calculates the range of months based on the temporal. - * - * @param temporal the temporal, not null - * @return the month range, negative if not fixed range + * Validates that the temporal has the correct chronology. */ - private long monthRange(Temporal temporal) { - if (temporal.isSupported(MONTH_OF_YEAR)) { - ValueRange startRange = Chronology.from(temporal).range(MONTH_OF_YEAR); - if (startRange.isFixed() && startRange.isIntValue()) { - return startRange.getMaximum() - startRange.getMinimum() + 1; - } + private void validateChrono(TemporalAccessor temporal) { + Objects.requireNonNull(temporal, "temporal"); + Chronology temporalChrono = temporal.query(TemporalQuery.chronology()); + if (temporalChrono != null && IsoChronology.INSTANCE.equals(temporalChrono) == false) { + throw new DateTimeException("Chronology mismatch, expected: ISO, actual: " + temporalChrono.getId()); } - return -1; } //----------------------------------------------------------------------- /** * Checks if this period is equal to another period. * <p> - * The comparison is based on the amounts held in the period. + * The comparison is based on the type {@code Period} and each of the three amounts. * To be equal, the years, months and days units must be individually equal. * Note that this means that a period of "15 Months" is not equal to a period * of "1 Year and 3 Months".
--- a/jdk/src/share/classes/java/time/Year.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/Year.java Tue Oct 08 14:57:32 2013 -0700 @@ -242,6 +242,7 @@ if (temporal instanceof Year) { return (Year) temporal; } + Objects.requireNonNull(temporal, "temporal"); try { if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { temporal = LocalDate.from(temporal); @@ -859,7 +860,8 @@ * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified year. * The result will be negative if the end is before the start. - * The {@code Temporal} passed to this method must be a {@code Year}. + * The {@code Temporal} passed to this method is converted to a + * {@code Year} using {@link #from(TemporalAccessor)}. * For example, the period in decades between two year can be calculated * using {@code startYear.until(endYear, DECADES)}. * <p> @@ -885,25 +887,22 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endYear the end year, which must be a {@code Year}, not null + * @param endExclusive the end date, exclusive, which is converted to a {@code Year}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this year and the end year - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code Year} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endYear, TemporalUnit unit) { - if (endYear instanceof Year == false) { - Objects.requireNonNull(endYear, "endYear"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - Year end = (Year) endYear; + public long until(Temporal endExclusive, TemporalUnit unit) { + Year end = Year.from(endExclusive); if (unit instanceof ChronoUnit) { long yearsUntil = ((long) end.year) - year; // no overflow switch ((ChronoUnit) unit) { @@ -915,7 +914,7 @@ } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return unit.between(this, endYear); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/YearMonth.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/YearMonth.java Tue Oct 08 14:57:32 2013 -0700 @@ -245,6 +245,7 @@ if (temporal instanceof YearMonth) { return (YearMonth) temporal; } + Objects.requireNonNull(temporal, "temporal"); try { if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { temporal = LocalDate.from(temporal); @@ -992,7 +993,8 @@ * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified year-month. * The result will be negative if the end is before the start. - * The {@code Temporal} passed to this method must be a {@code YearMonth}. + * The {@code Temporal} passed to this method is converted to a + * {@code YearMonth} using {@link #from(TemporalAccessor)}. * For example, the period in years between two year-months can be calculated * using {@code startYearMonth.until(endYearMonth, YEARS)}. * <p> @@ -1018,25 +1020,22 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endYearMonth the end year-month, which must be a {@code YearMonth}, not null + * @param endExclusive the end date, exclusive, which is converted to a {@code YearMonth}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this year-month and the end year-month - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code YearMonth} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endYearMonth, TemporalUnit unit) { - if (endYearMonth instanceof YearMonth == false) { - Objects.requireNonNull(endYearMonth, "endYearMonth"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - YearMonth end = (YearMonth) endYearMonth; + public long until(Temporal endExclusive, TemporalUnit unit) { + YearMonth end = YearMonth.from(endExclusive); if (unit instanceof ChronoUnit) { long monthsUntil = end.getProlepticMonth() - getProlepticMonth(); // no overflow switch ((ChronoUnit) unit) { @@ -1049,7 +1048,7 @@ } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return unit.between(this, endYearMonth); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/ZoneOffset.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/ZoneOffset.java Tue Oct 08 14:57:32 2013 -0700 @@ -333,6 +333,7 @@ * @throws DateTimeException if unable to convert to an {@code ZoneOffset} */ public static ZoneOffset from(TemporalAccessor temporal) { + Objects.requireNonNull(temporal, "temporal"); ZoneOffset offset = temporal.query(TemporalQuery.offset()); if (offset == null) { throw new DateTimeException("Unable to obtain ZoneOffset from TemporalAccessor: " + temporal.getClass());
--- a/jdk/src/share/classes/java/time/ZonedDateTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/ZonedDateTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -1540,6 +1540,11 @@ */ @Override public ZonedDateTime plus(TemporalAmount amountToAdd) { + if (amountToAdd instanceof Period) { + Period periodToAdd = (Period) amountToAdd; + return resolveLocal(dateTime.plus(periodToAdd)); + } + Objects.requireNonNull(amountToAdd, "amountToAdd"); return (ZonedDateTime) amountToAdd.addTo(this); } @@ -1787,6 +1792,11 @@ */ @Override public ZonedDateTime minus(TemporalAmount amountToSubtract) { + if (amountToSubtract instanceof Period) { + Period periodToSubtract = (Period) amountToSubtract; + return resolveLocal(dateTime.minus(periodToSubtract)); + } + Objects.requireNonNull(amountToSubtract, "amountToSubtract"); return (ZonedDateTime) amountToSubtract.subtractFrom(this); } @@ -2034,7 +2044,8 @@ * For example, the period in days between two date-times can be calculated * using {@code startDateTime.until(endDateTime, DAYS)}. * <p> - * The {@code Temporal} passed to this method must be a {@code ZonedDateTime}. + * The {@code Temporal} passed to this method is converted to a + * {@code ZonedDateTime} using {@link #from(TemporalAccessor)}. * If the time-zone differs between the two zoned date-times, the specified * end date-time is normalized to have the same zone as this date-time. * <p> @@ -2076,26 +2087,23 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as - * the second argument. + * passing {@code this} as the first argument and the converted input temporal + * as the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endDateTime the end date-time, which must be a {@code ZonedDateTime}, not null + * @param endExclusive the end date, exclusive, which is converted to a {@code ZonedDateTime}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this date-time and the end date-time - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code ZonedDateTime} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override - public long until(Temporal endDateTime, TemporalUnit unit) { - if (endDateTime instanceof ZonedDateTime == false) { - Objects.requireNonNull(endDateTime, "endDateTime"); - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } + public long until(Temporal endExclusive, TemporalUnit unit) { + ZonedDateTime end = ZonedDateTime.from(endExclusive); if (unit instanceof ChronoUnit) { - ZonedDateTime end = (ZonedDateTime) endDateTime; end = end.withZoneSameInstant(zone); if (unit.isDateBased()) { return dateTime.until(end.dateTime, unit); @@ -2103,7 +2111,7 @@ return toOffsetDateTime().until(end.toOffsetDateTime(), unit); } } - return unit.between(this, endDateTime); + return unit.between(this, end); } /**
--- a/jdk/src/share/classes/java/time/chrono/ChronoDateImpl.java Tue Oct 08 14:53:14 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,455 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of JSR-310 nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package java.time.chrono; - -import static java.time.temporal.ChronoField.DAY_OF_MONTH; -import static java.time.temporal.ChronoField.ERA; -import static java.time.temporal.ChronoField.MONTH_OF_YEAR; -import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; -import static java.time.temporal.ChronoField.YEAR_OF_ERA; - -import java.io.Serializable; -import java.time.DateTimeException; -import java.time.temporal.ChronoUnit; -import java.time.temporal.Temporal; -import java.time.temporal.TemporalAdjuster; -import java.time.temporal.TemporalAmount; -import java.time.temporal.TemporalField; -import java.time.temporal.TemporalUnit; -import java.time.temporal.UnsupportedTemporalTypeException; -import java.time.temporal.ValueRange; -import java.util.Objects; - -/** - * A date expressed in terms of a standard year-month-day calendar system. - * <p> - * This class is used by applications seeking to handle dates in non-ISO calendar systems. - * For example, the Japanese, Minguo, Thai Buddhist and others. - * <p> - * {@code ChronoLocalDate} is built on the generic concepts of year, month and day. - * The calendar system, represented by a {@link java.time.chrono.Chronology}, expresses the relationship between - * the fields and this class allows the resulting date to be manipulated. - * <p> - * Note that not all calendar systems are suitable for use with this class. - * For example, the Mayan calendar uses a system that bears no relation to years, months and days. - * <p> - * The API design encourages the use of {@code LocalDate} for the majority of the application. - * This includes code to read and write from a persistent data store, such as a database, - * and to send dates and times across a network. The {@code ChronoLocalDate} instance is then used - * at the user interface level to deal with localized input/output. - * - * <P>Example: </p> - * <pre> - * System.out.printf("Example()%n"); - * // Enumerate the list of available calendars and print today for each - * Set<Chronology> chronos = Chronology.getAvailableChronologies(); - * for (Chronology chrono : chronos) { - * ChronoLocalDate date = chrono.dateNow(); - * System.out.printf(" %20s: %s%n", chrono.getID(), date.toString()); - * } - * - * // Print the Hijrah date and calendar - * ChronoLocalDate date = Chronology.of("Hijrah").dateNow(); - * int day = date.get(ChronoField.DAY_OF_MONTH); - * int dow = date.get(ChronoField.DAY_OF_WEEK); - * int month = date.get(ChronoField.MONTH_OF_YEAR); - * int year = date.get(ChronoField.YEAR); - * System.out.printf(" Today is %s %s %d-%s-%d%n", date.getChronology().getID(), - * dow, day, month, year); - - * // Print today's date and the last day of the year - * ChronoLocalDate now1 = Chronology.of("Hijrah").dateNow(); - * ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1) - * .with(ChronoField.MONTH_OF_YEAR, 1); - * ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS) - * .minus(1, ChronoUnit.DAYS); - * System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(), - * first, last); - * </pre> - * - * <h3>Adding Calendars</h3> - * <p> The set of calendars is extensible by defining a subclass of {@link ChronoLocalDate} - * to represent a date instance and an implementation of {@code Chronology} - * to be the factory for the ChronoLocalDate subclass. - * </p> - * <p> To permit the discovery of the additional calendar types the implementation of - * {@code Chronology} must be registered as a Service implementing the {@code Chronology} interface - * in the {@code META-INF/Services} file as per the specification of {@link java.util.ServiceLoader}. - * The subclass must function according to the {@code Chronology} class description and must provide its - * {@link java.time.chrono.Chronology#getId() chronlogy ID} and {@link Chronology#getCalendarType() calendar type}. </p> - * - * @implSpec - * This abstract class must be implemented with care to ensure other classes operate correctly. - * All implementations that can be instantiated must be final, immutable and thread-safe. - * Subclasses should be Serializable wherever possible. - * - * @param <D> the ChronoLocalDate of this date-time - * @since 1.8 - */ -abstract class ChronoDateImpl<D extends ChronoLocalDate> - implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable { - - /** - * Serialization version. - */ - private static final long serialVersionUID = 6282433883239719096L; - - /** - * Casts the {@code Temporal} to {@code ChronoLocalDate} ensuring it bas the specified chronology. - * - * @param chrono the chronology to check for, not null - * @param temporal a date-time to cast, not null - * @return the date-time checked and cast to {@code ChronoLocalDate}, not null - * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate - * or the chronology is not equal this Chronology - */ - static <D extends ChronoLocalDate> D ensureValid(Chronology chrono, Temporal temporal) { - @SuppressWarnings("unchecked") - D other = (D) temporal; - if (chrono.equals(other.getChronology()) == false) { - throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + other.getChronology().getId()); - } - return other; - } - - //----------------------------------------------------------------------- - /** - * Creates an instance. - */ - ChronoDateImpl() { - } - - @Override - @SuppressWarnings("unchecked") - public D with(TemporalAdjuster adjuster) { - return (D) ChronoLocalDate.super.with(adjuster); - } - - @Override - @SuppressWarnings("unchecked") - public D with(TemporalField field, long value) { - return (D) ChronoLocalDate.super.with(field, value); - } - - //----------------------------------------------------------------------- - @Override - @SuppressWarnings("unchecked") - public D plus(TemporalAmount amount) { - return (D) ChronoLocalDate.super.plus(amount); - } - - //----------------------------------------------------------------------- - @Override - @SuppressWarnings("unchecked") - public D plus(long amountToAdd, TemporalUnit unit) { - if (unit instanceof ChronoUnit) { - ChronoUnit f = (ChronoUnit) unit; - switch (f) { - case DAYS: return plusDays(amountToAdd); - case WEEKS: return plusDays(Math.multiplyExact(amountToAdd, 7)); - case MONTHS: return plusMonths(amountToAdd); - case YEARS: return plusYears(amountToAdd); - case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); - case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); - case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); - case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); - } - throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); - } - return (D) ChronoLocalDate.super.plus(amountToAdd, unit); - } - - @Override - @SuppressWarnings("unchecked") - public D minus(TemporalAmount amount) { - return (D) ChronoLocalDate.super.minus(amount); - } - - @Override - @SuppressWarnings("unchecked") - public D minus(long amountToSubtract, TemporalUnit unit) { - return (D) ChronoLocalDate.super.minus(amountToSubtract, unit); - } - - //----------------------------------------------------------------------- - /** - * Returns a copy of this date with the specified period in years added. - * <p> - * This adds the specified period in years to the date. - * In some cases, adding years can cause the resulting date to become invalid. - * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure - * that the result is valid. Typically this will select the last valid day of the month. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param yearsToAdd the years to add, may be negative - * @return a date based on this one with the years added, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - abstract D plusYears(long yearsToAdd); - - /** - * Returns a copy of this date with the specified period in months added. - * <p> - * This adds the specified period in months to the date. - * In some cases, adding months can cause the resulting date to become invalid. - * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure - * that the result is valid. Typically this will select the last valid day of the month. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param monthsToAdd the months to add, may be negative - * @return a date based on this one with the months added, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - abstract D plusMonths(long monthsToAdd); - - /** - * Returns a copy of this date with the specified period in weeks added. - * <p> - * This adds the specified period in weeks to the date. - * In some cases, adding weeks can cause the resulting date to become invalid. - * If this occurs, then other fields will be adjusted to ensure that the result is valid. - * <p> - * The default implementation uses {@link #plusDays(long)} using a 7 day week. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param weeksToAdd the weeks to add, may be negative - * @return a date based on this one with the weeks added, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - D plusWeeks(long weeksToAdd) { - return plusDays(Math.multiplyExact(weeksToAdd, 7)); - } - - /** - * Returns a copy of this date with the specified number of days added. - * <p> - * This adds the specified period in days to the date. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param daysToAdd the days to add, may be negative - * @return a date based on this one with the days added, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - abstract D plusDays(long daysToAdd); - - //----------------------------------------------------------------------- - /** - * Returns a copy of this date with the specified period in years subtracted. - * <p> - * This subtracts the specified period in years to the date. - * In some cases, subtracting years can cause the resulting date to become invalid. - * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure - * that the result is valid. Typically this will select the last valid day of the month. - * <p> - * The default implementation uses {@link #plusYears(long)}. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param yearsToSubtract the years to subtract, may be negative - * @return a date based on this one with the years subtracted, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - @SuppressWarnings("unchecked") - D minusYears(long yearsToSubtract) { - return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract)); - } - - /** - * Returns a copy of this date with the specified period in months subtracted. - * <p> - * This subtracts the specified period in months to the date. - * In some cases, subtracting months can cause the resulting date to become invalid. - * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure - * that the result is valid. Typically this will select the last valid day of the month. - * <p> - * The default implementation uses {@link #plusMonths(long)}. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param monthsToSubtract the months to subtract, may be negative - * @return a date based on this one with the months subtracted, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - @SuppressWarnings("unchecked") - D minusMonths(long monthsToSubtract) { - return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract)); - } - - /** - * Returns a copy of this date with the specified period in weeks subtracted. - * <p> - * This subtracts the specified period in weeks to the date. - * In some cases, subtracting weeks can cause the resulting date to become invalid. - * If this occurs, then other fields will be adjusted to ensure that the result is valid. - * <p> - * The default implementation uses {@link #plusWeeks(long)}. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param weeksToSubtract the weeks to subtract, may be negative - * @return a date based on this one with the weeks subtracted, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - @SuppressWarnings("unchecked") - D minusWeeks(long weeksToSubtract) { - return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract)); - } - - /** - * Returns a copy of this date with the specified number of days subtracted. - * <p> - * This subtracts the specified period in days to the date. - * <p> - * The default implementation uses {@link #plusDays(long)}. - * <p> - * This instance is immutable and unaffected by this method call. - * - * @param daysToSubtract the days to subtract, may be negative - * @return a date based on this one with the days subtracted, not null - * @throws DateTimeException if the result exceeds the supported date range - */ - @SuppressWarnings("unchecked") - D minusDays(long daysToSubtract) { - return (daysToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract)); - } - - //----------------------------------------------------------------------- - /** - * {@inheritDoc} - * @throws DateTimeException {@inheritDoc} - * @throws ArithmeticException {@inheritDoc} - */ - @Override - public long until(Temporal endDateTime, TemporalUnit unit) { - Objects.requireNonNull(endDateTime, "endDateTime"); - Objects.requireNonNull(unit, "unit"); - if (endDateTime instanceof ChronoLocalDate == false) { - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } - ChronoLocalDate end = (ChronoLocalDate) endDateTime; - if (getChronology().equals(end.getChronology()) == false) { - throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); - } - if (unit instanceof ChronoUnit) { - switch ((ChronoUnit) unit) { - case DAYS: return daysUntil(end); - case WEEKS: return daysUntil(end) / 7; - case MONTHS: return monthsUntil(end); - case YEARS: return monthsUntil(end) / 12; - case DECADES: return monthsUntil(end) / 120; - case CENTURIES: return monthsUntil(end) / 1200; - case MILLENNIA: return monthsUntil(end) / 12000; - case ERAS: return end.getLong(ERA) - getLong(ERA); - } - throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); - } - return unit.between(this, endDateTime); - } - - private long daysUntil(ChronoLocalDate end) { - return end.toEpochDay() - toEpochDay(); // no overflow - } - - private long monthsUntil(ChronoLocalDate end) { - ValueRange range = getChronology().range(MONTH_OF_YEAR); - if (range.getMaximum() != 12) { - throw new IllegalStateException("ChronoDateImpl only supports Chronologies with 12 months per year"); - } - long packed1 = getLong(PROLEPTIC_MONTH) * 32L + get(DAY_OF_MONTH); // no overflow - long packed2 = end.getLong(PROLEPTIC_MONTH) * 32L + end.get(DAY_OF_MONTH); // no overflow - return (packed2 - packed1) / 32; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof ChronoLocalDate) { - return compareTo((ChronoLocalDate) obj) == 0; - } - return false; - } - - @Override - public int hashCode() { - long epDay = toEpochDay(); - return getChronology().hashCode() ^ ((int) (epDay ^ (epDay >>> 32))); - } - - @Override - public String toString() { - // getLong() reduces chances of exceptions in toString() - long yoe = getLong(YEAR_OF_ERA); - long moy = getLong(MONTH_OF_YEAR); - long dom = getLong(DAY_OF_MONTH); - StringBuilder buf = new StringBuilder(30); - buf.append(getChronology().toString()) - .append(" ") - .append(getEra()) - .append(" ") - .append(yoe) - .append(moy < 10 ? "-0" : "-").append(moy) - .append(dom < 10 ? "-0" : "-").append(dom); - return buf.toString(); - } - -}
--- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java Tue Oct 08 14:57:32 2013 -0700 @@ -69,7 +69,6 @@ import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; -import java.time.Period; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; @@ -292,6 +291,7 @@ if (temporal instanceof ChronoLocalDate) { return (ChronoLocalDate) temporal; } + Objects.requireNonNull(temporal, "temporal"); Chronology chrono = temporal.query(TemporalQuery.chronology()); if (chrono == null) { throw new DateTimeException("Unable to obtain ChronoLocalDate from TemporalAccessor: " + temporal.getClass()); @@ -428,7 +428,7 @@ */ @Override default ChronoLocalDate with(TemporalAdjuster adjuster) { - return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.with(adjuster)); + return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.with(adjuster)); } /** @@ -442,7 +442,7 @@ if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } - return ChronoDateImpl.ensureValid(getChronology(), field.adjustInto(this, newValue)); + return ChronoLocalDateImpl.ensureValid(getChronology(), field.adjustInto(this, newValue)); } /** @@ -452,7 +452,7 @@ */ @Override default ChronoLocalDate plus(TemporalAmount amount) { - return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.plus(amount)); + return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.plus(amount)); } /** @@ -465,7 +465,7 @@ if (unit instanceof ChronoUnit) { throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } - return ChronoDateImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd)); + return ChronoLocalDateImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd)); } /** @@ -475,7 +475,7 @@ */ @Override default ChronoLocalDate minus(TemporalAmount amount) { - return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.minus(amount)); + return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.minus(amount)); } /** @@ -486,7 +486,7 @@ */ @Override default ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) { - return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit)); + return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit)); } //----------------------------------------------------------------------- @@ -561,8 +561,8 @@ * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date. * The result will be negative if the end is before the start. - * The {@code Temporal} passed to this method must be a - * {@code ChronoLocalDate} in the same chronology. + * The {@code Temporal} passed to this method is converted to a + * {@code ChronoLocalDate} using {@link Chronology#date(TemporalAccessor)}. * The calculation returns a whole number, representing the number of * complete units between the two dates. * For example, the amount in days between two dates can be calculated @@ -586,25 +586,30 @@ * <p> * If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} - * passing {@code this} as the first argument and the input temporal as + * passing {@code this} as the first argument and the converted input temporal as * the second argument. * <p> * This instance is immutable and unaffected by this method call. * - * @param endDate the end date, which must be a {@code ChronoLocalDate} - * in the same chronology, not null + * @param endExclusive the end date, exclusive, which is converted to a + * {@code ChronoLocalDate} in the same chronology, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this date and the end date - * @throws DateTimeException if the amount cannot be calculated + * @throws DateTimeException if the amount cannot be calculated, or the end + * temporal cannot be converted to a {@code ChronoLocalDate} + * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override // override for Javadoc - long until(Temporal endDate, TemporalUnit unit); + long until(Temporal endExclusive, TemporalUnit unit); /** - * Calculates the period between this date and another date as a {@code Period}. + * Calculates the period between this date and another date as a {@code ChronoPeriod}. * <p> - * This calculates the period between two dates in terms of years, months and days. + * This calculates the period between two dates. All supplied chronologies + * calculate the period using years, months and days, however the + * {@code ChronoPeriod} API allows the period to be represented using other units. + * <p> * The start and end points are {@code this} and the specified date. * The result will be negative if the end is before the start. * The negative sign will be the same in each of year, month and day. @@ -614,12 +619,12 @@ * <p> * This instance is immutable and unaffected by this method call. * - * @param endDate the end date, exclusive, which may be in any chronology, not null + * @param endDateExclusive the end date, exclusive, which may be in any chronology, not null * @return the period between this date and the end date, not null * @throws DateTimeException if the period cannot be calculated * @throws ArithmeticException if numeric overflow occurs */ - Period until(ChronoLocalDate endDate); + ChronoPeriod until(ChronoLocalDate endDateExclusive); /** * Formats this date using the specified formatter.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateImpl.java Tue Oct 08 14:57:32 2013 -0700 @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.chrono; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; + +import java.io.Serializable; +import java.time.DateTimeException; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalUnit; +import java.time.temporal.UnsupportedTemporalTypeException; +import java.time.temporal.ValueRange; +import java.util.Objects; + +/** + * A date expressed in terms of a standard year-month-day calendar system. + * <p> + * This class is used by applications seeking to handle dates in non-ISO calendar systems. + * For example, the Japanese, Minguo, Thai Buddhist and others. + * <p> + * {@code ChronoLocalDate} is built on the generic concepts of year, month and day. + * The calendar system, represented by a {@link java.time.chrono.Chronology}, expresses the relationship between + * the fields and this class allows the resulting date to be manipulated. + * <p> + * Note that not all calendar systems are suitable for use with this class. + * For example, the Mayan calendar uses a system that bears no relation to years, months and days. + * <p> + * The API design encourages the use of {@code LocalDate} for the majority of the application. + * This includes code to read and write from a persistent data store, such as a database, + * and to send dates and times across a network. The {@code ChronoLocalDate} instance is then used + * at the user interface level to deal with localized input/output. + * + * <P>Example: </p> + * <pre> + * System.out.printf("Example()%n"); + * // Enumerate the list of available calendars and print today for each + * Set<Chronology> chronos = Chronology.getAvailableChronologies(); + * for (Chronology chrono : chronos) { + * ChronoLocalDate date = chrono.dateNow(); + * System.out.printf(" %20s: %s%n", chrono.getID(), date.toString()); + * } + * + * // Print the Hijrah date and calendar + * ChronoLocalDate date = Chronology.of("Hijrah").dateNow(); + * int day = date.get(ChronoField.DAY_OF_MONTH); + * int dow = date.get(ChronoField.DAY_OF_WEEK); + * int month = date.get(ChronoField.MONTH_OF_YEAR); + * int year = date.get(ChronoField.YEAR); + * System.out.printf(" Today is %s %s %d-%s-%d%n", date.getChronology().getID(), + * dow, day, month, year); + + * // Print today's date and the last day of the year + * ChronoLocalDate now1 = Chronology.of("Hijrah").dateNow(); + * ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1) + * .with(ChronoField.MONTH_OF_YEAR, 1); + * ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS) + * .minus(1, ChronoUnit.DAYS); + * System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(), + * first, last); + * </pre> + * + * <h3>Adding Calendars</h3> + * <p> The set of calendars is extensible by defining a subclass of {@link ChronoLocalDate} + * to represent a date instance and an implementation of {@code Chronology} + * to be the factory for the ChronoLocalDate subclass. + * </p> + * <p> To permit the discovery of the additional calendar types the implementation of + * {@code Chronology} must be registered as a Service implementing the {@code Chronology} interface + * in the {@code META-INF/Services} file as per the specification of {@link java.util.ServiceLoader}. + * The subclass must function according to the {@code Chronology} class description and must provide its + * {@link java.time.chrono.Chronology#getId() chronlogy ID} and {@link Chronology#getCalendarType() calendar type}. </p> + * + * @implSpec + * This abstract class must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * Subclasses should be Serializable wherever possible. + * + * @param <D> the ChronoLocalDate of this date-time + * @since 1.8 + */ +abstract class ChronoLocalDateImpl<D extends ChronoLocalDate> + implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = 6282433883239719096L; + + /** + * Casts the {@code Temporal} to {@code ChronoLocalDate} ensuring it bas the specified chronology. + * + * @param chrono the chronology to check for, not null + * @param temporal a date-time to cast, not null + * @return the date-time checked and cast to {@code ChronoLocalDate}, not null + * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate + * or the chronology is not equal this Chronology + */ + static <D extends ChronoLocalDate> D ensureValid(Chronology chrono, Temporal temporal) { + @SuppressWarnings("unchecked") + D other = (D) temporal; + if (chrono.equals(other.getChronology()) == false) { + throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + other.getChronology().getId()); + } + return other; + } + + //----------------------------------------------------------------------- + /** + * Creates an instance. + */ + ChronoLocalDateImpl() { + } + + @Override + @SuppressWarnings("unchecked") + public D with(TemporalAdjuster adjuster) { + return (D) ChronoLocalDate.super.with(adjuster); + } + + @Override + @SuppressWarnings("unchecked") + public D with(TemporalField field, long value) { + return (D) ChronoLocalDate.super.with(field, value); + } + + //----------------------------------------------------------------------- + @Override + @SuppressWarnings("unchecked") + public D plus(TemporalAmount amount) { + return (D) ChronoLocalDate.super.plus(amount); + } + + //----------------------------------------------------------------------- + @Override + @SuppressWarnings("unchecked") + public D plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + ChronoUnit f = (ChronoUnit) unit; + switch (f) { + case DAYS: return plusDays(amountToAdd); + case WEEKS: return plusDays(Math.multiplyExact(amountToAdd, 7)); + case MONTHS: return plusMonths(amountToAdd); + case YEARS: return plusYears(amountToAdd); + case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); + case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); + case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); + case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); + } + throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); + } + return (D) ChronoLocalDate.super.plus(amountToAdd, unit); + } + + @Override + @SuppressWarnings("unchecked") + public D minus(TemporalAmount amount) { + return (D) ChronoLocalDate.super.minus(amount); + } + + @Override + @SuppressWarnings("unchecked") + public D minus(long amountToSubtract, TemporalUnit unit) { + return (D) ChronoLocalDate.super.minus(amountToSubtract, unit); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date with the specified period in years added. + * <p> + * This adds the specified period in years to the date. + * In some cases, adding years can cause the resulting date to become invalid. + * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure + * that the result is valid. Typically this will select the last valid day of the month. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param yearsToAdd the years to add, may be negative + * @return a date based on this one with the years added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + abstract D plusYears(long yearsToAdd); + + /** + * Returns a copy of this date with the specified period in months added. + * <p> + * This adds the specified period in months to the date. + * In some cases, adding months can cause the resulting date to become invalid. + * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure + * that the result is valid. Typically this will select the last valid day of the month. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param monthsToAdd the months to add, may be negative + * @return a date based on this one with the months added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + abstract D plusMonths(long monthsToAdd); + + /** + * Returns a copy of this date with the specified period in weeks added. + * <p> + * This adds the specified period in weeks to the date. + * In some cases, adding weeks can cause the resulting date to become invalid. + * If this occurs, then other fields will be adjusted to ensure that the result is valid. + * <p> + * The default implementation uses {@link #plusDays(long)} using a 7 day week. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param weeksToAdd the weeks to add, may be negative + * @return a date based on this one with the weeks added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + D plusWeeks(long weeksToAdd) { + return plusDays(Math.multiplyExact(weeksToAdd, 7)); + } + + /** + * Returns a copy of this date with the specified number of days added. + * <p> + * This adds the specified period in days to the date. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param daysToAdd the days to add, may be negative + * @return a date based on this one with the days added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + abstract D plusDays(long daysToAdd); + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date with the specified period in years subtracted. + * <p> + * This subtracts the specified period in years to the date. + * In some cases, subtracting years can cause the resulting date to become invalid. + * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure + * that the result is valid. Typically this will select the last valid day of the month. + * <p> + * The default implementation uses {@link #plusYears(long)}. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param yearsToSubtract the years to subtract, may be negative + * @return a date based on this one with the years subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + @SuppressWarnings("unchecked") + D minusYears(long yearsToSubtract) { + return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract)); + } + + /** + * Returns a copy of this date with the specified period in months subtracted. + * <p> + * This subtracts the specified period in months to the date. + * In some cases, subtracting months can cause the resulting date to become invalid. + * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure + * that the result is valid. Typically this will select the last valid day of the month. + * <p> + * The default implementation uses {@link #plusMonths(long)}. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param monthsToSubtract the months to subtract, may be negative + * @return a date based on this one with the months subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + @SuppressWarnings("unchecked") + D minusMonths(long monthsToSubtract) { + return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract)); + } + + /** + * Returns a copy of this date with the specified period in weeks subtracted. + * <p> + * This subtracts the specified period in weeks to the date. + * In some cases, subtracting weeks can cause the resulting date to become invalid. + * If this occurs, then other fields will be adjusted to ensure that the result is valid. + * <p> + * The default implementation uses {@link #plusWeeks(long)}. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param weeksToSubtract the weeks to subtract, may be negative + * @return a date based on this one with the weeks subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + @SuppressWarnings("unchecked") + D minusWeeks(long weeksToSubtract) { + return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract)); + } + + /** + * Returns a copy of this date with the specified number of days subtracted. + * <p> + * This subtracts the specified period in days to the date. + * <p> + * The default implementation uses {@link #plusDays(long)}. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param daysToSubtract the days to subtract, may be negative + * @return a date based on this one with the days subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + @SuppressWarnings("unchecked") + D minusDays(long daysToSubtract) { + return (daysToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract)); + } + + //----------------------------------------------------------------------- + @Override + public long until(Temporal endExclusive, TemporalUnit unit) { + Objects.requireNonNull(endExclusive, "endExclusive"); + ChronoLocalDate end = getChronology().date(endExclusive); + if (unit instanceof ChronoUnit) { + switch ((ChronoUnit) unit) { + case DAYS: return daysUntil(end); + case WEEKS: return daysUntil(end) / 7; + case MONTHS: return monthsUntil(end); + case YEARS: return monthsUntil(end) / 12; + case DECADES: return monthsUntil(end) / 120; + case CENTURIES: return monthsUntil(end) / 1200; + case MILLENNIA: return monthsUntil(end) / 12000; + case ERAS: return end.getLong(ERA) - getLong(ERA); + } + throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); + } + Objects.requireNonNull(unit, "unit"); + return unit.between(this, end); + } + + private long daysUntil(ChronoLocalDate end) { + return end.toEpochDay() - toEpochDay(); // no overflow + } + + private long monthsUntil(ChronoLocalDate end) { + ValueRange range = getChronology().range(MONTH_OF_YEAR); + if (range.getMaximum() != 12) { + throw new IllegalStateException("ChronoLocalDateImpl only supports Chronologies with 12 months per year"); + } + long packed1 = getLong(PROLEPTIC_MONTH) * 32L + get(DAY_OF_MONTH); // no overflow + long packed2 = end.getLong(PROLEPTIC_MONTH) * 32L + end.get(DAY_OF_MONTH); // no overflow + return (packed2 - packed1) / 32; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ChronoLocalDate) { + return compareTo((ChronoLocalDate) obj) == 0; + } + return false; + } + + @Override + public int hashCode() { + long epDay = toEpochDay(); + return getChronology().hashCode() ^ ((int) (epDay ^ (epDay >>> 32))); + } + + @Override + public String toString() { + // getLong() reduces chances of exceptions in toString() + long yoe = getLong(YEAR_OF_ERA); + long moy = getLong(MONTH_OF_YEAR); + long dom = getLong(DAY_OF_MONTH); + StringBuilder buf = new StringBuilder(30); + buf.append(getChronology().toString()) + .append(" ") + .append(getEra()) + .append(" ") + .append(yoe) + .append(moy < 10 ? "-0" : "-").append(moy) + .append(dom < 10 ? "-0" : "-").append(dom); + return buf.toString(); + } + +}
--- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -165,6 +165,7 @@ if (temporal instanceof ChronoLocalDateTime) { return (ChronoLocalDateTime<?>) temporal; } + Objects.requireNonNull(temporal, "temporal"); Chronology chrono = temporal.query(TemporalQuery.chronology()); if (chrono == null) { throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass()); @@ -174,6 +175,18 @@ //----------------------------------------------------------------------- /** + * Gets the chronology of this date-time. + * <p> + * The {@code Chronology} represents the calendar system in use. + * The era and other fields in {@link ChronoField} are defined by the chronology. + * + * @return the chronology, not null + */ + default Chronology getChronology() { + return toLocalDate().getChronology(); + } + + /** * Gets the local date part of this date-time. * <p> * This returns a local date with the same year, month and day @@ -250,7 +263,7 @@ */ @Override default ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) { - return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.with(adjuster)); + return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster)); } /** @@ -268,7 +281,7 @@ */ @Override default ChronoLocalDateTime<D> plus(TemporalAmount amount) { - return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.plus(amount)); + return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount)); } /** @@ -286,7 +299,7 @@ */ @Override default ChronoLocalDateTime<D> minus(TemporalAmount amount) { - return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amount)); + return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount)); } /** @@ -296,7 +309,7 @@ */ @Override default ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { - return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amountToSubtract, unit)); + return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit)); } //----------------------------------------------------------------------- @@ -326,7 +339,7 @@ } else if (query == TemporalQuery.localTime()) { return (R) toLocalTime(); } else if (query == TemporalQuery.chronology()) { - return (R) toLocalDate().getChronology(); + return (R) getChronology(); } else if (query == TemporalQuery.precision()) { return (R) NANOS; } @@ -488,7 +501,7 @@ if (cmp == 0) { cmp = toLocalTime().compareTo(other.toLocalTime()); if (cmp == 0) { - cmp = toLocalDate().getChronology().compareTo(other.toLocalDate().getChronology()); + cmp = getChronology().compareTo(other.getChronology()); } } return cmp;
--- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java Tue Oct 08 14:57:32 2013 -0700 @@ -69,7 +69,6 @@ import java.io.ObjectOutput; import java.io.ObjectStreamException; import java.io.Serializable; -import java.time.DateTimeException; import java.time.LocalTime; import java.time.ZoneId; import java.time.temporal.ChronoField; @@ -187,9 +186,9 @@ static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) { @SuppressWarnings("unchecked") ChronoLocalDateTimeImpl<R> other = (ChronoLocalDateTimeImpl<R>) temporal; - if (chrono.equals(other.toLocalDate().getChronology()) == false) { + if (chrono.equals(other.getChronology()) == false) { throw new ClassCastException("Chronology mismatch, required: " + chrono.getId() - + ", actual: " + other.toLocalDate().getChronology().getId()); + + ", actual: " + other.getChronology().getId()); } return other; } @@ -220,7 +219,7 @@ return this; } // Validate that the new Temporal is a ChronoLocalDate (and not something else) - D cd = ChronoDateImpl.ensureValid(date.getChronology(), newDate); + D cd = ChronoLocalDateImpl.ensureValid(date.getChronology(), newDate); return new ChronoLocalDateTimeImpl<>(cd, newTime); } @@ -369,15 +368,10 @@ //----------------------------------------------------------------------- @Override - public long until(Temporal endDateTime, TemporalUnit unit) { - if (endDateTime instanceof ChronoLocalDateTime == false) { - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } + public long until(Temporal endExclusive, TemporalUnit unit) { + Objects.requireNonNull(endExclusive, "endExclusive"); @SuppressWarnings("unchecked") - ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) endDateTime; - if (toLocalDate().getChronology().equals(end.toLocalDate().getChronology()) == false) { - throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); - } + ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) getChronology().localDateTime(endExclusive); if (unit instanceof ChronoUnit) { if (unit.isTimeBased()) { long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY); @@ -398,7 +392,8 @@ } return date.until(endDate, unit); } - return unit.between(this, endDateTime); + Objects.requireNonNull(unit, "unit"); + return unit.between(this, end); } //-----------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/time/chrono/ChronoPeriod.java Tue Oct 08 14:57:32 2013 -0700 @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.chrono; + +import java.time.DateTimeException; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalUnit; +import java.time.temporal.UnsupportedTemporalTypeException; +import java.util.List; +import java.util.Objects; + +/** + * A date-based amount of time, such as '3 years, 4 months and 5 days' in an + * arbitrary chronology, intended for advanced globalization use cases. + * <p> + * This interface models a date-based amount of time in a calendar system. + * While most calendar systems use years, months and days, some do not. + * Therefore, this interface operates solely in terms of a set of supported + * units that are defined by the {@code Chronology}. + * The set of supported units is fixed for a given chronology. + * The amount of a supported unit may be set to zero. + * <p> + * The period is modeled as a directed amount of time, meaning that individual + * parts of the period may be negative. + * + * @implSpec + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * Subclasses should be Serializable wherever possible. + * + * @since 1.8 + */ +public interface ChronoPeriod + extends TemporalAmount { + + /** + * Obtains a {@code ChronoPeriod} consisting of amount of time between two dates. + * <p> + * The start date is included, but the end date is not. + * The period is calculated using {@link ChronoLocalDate#until(ChronoLocalDate)}. + * As such, the calculation is chronology specific. + * <p> + * The chronology of the first date is used. + * The chronology of the second date is ignored, with the date being converted + * to the target chronology system before the calculation starts. + * <p> + * The result of this method can be a negative period if the end is before the start. + * In most cases, the positive/negative sign will be the same in each of the supported fields. + * + * @param startDateInclusive the start date, inclusive, specifying the chronology of the calculation, not null + * @param endDateExclusive the end date, exclusive, in any chronology, not null + * @return the period between this date and the end date, not null + * @see ChronoLocalDate#until(ChronoLocalDate) + */ + public static ChronoPeriod between(ChronoLocalDate startDateInclusive, ChronoLocalDate endDateExclusive) { + Objects.requireNonNull(startDateInclusive, "startDateInclusive"); + Objects.requireNonNull(endDateExclusive, "endDateExclusive"); + return startDateInclusive.until(endDateExclusive); + } + + //----------------------------------------------------------------------- + /** + * Gets the value of the requested unit. + * <p> + * The supported units are chronology specific. + * They will typically be {@link ChronoUnit#YEARS YEARS}, + * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}. + * Requesting an unsupported unit will throw an exception. + * + * @param unit the {@code TemporalUnit} for which to return the value + * @return the long value of the unit + * @throws DateTimeException if the unit is not supported + * @throws UnsupportedTemporalTypeException if the unit is not supported + */ + @Override + long get(TemporalUnit unit); + + /** + * Gets the set of units supported by this period. + * <p> + * The supported units are chronology specific. + * They will typically be {@link ChronoUnit#YEARS YEARS}, + * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}. + * They are returned in order from largest to smallest. + * <p> + * This set can be used in conjunction with {@link #get(TemporalUnit)} + * to access the entire state of the period. + * + * @return a list containing the supported units, not null + */ + @Override + List<TemporalUnit> getUnits(); + + /** + * Gets the chronology that defines the meaning of the supported units. + * <p> + * The period is defined by the chronology. + * It controls the supported units and restricts addition/subtraction + * to {@code ChronoLocalDate} instances of the same chronology. + * + * @return the chronology defining the period, not null + */ + Chronology getChronology(); + + //----------------------------------------------------------------------- + /** + * Checks if all the supported units of this period are zero. + * + * @return true if this period is zero-length + */ + default boolean isZero() { + for (TemporalUnit unit : getUnits()) { + if (get(unit) != 0) { + return false; + } + } + return true; + } + + /** + * Checks if any of the supported units of this period are negative. + * + * @return true if any unit of this period is negative + */ + default boolean isNegative() { + for (TemporalUnit unit : getUnits()) { + if (get(unit) < 0) { + return true; + } + } + return false; + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this period with the specified period added. + * <p> + * If the specified amount is a {@code ChronoPeriod} then it must have + * the same chronology as this period. Implementations may choose to + * accept or reject other {@code TemporalAmount} implementations. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param amountToAdd the period to add, not null + * @return a {@code ChronoPeriod} based on this period with the requested period added, not null + * @throws ArithmeticException if numeric overflow occurs + */ + ChronoPeriod plus(TemporalAmount amountToAdd); + + /** + * Returns a copy of this period with the specified period subtracted. + * <p> + * If the specified amount is a {@code ChronoPeriod} then it must have + * the same chronology as this period. Implementations may choose to + * accept or reject other {@code TemporalAmount} implementations. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param amountToSubtract the period to subtract, not null + * @return a {@code ChronoPeriod} based on this period with the requested period subtracted, not null + * @throws ArithmeticException if numeric overflow occurs + */ + ChronoPeriod minus(TemporalAmount amountToSubtract); + + //----------------------------------------------------------------------- + /** + * Returns a new instance with each amount in this period in this period + * multiplied by the specified scalar. + * <p> + * This returns a period with each supported unit individually multiplied. + * For example, a period of "2 years, -3 months and 4 days" multiplied by + * 3 will return "6 years, -9 months and 12 days". + * No normalization is performed. + * + * @param scalar the scalar to multiply by, not null + * @return a {@code ChronoPeriod} based on this period with the amounts multiplied + * by the scalar, not null + * @throws ArithmeticException if numeric overflow occurs + */ + ChronoPeriod multipliedBy(int scalar); + + /** + * Returns a new instance with each amount in this period negated. + * <p> + * This returns a period with each supported unit individually negated. + * For example, a period of "2 years, -3 months and 4 days" will be + * negated to "-2 years, 3 months and -4 days". + * No normalization is performed. + * + * @return a {@code ChronoPeriod} based on this period with the amounts negated, not null + * @throws ArithmeticException if numeric overflow occurs, which only happens if + * one of the units has the value {@code Long.MIN_VALUE} + */ + default ChronoPeriod negated() { + return multipliedBy(-1); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this period with the amounts of each unit normalized. + * <p> + * The process of normalization is specific to each calendar system. + * For example, in the ISO calendar system, the years and months are + * normalized but the days are not, such that "15 months" would be + * normalized to "1 year and 3 months". + * <p> + * This instance is immutable and unaffected by this method call. + * + * @return a {@code ChronoPeriod} based on this period with the amounts of each + * unit normalized, not null + * @throws ArithmeticException if numeric overflow occurs + */ + ChronoPeriod normalized(); + + //------------------------------------------------------------------------- + /** + * Adds this period to the specified temporal object. + * <p> + * This returns a temporal object of the same observable type as the input + * with this period added. + * <p> + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#plus(TemporalAmount)}. + * <pre> + * // these two lines are equivalent, but the second approach is recommended + * dateTime = thisPeriod.addTo(dateTime); + * dateTime = dateTime.plus(thisPeriod); + * </pre> + * <p> + * The specified temporal must have the same chronology as this period. + * This returns a temporal with the non-zero supported units added. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same type with the adjustment made, not null + * @throws DateTimeException if unable to add + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + Temporal addTo(Temporal temporal); + + /** + * Subtracts this period from the specified temporal object. + * <p> + * This returns a temporal object of the same observable type as the input + * with this period subtracted. + * <p> + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#minus(TemporalAmount)}. + * <pre> + * // these two lines are equivalent, but the second approach is recommended + * dateTime = thisPeriod.subtractFrom(dateTime); + * dateTime = dateTime.minus(thisPeriod); + * </pre> + * <p> + * The specified temporal must have the same chronology as this period. + * This returns a temporal with the non-zero supported units subtracted. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same type with the adjustment made, not null + * @throws DateTimeException if unable to subtract + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + Temporal subtractFrom(Temporal temporal); + + //----------------------------------------------------------------------- + /** + * Checks if this period is equal to another period, including the chronology. + * <p> + * Compares this period with another ensuring that the type, each amount and + * the chronology are the same. + * Note that this means that a period of "15 Months" is not equal to a period + * of "1 Year and 3 Months". + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other period + */ + @Override + boolean equals(Object obj); + + /** + * A hash code for this period. + * + * @return a suitable hash code + */ + @Override + int hashCode(); + + //----------------------------------------------------------------------- + /** + * Outputs this period as a {@code String}. + * <p> + * The output will include the period amounts and chronology. + * + * @return a string representation of this period, not null + */ + @Override + String toString(); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/time/chrono/ChronoPeriodImpl.java Tue Oct 08 14:57:32 2013 -0700 @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.chrono; + +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.YEARS; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.DateTimeException; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalUnit; +import java.time.temporal.UnsupportedTemporalTypeException; +import java.time.temporal.ValueRange; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A period expressed in terms of a standard year-month-day calendar system. + * <p> + * This class is used by applications seeking to handle dates in non-ISO calendar systems. + * For example, the Japanese, Minguo, Thai Buddhist and others. + * + * @implSpec + * This class is immutable nad thread-safe. + * + * @since 1.8 + */ +final class ChronoPeriodImpl + implements ChronoPeriod, Serializable { + // this class is only used by JDK chronology implementations and makes assumptions based on that fact + + /** + * Serialization version. + */ + private static final long serialVersionUID = 57387258289L; + + /** + * The set of supported units. + */ + private final static List<TemporalUnit> SUPPORTED_UNITS = + Collections.unmodifiableList(Arrays.<TemporalUnit>asList(YEARS, MONTHS, DAYS)); + + /** + * The chronology. + */ + private final Chronology chrono; + /** + * The number of years. + */ + final int years; + /** + * The number of months. + */ + final int months; + /** + * The number of days. + */ + final int days; + + /** + * Creates an instance. + */ + ChronoPeriodImpl(Chronology chrono, int years, int months, int days) { + Objects.requireNonNull(chrono, "chrono"); + this.chrono = chrono; + this.years = years; + this.months = months; + this.days = days; + } + + //----------------------------------------------------------------------- + @Override + public long get(TemporalUnit unit) { + if (unit == ChronoUnit.YEARS) { + return years; + } else if (unit == ChronoUnit.MONTHS) { + return months; + } else if (unit == ChronoUnit.DAYS) { + return days; + } else { + throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); + } + } + + @Override + public List<TemporalUnit> getUnits() { + return ChronoPeriodImpl.SUPPORTED_UNITS; + } + + @Override + public Chronology getChronology() { + return chrono; + } + + //----------------------------------------------------------------------- + @Override + public boolean isZero() { + return years == 0 && months == 0 && days == 0; + } + + @Override + public boolean isNegative() { + return years < 0 || months < 0 || days < 0; + } + + //----------------------------------------------------------------------- + @Override + public ChronoPeriod plus(TemporalAmount amountToAdd) { + ChronoPeriodImpl amount = validateAmount(amountToAdd); + return new ChronoPeriodImpl( + chrono, + Math.addExact(years, amount.years), + Math.addExact(months, amount.months), + Math.addExact(days, amount.days)); + } + + @Override + public ChronoPeriod minus(TemporalAmount amountToSubtract) { + ChronoPeriodImpl amount = validateAmount(amountToSubtract); + return new ChronoPeriodImpl( + chrono, + Math.subtractExact(years, amount.years), + Math.subtractExact(months, amount.months), + Math.subtractExact(days, amount.days)); + } + + /** + * Obtains an instance of {@code ChronoPeriodImpl} from a temporal amount. + * + * @param amount the temporal amount to convert, not null + * @return the period, not null + */ + private ChronoPeriodImpl validateAmount(TemporalAmount amount) { + Objects.requireNonNull(amount, "amount"); + if (amount instanceof ChronoPeriodImpl == false) { + throw new DateTimeException("Unable to obtain ChronoPeriod from TemporalAmount: " + amount.getClass()); + } + ChronoPeriodImpl period = (ChronoPeriodImpl) amount; + if (chrono.equals(period.getChronology()) == false) { + throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + period.getChronology().getId()); + } + return period; + } + + //----------------------------------------------------------------------- + @Override + public ChronoPeriod multipliedBy(int scalar) { + if (this.isZero() || scalar == 1) { + return this; + } + return new ChronoPeriodImpl( + chrono, + Math.multiplyExact(years, scalar), + Math.multiplyExact(months, scalar), + Math.multiplyExact(days, scalar)); + } + + //----------------------------------------------------------------------- + @Override + public ChronoPeriod normalized() { + long monthRange = monthRange(); + if (monthRange > 0) { + long totalMonths = years * monthRange + months; + long splitYears = totalMonths / monthRange; + int splitMonths = (int) (totalMonths % monthRange); // no overflow + if (splitYears == years && splitMonths == months) { + return this; + } + return new ChronoPeriodImpl(chrono, Math.toIntExact(splitYears), splitMonths, days); + + } + return this; + } + + /** + * Calculates the range of months. + * + * @return the month range, -1 if not fixed range + */ + private long monthRange() { + ValueRange startRange = chrono.range(MONTH_OF_YEAR); + if (startRange.isFixed() && startRange.isIntValue()) { + return startRange.getMaximum() - startRange.getMinimum() + 1; + } + return -1; + } + + //------------------------------------------------------------------------- + @Override + public Temporal addTo(Temporal temporal) { + validateChrono(temporal); + if (months == 0) { + if (years != 0) { + temporal = temporal.plus(years, YEARS); + } + } else { + long monthRange = monthRange(); + if (monthRange > 0) { + temporal = temporal.plus(years * monthRange + months, MONTHS); + } else { + if (years != 0) { + temporal = temporal.plus(years, YEARS); + } + temporal = temporal.plus(months, MONTHS); + } + } + if (days != 0) { + temporal = temporal.plus(days, DAYS); + } + return temporal; + } + + + + @Override + public Temporal subtractFrom(Temporal temporal) { + validateChrono(temporal); + if (months == 0) { + if (years != 0) { + temporal = temporal.minus(years, YEARS); + } + } else { + long monthRange = monthRange(); + if (monthRange > 0) { + temporal = temporal.minus(years * monthRange + months, MONTHS); + } else { + if (years != 0) { + temporal = temporal.minus(years, YEARS); + } + temporal = temporal.minus(months, MONTHS); + } + } + if (days != 0) { + temporal = temporal.minus(days, DAYS); + } + return temporal; + } + + /** + * Validates that the temporal has the correct chronology. + */ + private void validateChrono(TemporalAccessor temporal) { + Objects.requireNonNull(temporal, "temporal"); + Chronology temporalChrono = temporal.query(TemporalQuery.chronology()); + if (temporalChrono != null && chrono.equals(temporalChrono) == false) { + throw new DateTimeException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + temporalChrono.getId()); + } + } + + //----------------------------------------------------------------------- + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ChronoPeriodImpl) { + ChronoPeriodImpl other = (ChronoPeriodImpl) obj; + return years == other.years && months == other.months && + days == other.days && chrono.equals(other.chrono); + } + return false; + } + + @Override + public int hashCode() { + return (years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16)) ^ chrono.hashCode(); + } + + //----------------------------------------------------------------------- + @Override + public String toString() { + if (isZero()) { + return getChronology().toString() + " P0D"; + } else { + StringBuilder buf = new StringBuilder(); + buf.append(getChronology().toString()).append(' ').append('P'); + if (years != 0) { + buf.append(years).append('Y'); + } + if (months != 0) { + buf.append(months).append('M'); + } + if (days != 0) { + buf.append(days).append('D'); + } + return buf.toString(); + } + } + + //----------------------------------------------------------------------- + /** + * Writes the Chronology using a + * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>. + * <pre> + * out.writeByte(12); // identifies this as a ChronoPeriodImpl + * out.writeUTF(getId()); // the chronology + * out.writeInt(years); + * out.writeInt(months); + * out.writeInt(days); + * </pre> + * + * @return the instance of {@code Ser}, not null + */ + protected Object writeReplace() { + return new Ser(Ser.CHRONO_PERIOD_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(DataOutput out) throws IOException { + out.writeUTF(chrono.getId()); + out.writeInt(years); + out.writeInt(months); + out.writeInt(days); + } + + static ChronoPeriodImpl readExternal(DataInput in) throws IOException { + Chronology chrono = Chronology.of(in.readUTF()); + int years = in.readInt(); + int months = in.readInt(); + int days = in.readInt(); + return new ChronoPeriodImpl(chrono, years, months, days); + } + +}
--- a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java Tue Oct 08 14:57:32 2013 -0700 @@ -166,6 +166,7 @@ if (temporal instanceof ChronoZonedDateTime) { return (ChronoZonedDateTime<?>) temporal; } + Objects.requireNonNull(temporal, "temporal"); Chronology chrono = temporal.query(TemporalQuery.chronology()); if (chrono == null) { throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass()); @@ -246,6 +247,18 @@ ChronoLocalDateTime<D> toLocalDateTime(); /** + * Gets the chronology of this date-time. + * <p> + * The {@code Chronology} represents the calendar system in use. + * The era and other fields in {@link ChronoField} are defined by the chronology. + * + * @return the chronology, not null + */ + default Chronology getChronology() { + return toLocalDate().getChronology(); + } + + /** * Gets the zone offset, such as '+01:00'. * <p> * This is the offset of the local date-time from UTC/Greenwich. @@ -397,7 +410,7 @@ */ @Override default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) { - return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.with(adjuster)); + return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster)); } /** @@ -415,7 +428,7 @@ */ @Override default ChronoZonedDateTime<D> plus(TemporalAmount amount) { - return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.plus(amount)); + return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount)); } /** @@ -433,7 +446,7 @@ */ @Override default ChronoZonedDateTime<D> minus(TemporalAmount amount) { - return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amount)); + return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount)); } /** @@ -443,7 +456,7 @@ */ @Override default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { - return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amountToSubtract, unit)); + return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit)); } //----------------------------------------------------------------------- @@ -475,7 +488,7 @@ } else if (query == TemporalQuery.localTime()) { return (R) toLocalTime(); } else if (query == TemporalQuery.chronology()) { - return (R) toLocalDate().getChronology(); + return (R) getChronology(); } else if (query == TemporalQuery.precision()) { return (R) NANOS; } @@ -562,7 +575,7 @@ if (cmp == 0) { cmp = getZone().getId().compareTo(other.getZone().getId()); if (cmp == 0) { - cmp = toLocalDate().getChronology().compareTo(other.toLocalDate().getChronology()); + cmp = getChronology().compareTo(other.getChronology()); } } }
--- a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java Tue Oct 08 14:57:32 2013 -0700 @@ -69,7 +69,6 @@ import java.io.ObjectOutput; import java.io.ObjectStreamException; import java.io.Serializable; -import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -186,7 +185,7 @@ */ @SuppressWarnings("unchecked") private ChronoZonedDateTimeImpl<D> create(Instant instant, ZoneId zone) { - return (ChronoZonedDateTimeImpl<D>)ofInstant(toLocalDate().getChronology(), instant, zone); + return (ChronoZonedDateTimeImpl<D>)ofInstant(getChronology(), instant, zone); } /** @@ -201,9 +200,9 @@ static <R extends ChronoLocalDate> ChronoZonedDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) { @SuppressWarnings("unchecked") ChronoZonedDateTimeImpl<R> other = (ChronoZonedDateTimeImpl<R>) temporal; - if (chrono.equals(other.toLocalDate().getChronology()) == false) { + if (chrono.equals(other.getChronology()) == false) { throw new ClassCastException("Chronology mismatch, required: " + chrono.getId() - + ", actual: " + other.toLocalDate().getChronology().getId()); + + ", actual: " + other.getChronology().getId()); } return other; } @@ -234,7 +233,7 @@ if (trans != null && trans.isOverlap()) { ZoneOffset earlierOffset = trans.getOffsetBefore(); if (earlierOffset.equals(offset) == false) { - return new ChronoZonedDateTimeImpl<D>(dateTime, earlierOffset, zone); + return new ChronoZonedDateTimeImpl<>(dateTime, earlierOffset, zone); } } return this; @@ -246,7 +245,7 @@ if (trans != null) { ZoneOffset offset = trans.getOffsetAfter(); if (offset.equals(getOffset()) == false) { - return new ChronoZonedDateTimeImpl<D>(dateTime, offset, zone); + return new ChronoZonedDateTimeImpl<>(dateTime, offset, zone); } } return this; @@ -294,7 +293,7 @@ } return ofBest(dateTime.with(field, newValue), zone, offset); } - return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), field.adjustInto(this, newValue)); + return ChronoZonedDateTimeImpl.ensureValid(getChronology(), field.adjustInto(this, newValue)); } //----------------------------------------------------------------------- @@ -303,25 +302,21 @@ if (unit instanceof ChronoUnit) { return with(dateTime.plus(amountToAdd, unit)); } - return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), unit.addTo(this, amountToAdd)); /// TODO: Generics replacement Risk! + return ChronoZonedDateTimeImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd)); /// TODO: Generics replacement Risk! } //----------------------------------------------------------------------- @Override - public long until(Temporal endDateTime, TemporalUnit unit) { - if (endDateTime instanceof ChronoZonedDateTime == false) { - throw new DateTimeException("Unable to calculate amount as objects are of two different types"); - } + public long until(Temporal endExclusive, TemporalUnit unit) { + Objects.requireNonNull(endExclusive, "endExclusive"); @SuppressWarnings("unchecked") - ChronoZonedDateTime<D> end = (ChronoZonedDateTime<D>) endDateTime; - if (toLocalDate().getChronology().equals(end.toLocalDate().getChronology()) == false) { - throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); - } + ChronoZonedDateTime<D> end = (ChronoZonedDateTime<D>) getChronology().zonedDateTime(endExclusive); if (unit instanceof ChronoUnit) { end = end.withZoneSameInstant(offset); return dateTime.until(end.toLocalDateTime(), unit); } - return unit.between(this, endDateTime); + Objects.requireNonNull(unit, "unit"); + return unit.between(this, end); } //-----------------------------------------------------------------------
--- a/jdk/src/share/classes/java/time/chrono/Chronology.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/Chronology.java Tue Oct 08 14:57:32 2013 -0700 @@ -91,14 +91,11 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; -import java.time.Month; -import java.time.Year; import java.time.ZoneId; import java.time.format.DateTimeFormatterBuilder; import java.time.format.ResolverStyle; import java.time.format.TextStyle; import java.time.temporal.ChronoField; -import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; @@ -1192,6 +1189,38 @@ //----------------------------------------------------------------------- /** + * Obtains a period for this chronology based on years, months and days. + * <p> + * This returns a period tied to this chronology using the specified + * years, months and days. All supplied chronologies use periods + * based on years, months and days, however the {@code ChronoPeriod} API + * allows the period to be represented using other units. + * + * @implSpec + * The default implementation returns an implementation class suitable + * for most calendar systems. It is based solely on the three units. + * Normalization, addition and subtraction derive the number of months + * in a year from the {@link #range(ChronoField)}. If the number of + * months within a year is fixed, then the calculation approach for + * addition, subtraction and normalization is slightly different. + * <p> + * If implementing an unusual calendar system that is not based on + * years, months and days, or where you want direct control, then + * the {@code ChronoPeriod} interface must be directly implemented. + * <p> + * The returned period is immutable and thread-safe. + * + * @param years the number of years, may be negative + * @param months the number of years, may be negative + * @param days the number of years, may be negative + * @return the period in terms of this chronology, not null + */ + public ChronoPeriod period(int years, int months, int days) { + return new ChronoPeriodImpl(this, years, months, days); + } + + //----------------------------------------------------------------------- + /** * Compares this chronology to another chronology. * <p> * The comparison order first by the chronology ID string, then by any
--- a/jdk/src/share/classes/java/time/chrono/HijrahDate.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/HijrahDate.java Tue Oct 08 14:57:32 2013 -0700 @@ -73,7 +73,6 @@ import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; -import java.time.Period; import java.time.ZoneId; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; @@ -109,7 +108,7 @@ * @since 1.8 */ public final class HijrahDate - extends ChronoDateImpl<HijrahDate> + extends ChronoLocalDateImpl<HijrahDate> implements ChronoLocalDate, Serializable { /** @@ -582,7 +581,7 @@ } @Override - public Period until(ChronoLocalDate endDate) { + public ChronoPeriod until(ChronoLocalDate endDate) { // TODO: untested HijrahDate end = getChronology().date(endDate); long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe @@ -597,7 +596,7 @@ } long years = totalMonths / 12; // safe int months = (int) (totalMonths % 12); // safe - return Period.of(Math.toIntExact(years), months, days); + return getChronology().period(Math.toIntExact(years), months, days); } //-----------------------------------------------------------------------
--- a/jdk/src/share/classes/java/time/chrono/IsoChronology.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/IsoChronology.java Tue Oct 08 14:57:32 2013 -0700 @@ -77,6 +77,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; +import java.time.Period; import java.time.Year; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -567,6 +568,24 @@ //----------------------------------------------------------------------- /** + * Obtains a period for this chronology based on years, months and days. + * <p> + * This returns a period tied to the ISO chronology using the specified + * years, months and days. See {@link Period} for further details. + * + * @param years the number of years, may be negative + * @param months the number of years, may be negative + * @param days the number of years, may be negative + * @return the period in terms of this chronology, not null + * @return the ISO period, not null + */ + @Override // override with covariant return type + public Period period(int years, int months, int days) { + return Period.of(years, months, days); + } + + //----------------------------------------------------------------------- + /** * Writes the Chronology using a * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>. * @serialData
--- a/jdk/src/share/classes/java/time/chrono/JapaneseDate.java Tue Oct 08 14:53:14 2013 -0700 +++ b/jdk/src/share/classes/java/time/chrono/JapaneseDate.java Tue Oct 08 14:57:32 2013 -0700 @@ -61,10 +61,8 @@ import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; -import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; -import static java.time.temporal.ChronoField.YEAR_OF_ERA; import java.io.DataInput; import java.io.DataOutput; @@ -76,7 +74,6 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.Period; -import java.time.Year; import java.time.ZoneId; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor;