mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-05 16:09:09 +00:00
Merge
This commit is contained in:
commit
b6903ca07e
@ -50,6 +50,7 @@ BUG_SUBMIT_LINE = <a href="$(BUG_SUBMIT_URL)">Submit a bug or feature</a>
|
||||
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
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ FILES_c = \
|
||||
Console_md.c \
|
||||
Double.c \
|
||||
Executable.c \
|
||||
Field.c \
|
||||
FileDescriptor_md.c \
|
||||
FileInputStream.c \
|
||||
FileInputStream_md.c \
|
||||
|
||||
@ -190,6 +190,8 @@ SUNWprivate_1.1 {
|
||||
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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -173,6 +173,44 @@ class JarMetaIndex {
|
||||
*/
|
||||
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
|
||||
@ -237,39 +275,25 @@ class JarMetaIndex {
|
||||
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] + "/";
|
||||
}
|
||||
indexSet.add(meta);
|
||||
for (int i = 0; i < levels && i < pkgElements.length - 1; i++) {
|
||||
meta += pkgElements[i] + "/";
|
||||
}
|
||||
|
||||
if (!meta.equals("")) {
|
||||
indexSet.add(meta);
|
||||
}
|
||||
}
|
||||
|
||||
} // end of "while" loop;
|
||||
|
||||
@ -237,10 +237,10 @@ endif
|
||||
|
||||
# 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 @@ $(eval $(call SetupJavaCompilation,BUILD_JDK,\
|
||||
|
||||
##########################################################################################
|
||||
|
||||
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 @@ endif
|
||||
|
||||
##########################################################################################
|
||||
|
||||
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)
|
||||
|
||||
@ -1003,15 +1003,6 @@ endif
|
||||
|
||||
##########################################################################################
|
||||
|
||||
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.
|
||||
|
||||
@ -40,8 +40,8 @@ include profile-includes.txt
|
||||
# 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 @@ PROFILE_3_JARS := \
|
||||
$(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 @@ endif
|
||||
|
||||
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 @@ ALL_JRE_BIN_FILES := \
|
||||
$(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 @@ ifeq ($(PROFILE), profile_1)
|
||||
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 @@ ALL_JRE_LIB_FILES := \
|
||||
$(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 @@ ifeq ($(PROFILE), profile_1)
|
||||
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 @@ NOT_JRE_LIB_FILES += $(CUSTOM_JARS)
|
||||
|
||||
###############################################################################
|
||||
# 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 @@ NOT_JRE_LIB_FILES += $(CUSTOM_JARS)
|
||||
#
|
||||
# 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 @@ NOT_JRE_LIB_FILES += $(CUSTOM_JARS)
|
||||
# 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 @@ NOT_JRE_LIB_FILES += $(CUSTOM_JARS)
|
||||
# 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 @@ NOT_JRE_LIB_FILES += $(CUSTOM_JARS)
|
||||
#
|
||||
# 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 @@ endif
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@ -190,6 +190,8 @@ SUNWprivate_1.1 {
|
||||
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;
|
||||
|
||||
@ -107,14 +107,14 @@ PROFILE_2_JRE_BIN_FILES := \
|
||||
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 @@ PROFILE_3_JRE_LIB_FILES := \
|
||||
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 @@ FULL_JRE_LIB_FILES := \
|
||||
$(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 @@ FULL_JRE_OTHER_FILES := \
|
||||
man/man1/unpack200.1
|
||||
|
||||
FULL_JRE_JAR_FILES := \
|
||||
alt-rt.jar \
|
||||
charsets.jar \
|
||||
ext/cldrdata.jar \
|
||||
ext/dnsns.jar \
|
||||
|
||||
@ -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 final class AppleProvider extends Provider {
|
||||
|
||||
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() {
|
||||
|
||||
@ -104,7 +104,7 @@ public final class SunJCE extends Provider {
|
||||
|
||||
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" +
|
||||
|
||||
@ -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 @@ class Driver {
|
||||
} 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);
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
//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,
|
||||
}
|
||||
},
|
||||
|
||||
{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;
|
||||
}
|
||||
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 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
|
||||
};
|
||||
|
||||
protected Object[][] getContents() {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 @@ public final class Provider extends java.security.Provider {
|
||||
" 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() {
|
||||
|
||||
@ -1908,10 +1908,18 @@ public class File
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3414,16 +3414,20 @@ public final class Class<T> implements java.io.Serializable,
|
||||
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 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* <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
|
||||
* 'extends' clause of the declaration of this Class.
|
||||
* 'implements' 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 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.
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
|
||||
@ -125,10 +125,7 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
||||
|
||||
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 ||
|
||||
|
||||
@ -360,6 +360,10 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
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 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
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 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* 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 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* {@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) {
|
||||
|
||||
@ -60,7 +60,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
* <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 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam
|
||||
// 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 @@ public class CallSite {
|
||||
* 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 @@ public class CallSite {
|
||||
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 @@ public class CallSite {
|
||||
} 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) {
|
||||
|
||||
@ -257,12 +257,12 @@ class DirectMethodHandle extends MethodHandle {
|
||||
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 @@ class DirectMethodHandle extends MethodHandle {
|
||||
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 @@ class DirectMethodHandle extends MethodHandle {
|
||||
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 @@ class DirectMethodHandle extends MethodHandle {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -242,9 +242,6 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
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 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* Emit an invoke for the given name.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
void emitInvoke(Name name) {
|
||||
if (true) {
|
||||
@ -645,8 +624,6 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
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 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* 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 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
/**
|
||||
* Generate bytecode for a NamedFunction invoker.
|
||||
*
|
||||
* @param srcType
|
||||
* @param dstType
|
||||
* @return
|
||||
*/
|
||||
static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
|
||||
MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
|
||||
|
||||
@ -44,6 +44,7 @@ class Invokers {
|
||||
|
||||
// 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 @@ class Invokers {
|
||||
/*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 @@ class Invokers {
|
||||
/*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 @@ class Invokers {
|
||||
vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
|
||||
}
|
||||
assert(vaInvoker.type().equals(spreadInvokerType.invokerType()));
|
||||
maybeCompileToBytecode(vaInvoker);
|
||||
spreadInvokers[leadingArgCount] = vaInvoker;
|
||||
return vaInvoker;
|
||||
}
|
||||
@ -231,32 +232,38 @@ class Invokers {
|
||||
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 @@ class Invokers {
|
||||
: 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 @@ class Invokers {
|
||||
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 @@ class Invokers {
|
||||
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 @@ class Invokers {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ import java.util.Objects;
|
||||
* 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 @@ import java.util.Objects;
|
||||
* 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 @@ import java.util.Objects;
|
||||
* 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 @@ import java.util.Objects;
|
||||
* 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 @@ import java.util.Objects;
|
||||
* (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 @@ class LambdaForm {
|
||||
isCompiled = true;
|
||||
return vmentry;
|
||||
} catch (Error | Exception ex) {
|
||||
throw newInternalError(this.toString(), ex);
|
||||
throw newInternalError("compileToBytecode", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,8 +683,9 @@ class LambdaForm {
|
||||
*/
|
||||
|
||||
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 @@ class LambdaForm {
|
||||
|
||||
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 @@ class LambdaForm {
|
||||
//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 @@ class LambdaForm {
|
||||
}
|
||||
|
||||
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 @@ class LambdaForm {
|
||||
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 @@ class LambdaForm {
|
||||
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 @@ class LambdaForm {
|
||||
* 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 @@ class LambdaForm {
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -70,13 +70,13 @@ import java.util.Objects;
|
||||
* @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 @@ import java.util.Objects;
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
expandFromVM();
|
||||
if (name == null) return null;
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@ -119,28 +121,39 @@ import java.util.Objects;
|
||||
public MethodType getMethodType() {
|
||||
if (type == null) {
|
||||
expandFromVM();
|
||||
if (type == null) return null;
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!isInvocable())
|
||||
if (!isInvocable()) {
|
||||
throw newIllegalArgumentException("not invocable, no method 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;
|
||||
|
||||
{
|
||||
// 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 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
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 @@ import java.util.Objects;
|
||||
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 @@ import java.util.Objects;
|
||||
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 @@ import java.util.Objects;
|
||||
//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 @@ import java.util.Objects;
|
||||
}
|
||||
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 @@ import java.util.Objects;
|
||||
}
|
||||
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 @@ import java.util.Objects;
|
||||
|
||||
@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 @@ import java.util.Objects;
|
||||
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 @@ import java.util.Objects;
|
||||
* 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, 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);
|
||||
}
|
||||
// /** 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);
|
||||
// }
|
||||
|
||||
/** Query whether this member name is resolved to a non-static, non-final method.
|
||||
*/
|
||||
public boolean hasReceiverTypeDispatch() {
|
||||
|
||||
@ -292,7 +292,7 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* 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 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* 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 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* 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 @@ public abstract class MethodHandle {
|
||||
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 @@ public abstract class MethodHandle {
|
||||
/*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 @@ public abstract class MethodHandle {
|
||||
* 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 @@ public abstract class MethodHandle {
|
||||
* <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 @@ public abstract class MethodHandle {
|
||||
* @see MethodHandles#explicitCastArguments
|
||||
*/
|
||||
public MethodHandle asType(MethodType newType) {
|
||||
if (!type.isConvertibleTo(newType)) {
|
||||
throw new WrongMethodTypeException("cannot convert "+this+" to "+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 convertArguments(newType);
|
||||
// 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 asTypeCache = convertArguments(newType);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -772,6 +804,10 @@ public abstract class MethodHandle {
|
||||
* 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 @@ assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
|
||||
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 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray
|
||||
* @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 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
* @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 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
* 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.
|
||||
@ -1284,6 +1330,11 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
return null; // DMH returns DMH.member
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
Class<?> internalCallerClass() {
|
||||
return null; // caller-bound MH for @CallerSensitive method returns caller
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle withInternalMemberName(MemberName member) {
|
||||
if (member != null) {
|
||||
@ -1434,7 +1485,6 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
* 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) {
|
||||
|
||||
@ -314,13 +314,13 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
|
||||
@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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
} 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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
MemberName internalMemberName() {
|
||||
return asFixedArity().internalMemberName();
|
||||
}
|
||||
@Override
|
||||
Class<?> internalCallerClass() {
|
||||
return asFixedArity().internalCallerClass();
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
@Override
|
||||
@ -435,7 +440,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
// 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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
|
||||
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
|
||||
// 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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -32,9 +32,10 @@ import java.lang.invoke.MethodHandles.Lookup;
|
||||
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 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* 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 @@ interface MethodHandleInfo {
|
||||
* @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();
|
||||
|
||||
|
||||
@ -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 @@ class MethodHandleNatives {
|
||||
}
|
||||
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 MethodHandleNatives {
|
||||
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 @@ class MethodHandleNatives {
|
||||
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,13 +440,33 @@ class MethodHandleNatives {
|
||||
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
|
||||
|
||||
@ -107,6 +107,11 @@ public class MethodHandleProxies {
|
||||
* 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}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -77,7 +77,8 @@ import sun.invoke.util.VerifyType;
|
||||
* 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 @@ class MethodType implements java.io.Serializable {
|
||||
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 @@ class MethodType implements java.io.Serializable {
|
||||
* 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}.
|
||||
|
||||
@ -28,6 +28,7 @@ package java.lang.invoke;
|
||||
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 class MethodTypeForm {
|
||||
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 @@ final class MethodTypeForm {
|
||||
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 @@ final class MethodTypeForm {
|
||||
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.
|
||||
|
||||
@ -38,7 +38,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* 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 @@ assertEquals("ROCKY", (String) worker1.invokeExact());
|
||||
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 @@ assertEquals("Fred, dear?", (String) worker2.invokeExact());
|
||||
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
|
||||
|
||||
73
jdk/src/share/classes/java/lang/invoke/Stable.java
Normal file
73
jdk/src/share/classes/java/lang/invoke/Stable.java
Normal file
@ -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 {
|
||||
}
|
||||
@ -55,7 +55,7 @@ package java.lang.invoke;
|
||||
* 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 @@ assertEquals("method", (String) worker.invokeExact("met", "hod"));
|
||||
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 @@ assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
|
||||
* <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 @@ public class SwitchPoint {
|
||||
MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
|
||||
}
|
||||
}
|
||||
* </pre></blockquote>
|
||||
* }</pre></blockquote>
|
||||
* @author Remi Forax, JSR 292 EG
|
||||
*/
|
||||
public class SwitchPoint {
|
||||
|
||||
@ -27,17 +27,18 @@ package java.lang.reflect;
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -67,8 +67,6 @@ public final class Constructor<T> extends Executable {
|
||||
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 @@ public final class Constructor<T> extends Executable {
|
||||
res.root = this;
|
||||
// Might as well eagerly propagate this if already present
|
||||
res.constructorAccessor = constructorAccessor;
|
||||
|
||||
res.typeAnnotations = typeAnnotations;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -155,10 +151,6 @@ public final class Constructor<T> extends Executable {
|
||||
byte[] getAnnotationBytes() {
|
||||
return annotations;
|
||||
}
|
||||
@Override
|
||||
byte[] getTypeAnnotationBytes() {
|
||||
return typeAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
||||
@ -51,7 +51,6 @@ public abstract class Executable extends AccessibleObject
|
||||
* Accessor method to allow code sharing
|
||||
*/
|
||||
abstract byte[] getAnnotationBytes();
|
||||
abstract byte[] getTypeAnnotationBytes();
|
||||
|
||||
/**
|
||||
* Does the Executable have generic information.
|
||||
@ -287,12 +286,14 @@ public abstract class Executable extends AccessibleObject
|
||||
* 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 @@ public abstract class Executable extends AccessibleObject
|
||||
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 @@ public abstract class Executable extends AccessibleObject
|
||||
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 @@ public abstract class Executable extends AccessibleObject
|
||||
tmp = synthesizeAllParams();
|
||||
} else {
|
||||
hasRealParameterData = true;
|
||||
verifyParameters(tmp);
|
||||
}
|
||||
|
||||
parameters = tmp;
|
||||
@ -352,6 +383,12 @@ public abstract class Executable extends AccessibleObject
|
||||
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 @@ public abstract class Executable extends AccessibleObject
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the method
|
||||
* or constructor represented by this {@code Executable}
|
||||
*
|
||||
* @return an object representing the return type of this method
|
||||
* or constructor
|
||||
* @since 1.8
|
||||
*/
|
||||
public abstract AnnotatedType getAnnotatedReturnType();
|
||||
@ -539,7 +578,7 @@ public abstract class Executable extends AccessibleObject
|
||||
* @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 @@ public abstract class Executable extends AccessibleObject
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class Executable extends AccessibleObject
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class Executable extends AccessibleObject
|
||||
* 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 @@ public abstract class Executable extends AccessibleObject
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class Executable extends AccessibleObject
|
||||
* 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,
|
||||
|
||||
@ -82,8 +82,6 @@ class Field extends AccessibleObject implements Member {
|
||||
// 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 @@ class Field extends AccessibleObject implements Member {
|
||||
res.fieldAccessor = fieldAccessor;
|
||||
res.overrideFieldAccessor = overrideFieldAccessor;
|
||||
|
||||
res.typeAnnotations = typeAnnotations;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -1148,6 +1145,8 @@ class Field extends AccessibleObject implements Member {
|
||||
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 @@ class Field extends AccessibleObject implements Member {
|
||||
* @since 1.8
|
||||
*/
|
||||
public AnnotatedType getAnnotatedType() {
|
||||
return TypeAnnotationParser.buildAnnotatedType(typeAnnotations,
|
||||
return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes0(),
|
||||
sun.misc.SharedSecrets.getJavaLangAccess().
|
||||
getConstantPool(getDeclaringClass()),
|
||||
this,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -80,8 +80,6 @@ public final class Method extends Executable {
|
||||
// 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 @@ public final class Method extends Executable {
|
||||
res.root = this;
|
||||
// Might as well eagerly propagate this if already present
|
||||
res.methodAccessor = methodAccessor;
|
||||
|
||||
res.typeAnnotations = typeAnnotations;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -166,10 +162,6 @@ public final class Method extends Executable {
|
||||
byte[] getAnnotationBytes() {
|
||||
return annotations;
|
||||
}
|
||||
@Override
|
||||
byte[] getTypeAnnotationBytes() {
|
||||
return typeAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
||||
@ -104,7 +104,7 @@ public final class Parameter implements AnnotatedElement {
|
||||
* to the class file.
|
||||
*/
|
||||
public boolean isNamePresent() {
|
||||
return executable.hasRealParameterData();
|
||||
return executable.hasRealParameterData() && name != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,6 +182,11 @@ public final class Parameter implements AnnotatedElement {
|
||||
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}
|
||||
|
||||
@ -2128,7 +2128,7 @@ public final class Files {
|
||||
/**
|
||||
* 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 @@ public final class Files {
|
||||
* 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 @@ public final class Files {
|
||||
* 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 @@ public final class Files {
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -315,7 +315,7 @@ public interface Path
|
||||
* 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
|
||||
|
||||
@ -578,39 +578,30 @@ public class SecureRandom extends java.util.Random {
|
||||
/**
|
||||
* 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 @@ public class SecureRandom extends java.util.Random {
|
||||
});
|
||||
|
||||
if ((property == null) || (property.length() == 0)) {
|
||||
return null;
|
||||
throw new NoSuchAlgorithmException(
|
||||
"Null/empty securerandom.strongAlgorithms Security Property");
|
||||
}
|
||||
|
||||
String remainder = property;
|
||||
@ -649,7 +641,8 @@ public class SecureRandom extends java.util.Random {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new NoSuchAlgorithmException(
|
||||
"No strong SecureRandom impls available: " + property);
|
||||
}
|
||||
|
||||
// Declare serialVersionUID to be compatible with JDK1.1
|
||||
|
||||
@ -441,9 +441,13 @@ public final class Duration
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* 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. 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>
|
||||
* This calculates the duration between two temporal objects of the same type.
|
||||
* 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.
|
||||
|
||||
@ -362,6 +362,10 @@ public final class Instant
|
||||
* @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 @@ public final class Instant
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* 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 @@ public final class Instant
|
||||
* 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 @@ public final class Instant
|
||||
* <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 @@ public final class Instant
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endInstant);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
private long nanosUntil(Instant end) {
|
||||
|
||||
@ -353,6 +353,7 @@ public final class LocalDate
|
||||
* @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 @@ public final class LocalDate
|
||||
*/
|
||||
@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 @@ public final class LocalDate
|
||||
*/
|
||||
@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 @@ public final class LocalDate
|
||||
* 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 @@ public final class LocalDate
|
||||
* <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 @@ public final class LocalDate
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endDate);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
long daysUntil(LocalDate end) {
|
||||
@ -1632,12 +1640,12 @@ public final class LocalDate
|
||||
* </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) {
|
||||
|
||||
@ -1129,6 +1129,11 @@ public final class LocalDateTime
|
||||
*/
|
||||
@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 @@ public final class LocalDateTime
|
||||
*/
|
||||
@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 @@ public final class LocalDateTime
|
||||
* 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 @@ public final class LocalDateTime
|
||||
* <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 @@ public final class LocalDateTime
|
||||
}
|
||||
return date.until(endDate, unit);
|
||||
}
|
||||
return unit.between(this, endDateTime);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -394,6 +394,7 @@ public final class LocalTime
|
||||
* @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 @@ public final class LocalTime
|
||||
* 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 @@ public final class LocalTime
|
||||
* <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 @@ public final class LocalTime
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endTime);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -246,7 +246,8 @@ public final class MonthDay
|
||||
* <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}.
|
||||
|
||||
@ -1592,7 +1592,8 @@ public final class OffsetDateTime
|
||||
* 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 @@ public final class OffsetDateTime
|
||||
* <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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1124,7 +1124,8 @@ public final class OffsetTime
|
||||
* 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 @@ public final class OffsetTime
|
||||
* <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 @@ public final class OffsetTime
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endTime);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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.DataInput;
|
||||
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.Matcher;
|
||||
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 @@ import java.util.regex.Pattern;
|
||||
* {@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 @@ import java.util.regex.Pattern;
|
||||
* @since 1.8
|
||||
*/
|
||||
public final class Period
|
||||
implements TemporalAmount, Serializable {
|
||||
implements ChronoPeriod, Serializable {
|
||||
|
||||
/**
|
||||
* A constant for a period of zero.
|
||||
@ -140,6 +140,7 @@ public final class Period
|
||||
*/
|
||||
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 @@ public final class Period
|
||||
* <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 @@ public final class Period
|
||||
* @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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
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 @@ public final class Period
|
||||
* <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 @@ public final class Period
|
||||
* <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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* 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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class Period
|
||||
* <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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
*/
|
||||
@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 @@ public final class Period
|
||||
* <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 @@ public final class Period
|
||||
* 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 @@ public final class Period
|
||||
*/
|
||||
@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 @@ public final class Period
|
||||
}
|
||||
|
||||
/**
|
||||
* 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".
|
||||
|
||||
@ -242,6 +242,7 @@ public final class Year
|
||||
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 @@ public final class Year
|
||||
* 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 @@ public final class Year
|
||||
* <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 @@ public final class Year
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endYear);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -245,6 +245,7 @@ public final class YearMonth
|
||||
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 @@ public final class YearMonth
|
||||
* 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 @@ public final class YearMonth
|
||||
* <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 @@ public final class YearMonth
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endYearMonth);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -333,6 +333,7 @@ public final class ZoneOffset
|
||||
* @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());
|
||||
|
||||
@ -1540,6 +1540,11 @@ public final class ZonedDateTime
|
||||
*/
|
||||
@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 @@ public final class ZonedDateTime
|
||||
*/
|
||||
@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 @@ public final class ZonedDateTime
|
||||
* 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 @@ public final class ZonedDateTime
|
||||
* <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 @@ public final class ZonedDateTime
|
||||
return toOffsetDateTime().until(end.toOffsetDateTime(), unit);
|
||||
}
|
||||
}
|
||||
return unit.between(this, endDateTime);
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -69,7 +69,6 @@ import static java.time.temporal.ChronoUnit.DAYS;
|
||||
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 @@ public interface ChronoLocalDate
|
||||
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 @@ public interface ChronoLocalDate
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDate
|
||||
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 @@ public interface ChronoLocalDate
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDate
|
||||
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 @@ public interface ChronoLocalDate
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDate
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDate
|
||||
* 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 @@ public interface ChronoLocalDate
|
||||
* <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. 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>
|
||||
* This calculates the period between two dates in terms of years, months and days.
|
||||
* 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 @@ public interface ChronoLocalDate
|
||||
* <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.
|
||||
|
||||
@ -140,7 +140,7 @@ import java.util.Objects;
|
||||
* @param <D> the ChronoLocalDate of this date-time
|
||||
* @since 1.8
|
||||
*/
|
||||
abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
abstract class ChronoLocalDateImpl<D extends ChronoLocalDate>
|
||||
implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable {
|
||||
|
||||
/**
|
||||
@ -170,7 +170,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
ChronoDateImpl() {
|
||||
ChronoLocalDateImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -309,7 +309,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
D minusYears(long yearsToSubtract) {
|
||||
return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract));
|
||||
return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -330,7 +330,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
D minusMonths(long monthsToSubtract) {
|
||||
return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract));
|
||||
return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,7 +350,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
D minusWeeks(long weeksToSubtract) {
|
||||
return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract));
|
||||
return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,26 +368,14 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
D minusDays(long daysToSubtract) {
|
||||
return (daysToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract));
|
||||
return (daysToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<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");
|
||||
}
|
||||
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);
|
||||
@ -401,7 +389,8 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
}
|
||||
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
|
||||
}
|
||||
return unit.between(this, endDateTime);
|
||||
Objects.requireNonNull(unit, "unit");
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
private long daysUntil(ChronoLocalDate end) {
|
||||
@ -411,7 +400,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate>
|
||||
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");
|
||||
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
|
||||
@ -165,6 +165,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
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());
|
||||
@ -173,6 +174,18 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* 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>
|
||||
@ -250,7 +263,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
} 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 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate>
|
||||
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;
|
||||
|
||||
@ -69,7 +69,6 @@ import java.io.ObjectInput;
|
||||
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 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
|
||||
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 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
|
||||
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 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@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 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
|
||||
}
|
||||
return date.until(endDate, unit);
|
||||
}
|
||||
return unit.between(this, endDateTime);
|
||||
Objects.requireNonNull(unit, "unit");
|
||||
return unit.between(this, end);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
365
jdk/src/share/classes/java/time/chrono/ChronoPeriod.java
Normal file
365
jdk/src/share/classes/java/time/chrono/ChronoPeriod.java
Normal file
@ -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();
|
||||
|
||||
}
|
||||
399
jdk/src/share/classes/java/time/chrono/ChronoPeriodImpl.java
Normal file
399
jdk/src/share/classes/java/time/chrono/ChronoPeriodImpl.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -166,6 +166,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
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());
|
||||
@ -245,6 +246,18 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
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>
|
||||
@ -397,7 +410,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
} 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 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate>
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +69,6 @@ import java.io.ObjectInput;
|
||||
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 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
|
||||
*/
|
||||
@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 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
|
||||
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 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
|
||||
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 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
|
||||
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 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
|
||||
}
|
||||
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 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
|
||||
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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@ -91,14 +91,11 @@ import java.time.DayOfWeek;
|
||||
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;
|
||||
@ -1190,6 +1187,38 @@ public abstract class Chronology implements Comparable<Chronology> {
|
||||
fieldValues.put(field, value);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@ -73,7 +73,6 @@ import java.time.Clock;
|
||||
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 @@ import java.time.temporal.ValueRange;
|
||||
* @since 1.8
|
||||
*/
|
||||
public final class HijrahDate
|
||||
extends ChronoDateImpl<HijrahDate>
|
||||
extends ChronoLocalDateImpl<HijrahDate>
|
||||
implements ChronoLocalDate, Serializable {
|
||||
|
||||
/**
|
||||
@ -582,7 +581,7 @@ public final class HijrahDate
|
||||
}
|
||||
|
||||
@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 @@ public final class HijrahDate
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@ -77,6 +77,7 @@ import java.time.Instant;
|
||||
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;
|
||||
@ -565,6 +566,24 @@ public final class IsoChronology extends Chronology implements Serializable {
|
||||
return field.range();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -61,10 +61,8 @@ import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
|
||||
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.DateTimeException;
|
||||
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;
|
||||
@ -119,7 +116,7 @@ import sun.util.calendar.LocalGregorianCalendar;
|
||||
* @since 1.8
|
||||
*/
|
||||
public final class JapaneseDate
|
||||
extends ChronoDateImpl<JapaneseDate>
|
||||
extends ChronoLocalDateImpl<JapaneseDate>
|
||||
implements ChronoLocalDate, Serializable {
|
||||
|
||||
/**
|
||||
@ -662,8 +659,9 @@ public final class JapaneseDate
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period until(ChronoLocalDate endDate) {
|
||||
return isoDate.until(endDate);
|
||||
public ChronoPeriod until(ChronoLocalDate endDate) {
|
||||
Period period = isoDate.until(endDate);
|
||||
return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
|
||||
}
|
||||
|
||||
@Override // override for performance
|
||||
|
||||
@ -96,7 +96,7 @@ import java.util.Objects;
|
||||
* @since 1.8
|
||||
*/
|
||||
public final class MinguoDate
|
||||
extends ChronoDateImpl<MinguoDate>
|
||||
extends ChronoLocalDateImpl<MinguoDate>
|
||||
implements ChronoLocalDate, Serializable {
|
||||
|
||||
/**
|
||||
@ -421,8 +421,9 @@ public final class MinguoDate
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period until(ChronoLocalDate endDate) {
|
||||
return isoDate.until(endDate);
|
||||
public ChronoPeriod until(ChronoLocalDate endDate) {
|
||||
Period period = isoDate.until(endDate);
|
||||
return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
|
||||
}
|
||||
|
||||
@Override // override for performance
|
||||
|
||||
@ -104,6 +104,7 @@ final class Ser implements Externalizable {
|
||||
static final byte HIJRAH_DATE_TYPE = 6;
|
||||
static final byte MINGUO_DATE_TYPE = 7;
|
||||
static final byte THAIBUDDHIST_DATE_TYPE = 8;
|
||||
static final byte CHRONO_PERIOD_TYPE = 9;
|
||||
|
||||
/** The type being serialized. */
|
||||
private byte type;
|
||||
@ -183,6 +184,9 @@ final class Ser implements Externalizable {
|
||||
case THAIBUDDHIST_DATE_TYPE:
|
||||
((ThaiBuddhistDate) object).writeExternal(out);
|
||||
break;
|
||||
case CHRONO_PERIOD_TYPE:
|
||||
((ChronoPeriodImpl) object).writeExternal(out);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidClassException("Unknown serialized type");
|
||||
}
|
||||
@ -235,6 +239,7 @@ final class Ser implements Externalizable {
|
||||
case HIJRAH_DATE_TYPE: return HijrahDate.readExternal(in);
|
||||
case MINGUO_DATE_TYPE: return MinguoDate.readExternal(in);
|
||||
case THAIBUDDHIST_DATE_TYPE: return ThaiBuddhistDate.readExternal(in);
|
||||
case CHRONO_PERIOD_TYPE: return ChronoPeriodImpl.readExternal(in);
|
||||
default: throw new StreamCorruptedException("Unknown serialized type");
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ import java.util.Objects;
|
||||
* @since 1.8
|
||||
*/
|
||||
public final class ThaiBuddhistDate
|
||||
extends ChronoDateImpl<ThaiBuddhistDate>
|
||||
extends ChronoLocalDateImpl<ThaiBuddhistDate>
|
||||
implements ChronoLocalDate, Serializable {
|
||||
|
||||
/**
|
||||
@ -421,8 +421,9 @@ public final class ThaiBuddhistDate
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period until(ChronoLocalDate endDate) {
|
||||
return isoDate.until(endDate);
|
||||
public ChronoPeriod until(ChronoLocalDate endDate) {
|
||||
Period period = isoDate.until(endDate);
|
||||
return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
|
||||
}
|
||||
|
||||
@Override // override for performance
|
||||
|
||||
@ -83,6 +83,8 @@ import java.time.LocalTime;
|
||||
import java.time.Period;
|
||||
import java.time.ZoneId;
|
||||
import java.time.chrono.ChronoLocalDate;
|
||||
import java.time.chrono.ChronoLocalDateTime;
|
||||
import java.time.chrono.ChronoZonedDateTime;
|
||||
import java.time.chrono.Chronology;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
@ -260,11 +262,34 @@ final class Parsed implements TemporalAccessor {
|
||||
while (changedCount < 50) {
|
||||
for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
|
||||
TemporalField targetField = entry.getKey();
|
||||
ChronoLocalDate resolvedDate = targetField.resolve(fieldValues, chrono, zone, resolverStyle);
|
||||
if (resolvedDate != null) {
|
||||
updateCheckConflict(resolvedDate);
|
||||
changedCount++;
|
||||
continue outer; // have to restart to avoid concurrent modification
|
||||
TemporalAccessor resolvedObject = targetField.resolve(fieldValues, chrono, zone, resolverStyle);
|
||||
if (resolvedObject != null) {
|
||||
if (resolvedObject instanceof ChronoZonedDateTime) {
|
||||
ChronoZonedDateTime czdt = (ChronoZonedDateTime) resolvedObject;
|
||||
if (zone.equals(czdt.getZone()) == false) {
|
||||
throw new DateTimeException("ChronoZonedDateTime must use the effective parsed zone: " + zone);
|
||||
}
|
||||
resolvedObject = czdt.toLocalDateTime();
|
||||
}
|
||||
if (resolvedObject instanceof ChronoLocalDateTime) {
|
||||
ChronoLocalDateTime cldt = (ChronoLocalDateTime) resolvedObject;
|
||||
updateCheckConflict(cldt.toLocalTime(), Period.ZERO);
|
||||
updateCheckConflict(cldt.toLocalDate());
|
||||
changedCount++;
|
||||
continue outer; // have to restart to avoid concurrent modification
|
||||
}
|
||||
if (resolvedObject instanceof ChronoLocalDate) {
|
||||
updateCheckConflict((ChronoLocalDate) resolvedObject);
|
||||
changedCount++;
|
||||
continue outer; // have to restart to avoid concurrent modification
|
||||
}
|
||||
if (resolvedObject instanceof LocalTime) {
|
||||
updateCheckConflict((LocalTime) resolvedObject, Period.ZERO);
|
||||
changedCount++;
|
||||
continue outer; // have to restart to avoid concurrent modification
|
||||
}
|
||||
throw new DateTimeException("Method resolveFields() can only return ChronoZonedDateTime," +
|
||||
"ChronoLocalDateTime, ChronoLocalDate or LocalTime");
|
||||
} else if (fieldValues.containsKey(targetField) == false) {
|
||||
changedCount++;
|
||||
continue outer; // have to restart to avoid concurrent modification
|
||||
@ -302,7 +327,10 @@ final class Parsed implements TemporalAccessor {
|
||||
if (cld != null && date.equals(cld) == false) {
|
||||
throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld);
|
||||
}
|
||||
} else {
|
||||
} else if (cld != null) {
|
||||
if (chrono.equals(cld.getChronology()) == false) {
|
||||
throw new DateTimeException("ChronoLocalDate must use the effective parsed chronology: " + chrono);
|
||||
}
|
||||
date = cld;
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,8 +268,8 @@ public enum ChronoUnit implements TemporalUnit {
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@Override
|
||||
public long between(Temporal temporal1, Temporal temporal2) {
|
||||
return temporal1.until(temporal2, this);
|
||||
public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) {
|
||||
return temporal1Inclusive.until(temporal2Exclusive, this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@ -684,13 +684,16 @@ public final class IsoFields {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long between(Temporal temporal1, Temporal temporal2) {
|
||||
public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) {
|
||||
if (temporal1Inclusive.getClass() != temporal2Exclusive.getClass()) {
|
||||
return temporal1Inclusive.until(temporal2Exclusive, this);
|
||||
}
|
||||
switch(this) {
|
||||
case WEEK_BASED_YEARS:
|
||||
return Math.subtractExact(temporal2.getLong(WEEK_BASED_YEAR),
|
||||
temporal1.getLong(WEEK_BASED_YEAR));
|
||||
return Math.subtractExact(temporal2Exclusive.getLong(WEEK_BASED_YEAR),
|
||||
temporal1Inclusive.getLong(WEEK_BASED_YEAR));
|
||||
case QUARTER_YEARS:
|
||||
return temporal1.until(temporal2, MONTHS) / 3;
|
||||
return temporal1Inclusive.until(temporal2Exclusive, MONTHS) / 3;
|
||||
default:
|
||||
throw new IllegalStateException("Unreachable");
|
||||
}
|
||||
|
||||
@ -170,7 +170,8 @@ public interface Temporal extends TemporalAccessor {
|
||||
* </pre>
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations must not alter either this object.
|
||||
* <p>
|
||||
* Implementations must not alter either this object or the specified temporal object.
|
||||
* Instead, an adjusted copy of the original must be returned.
|
||||
* This provides equivalent, safe behavior for immutable and mutable implementations.
|
||||
* <p>
|
||||
@ -209,7 +210,7 @@ public interface Temporal extends TemporalAccessor {
|
||||
* is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
|
||||
* passing {@code this} as the first argument.
|
||||
* <p>
|
||||
* Implementations must not alter either this object or the specified temporal object.
|
||||
* Implementations must not alter this object.
|
||||
* Instead, an adjusted copy of the original must be returned.
|
||||
* This provides equivalent, safe behavior for immutable and mutable implementations.
|
||||
*
|
||||
@ -232,16 +233,17 @@ public interface Temporal extends TemporalAccessor {
|
||||
* <p>
|
||||
* Some example code indicating how and why this method is used:
|
||||
* <pre>
|
||||
* date = date.plus(period); // add a Period instance
|
||||
* date = date.plus(duration); // add a Duration instance
|
||||
* date = date.plus(workingDays(6)); // example user-written workingDays method
|
||||
* date = date.plus(period); // add a Period instance
|
||||
* date = date.plus(duration); // add a Duration instance
|
||||
* date = date.plus(workingDays(6)); // example user-written workingDays method
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that calling {@code plus} followed by {@code minus} is not guaranteed to
|
||||
* return the same date-time.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations must not alter either this object.
|
||||
* <p>
|
||||
* Implementations must not alter either this object or the specified temporal object.
|
||||
* Instead, an adjusted copy of the original must be returned.
|
||||
* This provides equivalent, safe behavior for immutable and mutable implementations.
|
||||
* <p>
|
||||
@ -280,7 +282,7 @@ public interface Temporal extends TemporalAccessor {
|
||||
* is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
|
||||
* passing {@code this} as the first argument.
|
||||
* <p>
|
||||
* Implementations must not alter either this object or the specified temporal object.
|
||||
* Implementations must not alter this object.
|
||||
* Instead, an adjusted copy of the original must be returned.
|
||||
* This provides equivalent, safe behavior for immutable and mutable implementations.
|
||||
*
|
||||
@ -303,16 +305,17 @@ public interface Temporal extends TemporalAccessor {
|
||||
* <p>
|
||||
* Some example code indicating how and why this method is used:
|
||||
* <pre>
|
||||
* date = date.minus(period); // subtract a Period instance
|
||||
* date = date.minus(duration); // subtract a Duration instance
|
||||
* date = date.minus(workingDays(6)); // example user-written workingDays method
|
||||
* date = date.minus(period); // subtract a Period instance
|
||||
* date = date.minus(duration); // subtract a Duration instance
|
||||
* date = date.minus(workingDays(6)); // example user-written workingDays method
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that calling {@code plus} followed by {@code minus} is not guaranteed to
|
||||
* return the same date-time.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations must not alter either this object.
|
||||
* <p>
|
||||
* Implementations must not alter either this object or the specified temporal object.
|
||||
* Instead, an adjusted copy of the original must be returned.
|
||||
* This provides equivalent, safe behavior for immutable and mutable implementations.
|
||||
* <p>
|
||||
@ -345,7 +348,7 @@ public interface Temporal extends TemporalAccessor {
|
||||
* @implSpec
|
||||
* Implementations must behave in a manor equivalent to the default method behavior.
|
||||
* <p>
|
||||
* Implementations must not alter either this object or the specified temporal object.
|
||||
* Implementations must not alter this object.
|
||||
* Instead, an adjusted copy of the original must be returned.
|
||||
* This provides equivalent, safe behavior for immutable and mutable implementations.
|
||||
* <p>
|
||||
@ -371,8 +374,9 @@ public interface Temporal extends TemporalAccessor {
|
||||
* Calculates the amount of time until another temporal in terms of the specified unit.
|
||||
* <p>
|
||||
* This calculates the amount of time between two temporal objects
|
||||
* of the same type in terms of a single {@code TemporalUnit}.
|
||||
* in terms of a single {@code TemporalUnit}.
|
||||
* The start and end points are {@code this} and the specified temporal.
|
||||
* The end point is converted to be of the same type as the start point if different.
|
||||
* The result will be negative if the end is before the start.
|
||||
* For example, the period in hours between two temporal objects can be
|
||||
* calculated using {@code startTime.until(endTime, HOURS)}.
|
||||
@ -409,31 +413,36 @@ public interface Temporal extends TemporalAccessor {
|
||||
* <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>
|
||||
* In summary, implementations must behave in a manner equivalent to this code:
|
||||
* In summary, implementations must behave in a manner equivalent to this pseudo-code:
|
||||
* <pre>
|
||||
* // check input temporal is the same type as this class
|
||||
* // convert the end temporal to the same type as this class
|
||||
* if (unit instanceof ChronoUnit) {
|
||||
* // if unit is supported, then calculate and return result
|
||||
* // else throw UnsupportedTemporalTypeException for unsupported units
|
||||
* }
|
||||
* return unit.between(this, endTemporal);
|
||||
* return unit.between(this, convertedEndTemporal);
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that the unit's {@code between} method must only be invoked if the
|
||||
* two temporal objects have exactly the same type evaluated by {@code getClass()}.
|
||||
* <p>
|
||||
* Implementations must ensure that no observable state is altered when this
|
||||
* read-only method is invoked.
|
||||
*
|
||||
* @param endTemporal the end temporal, of the same type as this object, not null
|
||||
* @param endExclusive the end temporal, exclusive, converted to be of the
|
||||
* same type as this object, not null
|
||||
* @param unit the unit to measure the amount in, not null
|
||||
* @return the amount of time between this temporal object and the specified one
|
||||
* in terms of the unit; positive if the specified object is later than this one,
|
||||
* negative if it is earlier than this one
|
||||
* @throws DateTimeException if the amount cannot be calculated
|
||||
* @throws DateTimeException if the amount cannot be calculated, or the end
|
||||
* temporal cannot be converted to the same type as this temporal
|
||||
* @throws UnsupportedTemporalTypeException if the unit is not supported
|
||||
* @throws ArithmeticException if numeric overflow occurs
|
||||
*/
|
||||
long until(Temporal endTemporal, TemporalUnit unit);
|
||||
long until(Temporal endExclusive, TemporalUnit unit);
|
||||
|
||||
}
|
||||
|
||||
@ -63,7 +63,6 @@ package java.time.temporal;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.ZoneId;
|
||||
import java.time.chrono.ChronoLocalDate;
|
||||
import java.time.chrono.Chronology;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.Locale;
|
||||
@ -350,6 +349,10 @@ public interface TemporalField {
|
||||
* be acceptable for the date fields to be resolved into other {@code ChronoField}
|
||||
* instances that can produce a date, such as {@code EPOCH_DAY}.
|
||||
* <p>
|
||||
* Not all {@code TemporalAccessor} implementations are accepted as return values.
|
||||
* Implementations must accept {@code ChronoLocalDate}, {@code ChronoLocalDateTime},
|
||||
* {@code ChronoZonedDateTime} and {@code LocalTime}.
|
||||
* <p>
|
||||
* The zone is not normally required for resolution, but is provided for completeness.
|
||||
* <p>
|
||||
* The default implementation must return null.
|
||||
@ -358,13 +361,13 @@ public interface TemporalField {
|
||||
* @param chronology the effective chronology, not null
|
||||
* @param zone the effective zone, not null
|
||||
* @param resolverStyle the requested type of resolve, not null
|
||||
* @return the resolved date; null if resolving only changed the map,
|
||||
* or no resolve occurred
|
||||
* @return the resolved temporal object; null if resolving only
|
||||
* changed the map, or no resolve occurred
|
||||
* @throws ArithmeticException if numeric overflow occurs
|
||||
* @throws DateTimeException if resolving results in an error. This must not be thrown
|
||||
* by querying a field on the temporal without first checking if it is supported
|
||||
*/
|
||||
default ChronoLocalDate resolve(
|
||||
default TemporalAccessor resolve(
|
||||
Map<TemporalField, Long> fieldValues, Chronology chronology,
|
||||
ZoneId zone, ResolverStyle resolverStyle) {
|
||||
return null;
|
||||
|
||||
@ -231,7 +231,9 @@ public interface TemporalUnit {
|
||||
* Calculates the amount of time between two temporal objects.
|
||||
* <p>
|
||||
* This calculates the amount in terms of this unit. The start and end
|
||||
* points are supplied as temporal objects and must be of the same type.
|
||||
* points are supplied as temporal objects and must be of compatible types.
|
||||
* The implementation will convert the second type to be an instance of the
|
||||
* first type before the calculating the amount.
|
||||
* The result will be negative if the end is before the start.
|
||||
* For example, the amount in hours between two temporal objects can be
|
||||
* calculated using {@code HOURS.between(startTime, endTime)}.
|
||||
@ -264,15 +266,22 @@ public interface TemporalUnit {
|
||||
* If the unit is not supported an {@code UnsupportedTemporalTypeException} must be thrown.
|
||||
* Implementations must not alter the specified temporal objects.
|
||||
*
|
||||
* @param temporal1 the base temporal object, not null
|
||||
* @param temporal2 the other temporal object, not null
|
||||
* @return the amount of time between temporal1 and temporal2 in terms of this unit;
|
||||
* positive if temporal2 is later than temporal1, negative if earlier
|
||||
* @throws DateTimeException if the amount cannot be calculated
|
||||
* @implSpec
|
||||
* Implementations must begin by checking to if the two temporals have the
|
||||
* same type using {@code getClass()}. If they do not, then the result must be
|
||||
* obtained by calling {@code temporal1Inclusive.until(temporal2Exclusive, this)}.
|
||||
*
|
||||
* @param temporal1Inclusive the base temporal object, not null
|
||||
* @param temporal2Exclusive the other temporal object, exclusive, not null
|
||||
* @return the amount of time between temporal1Inclusive and temporal2Exclusive
|
||||
* in terms of this unit; positive if temporal2Exclusive is later than
|
||||
* temporal1Inclusive, negative if earlier
|
||||
* @throws DateTimeException if the amount cannot be calculated, or the end
|
||||
* temporal cannot be converted to the same type as the start temporal
|
||||
* @throws UnsupportedTemporalTypeException if the unit is not supported by the temporal
|
||||
* @throws ArithmeticException if numeric overflow occurs
|
||||
*/
|
||||
long between(Temporal temporal1, Temporal temporal2);
|
||||
long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
|
||||
@ -61,6 +61,7 @@
|
||||
*/
|
||||
package java.time.temporal;
|
||||
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.Serializable;
|
||||
import java.time.DateTimeException;
|
||||
|
||||
@ -337,6 +338,21 @@ public final class ValueRange implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ValueRange for the serialized values.
|
||||
* The values are validated according to the constraints of the {@link #of}
|
||||
* factory method.
|
||||
* @return the ValueRange for the serialized fields
|
||||
* @throws InvalidObjectException if the serialized object has invalid values
|
||||
*/
|
||||
private Object readResolve() throws InvalidObjectException {
|
||||
try {
|
||||
return of(minSmallest, minLargest, maxSmallest, maxLargest);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new InvalidObjectException("Invalid serialized ValueRange: " + iae.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Checks if this range is equal to another range.
|
||||
|
||||
@ -1583,6 +1583,7 @@ public class Arrays {
|
||||
* @since 1.8
|
||||
*/
|
||||
public static <T> void parallelPrefix(T[] array, BinaryOperator<T> op) {
|
||||
Objects.requireNonNull(op);
|
||||
if (array.length > 0)
|
||||
new ArrayPrefixHelpers.CumulateTask<>
|
||||
(null, op, array, 0, array.length).invoke();
|
||||
@ -1606,6 +1607,7 @@ public class Arrays {
|
||||
*/
|
||||
public static <T> void parallelPrefix(T[] array, int fromIndex,
|
||||
int toIndex, BinaryOperator<T> op) {
|
||||
Objects.requireNonNull(op);
|
||||
rangeCheck(array.length, fromIndex, toIndex);
|
||||
if (fromIndex < toIndex)
|
||||
new ArrayPrefixHelpers.CumulateTask<>
|
||||
@ -1627,6 +1629,7 @@ public class Arrays {
|
||||
* @since 1.8
|
||||
*/
|
||||
public static void parallelPrefix(long[] array, LongBinaryOperator op) {
|
||||
Objects.requireNonNull(op);
|
||||
if (array.length > 0)
|
||||
new ArrayPrefixHelpers.LongCumulateTask
|
||||
(null, op, array, 0, array.length).invoke();
|
||||
@ -1649,6 +1652,7 @@ public class Arrays {
|
||||
*/
|
||||
public static void parallelPrefix(long[] array, int fromIndex,
|
||||
int toIndex, LongBinaryOperator op) {
|
||||
Objects.requireNonNull(op);
|
||||
rangeCheck(array.length, fromIndex, toIndex);
|
||||
if (fromIndex < toIndex)
|
||||
new ArrayPrefixHelpers.LongCumulateTask
|
||||
@ -1673,6 +1677,7 @@ public class Arrays {
|
||||
* @since 1.8
|
||||
*/
|
||||
public static void parallelPrefix(double[] array, DoubleBinaryOperator op) {
|
||||
Objects.requireNonNull(op);
|
||||
if (array.length > 0)
|
||||
new ArrayPrefixHelpers.DoubleCumulateTask
|
||||
(null, op, array, 0, array.length).invoke();
|
||||
@ -1695,6 +1700,7 @@ public class Arrays {
|
||||
*/
|
||||
public static void parallelPrefix(double[] array, int fromIndex,
|
||||
int toIndex, DoubleBinaryOperator op) {
|
||||
Objects.requireNonNull(op);
|
||||
rangeCheck(array.length, fromIndex, toIndex);
|
||||
if (fromIndex < toIndex)
|
||||
new ArrayPrefixHelpers.DoubleCumulateTask
|
||||
@ -1716,6 +1722,7 @@ public class Arrays {
|
||||
* @since 1.8
|
||||
*/
|
||||
public static void parallelPrefix(int[] array, IntBinaryOperator op) {
|
||||
Objects.requireNonNull(op);
|
||||
if (array.length > 0)
|
||||
new ArrayPrefixHelpers.IntCumulateTask
|
||||
(null, op, array, 0, array.length).invoke();
|
||||
@ -1738,6 +1745,7 @@ public class Arrays {
|
||||
*/
|
||||
public static void parallelPrefix(int[] array, int fromIndex,
|
||||
int toIndex, IntBinaryOperator op) {
|
||||
Objects.requireNonNull(op);
|
||||
rangeCheck(array.length, fromIndex, toIndex);
|
||||
if (fromIndex < toIndex)
|
||||
new ArrayPrefixHelpers.IntCumulateTask
|
||||
|
||||
@ -1937,7 +1937,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
|
||||
/**
|
||||
* Sets the values for the fields <code>YEAR</code>, <code>MONTH</code>,
|
||||
* <code>DAY_OF_MONTH</code>, <code>HOUR</code>, <code>MINUTE</code>, and
|
||||
* <code>DAY_OF_MONTH</code>, <code>HOUR_OF_DAY</code>, <code>MINUTE</code>, and
|
||||
* <code>SECOND</code>.
|
||||
* Previous values of other fields are retained. If this is not desired,
|
||||
* call {@link #clear()} first.
|
||||
|
||||
@ -104,6 +104,13 @@ import java.util.stream.StreamSupport;
|
||||
* the specified behavior of underlying {@link Object} methods wherever the
|
||||
* implementor deems it appropriate.
|
||||
*
|
||||
* <p>Some collection operations which perform recursive traversal of the
|
||||
* collection may fail with an exception for self-referential instances where
|
||||
* the collection directly or indirectly contains itself. This includes the
|
||||
* {@code clone()}, {@code equals()}, {@code hashCode()} and {@code toString()}
|
||||
* methods. Implementations may optionally handle the self-referential scenario,
|
||||
* however most current implementations do not do so.
|
||||
*
|
||||
* <p>This interface is a member of the
|
||||
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
|
||||
* Java Collections Framework</a>.
|
||||
@ -379,8 +386,8 @@ public interface Collection<E> extends Iterable<E> {
|
||||
|
||||
/**
|
||||
* Removes all of the elements of this collection that satisfy the given
|
||||
* predicate. Errors or runtime exceptions thrown by the predicate are
|
||||
* relayed to the caller.
|
||||
* predicate. Errors or runtime exceptions thrown during iteration or by
|
||||
* the predicate are relayed to the caller.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation traverses all elements of the collection using
|
||||
@ -393,9 +400,10 @@ public interface Collection<E> extends Iterable<E> {
|
||||
* removed
|
||||
* @return {@code true} if any elements were removed
|
||||
* @throws NullPointerException if the specified filter is null
|
||||
* @throws UnsupportedOperationException if the {@code remove}
|
||||
* method is not supported by this collection's
|
||||
* {@link #iterator}
|
||||
* @throws UnsupportedOperationException if elements cannot be removed
|
||||
* from this collection. Implementations may throw this exception if a
|
||||
* matching element cannot be removed or if, in general, removal is not
|
||||
* supported.
|
||||
* @since 1.8
|
||||
*/
|
||||
default boolean removeIf(Predicate<? super E> filter) {
|
||||
@ -502,12 +510,10 @@ public interface Collection<E> extends Iterable<E> {
|
||||
/**
|
||||
* Creates a {@link Spliterator} over the elements in this collection.
|
||||
*
|
||||
* <p>The returned {@code Spliterator} must report the characteristic
|
||||
* {@link Spliterator#SIZED}; implementations should document any additional
|
||||
* characteristic values reported by the returned spliterator. If
|
||||
* this collection contains no elements then the returned spliterator is
|
||||
* only required to report {@link Spliterator#SIZED} and is not required to
|
||||
* report additional characteristic values (if any).
|
||||
* Implementations should document characteristic values reported by the
|
||||
* spliterator. Such characteristic values are not required to be reported
|
||||
* if the spliterator reports {@link Spliterator#SIZED} and this collection
|
||||
* contains no elements.
|
||||
*
|
||||
* <p>The default implementation should be overridden by subclasses that
|
||||
* can return a more efficient spliterator. In order to
|
||||
@ -533,9 +539,11 @@ public interface Collection<E> extends Iterable<E> {
|
||||
* <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
|
||||
* from the collections's {@code Iterator}. The spliterator inherits the
|
||||
* <em>fail-fast</em> properties of the collection's iterator.
|
||||
* <p>
|
||||
* The created {@code Spliterator} reports {@link Spliterator#SIZED}.
|
||||
*
|
||||
* @implNote
|
||||
* The returned {@code Spliterator} additionally reports
|
||||
* The created {@code Spliterator} additionally reports
|
||||
* {@link Spliterator#SUBSIZED}.
|
||||
*
|
||||
* <p>If a spliterator covers no elements then the reporting of additional
|
||||
|
||||
@ -41,11 +41,8 @@ package java.util;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.chrono.IsoChronology;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalQuery;
|
||||
import sun.util.calendar.BaseCalendar;
|
||||
import sun.util.calendar.CalendarDate;
|
||||
import sun.util.calendar.CalendarSystem;
|
||||
@ -867,6 +864,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* <code>false</code> otherwise.
|
||||
* @see Calendar#compareTo(Calendar)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof GregorianCalendar &&
|
||||
super.equals(obj) &&
|
||||
@ -876,6 +874,7 @@ public class GregorianCalendar extends Calendar {
|
||||
/**
|
||||
* Generates the hash code for this <code>GregorianCalendar</code> object.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ (int)gregorianCutoverDate;
|
||||
}
|
||||
@ -908,6 +907,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* or if any calendar fields have out-of-range values in
|
||||
* non-lenient mode.
|
||||
*/
|
||||
@Override
|
||||
public void add(int field, int amount) {
|
||||
// If amount == 0, do nothing even the given field is out of
|
||||
// range. This is tested by JCK.
|
||||
@ -1106,6 +1106,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #add(int,int)
|
||||
* @see #set(int,int)
|
||||
*/
|
||||
@Override
|
||||
public void roll(int field, boolean up) {
|
||||
roll(field, up ? +1 : -1);
|
||||
}
|
||||
@ -1154,6 +1155,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #set(int,int)
|
||||
* @since 1.2
|
||||
*/
|
||||
@Override
|
||||
public void roll(int field, int amount) {
|
||||
// If amount == 0, do nothing even the given field is out of
|
||||
// range. This is tested by JCK.
|
||||
@ -1272,25 +1274,44 @@ public class GregorianCalendar extends Calendar {
|
||||
int woy = internalGet(WEEK_OF_YEAR);
|
||||
int value = woy + amount;
|
||||
if (!isCutoverYear(y)) {
|
||||
// If the new value is in between min and max
|
||||
// (exclusive), then we can use the value.
|
||||
if (value > min && value < max) {
|
||||
set(WEEK_OF_YEAR, value);
|
||||
return;
|
||||
}
|
||||
long fd = getCurrentFixedDate();
|
||||
// Make sure that the min week has the current DAY_OF_WEEK
|
||||
long day1 = fd - (7 * (woy - min));
|
||||
if (calsys.getYearFromFixedDate(day1) != y) {
|
||||
min++;
|
||||
}
|
||||
int weekYear = getWeekYear();
|
||||
if (weekYear == y) {
|
||||
// If the new value is in between min and max
|
||||
// (exclusive), then we can use the value.
|
||||
if (value > min && value < max) {
|
||||
set(WEEK_OF_YEAR, value);
|
||||
return;
|
||||
}
|
||||
long fd = getCurrentFixedDate();
|
||||
// Make sure that the min week has the current DAY_OF_WEEK
|
||||
// in the calendar year
|
||||
long day1 = fd - (7 * (woy - min));
|
||||
if (calsys.getYearFromFixedDate(day1) != y) {
|
||||
min++;
|
||||
}
|
||||
|
||||
// Make sure the same thing for the max week
|
||||
fd += 7 * (max - internalGet(WEEK_OF_YEAR));
|
||||
if (calsys.getYearFromFixedDate(fd) != y) {
|
||||
max--;
|
||||
// Make sure the same thing for the max week
|
||||
fd += 7 * (max - internalGet(WEEK_OF_YEAR));
|
||||
if (calsys.getYearFromFixedDate(fd) != y) {
|
||||
max--;
|
||||
}
|
||||
} else {
|
||||
// When WEEK_OF_YEAR and YEAR are out of sync,
|
||||
// adjust woy and amount to stay in the calendar year.
|
||||
if (weekYear > y) {
|
||||
if (amount < 0) {
|
||||
amount++;
|
||||
}
|
||||
woy = max;
|
||||
} else {
|
||||
if (amount > 0) {
|
||||
amount -= woy - max;
|
||||
}
|
||||
woy = min;
|
||||
}
|
||||
}
|
||||
break;
|
||||
set(field, getRolledValue(woy, amount, min, max));
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle cutover here.
|
||||
@ -1510,6 +1531,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMinimum(int)
|
||||
* @see #getActualMaximum(int)
|
||||
*/
|
||||
@Override
|
||||
public int getMinimum(int field) {
|
||||
return MIN_VALUES[field];
|
||||
}
|
||||
@ -1533,6 +1555,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMinimum(int)
|
||||
* @see #getActualMaximum(int)
|
||||
*/
|
||||
@Override
|
||||
public int getMaximum(int field) {
|
||||
switch (field) {
|
||||
case MONTH:
|
||||
@ -1581,6 +1604,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMinimum(int)
|
||||
* @see #getActualMaximum(int)
|
||||
*/
|
||||
@Override
|
||||
public int getGreatestMinimum(int field) {
|
||||
if (field == DAY_OF_MONTH) {
|
||||
BaseCalendar.Date d = getGregorianCutoverDate();
|
||||
@ -1610,6 +1634,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMinimum(int)
|
||||
* @see #getActualMaximum(int)
|
||||
*/
|
||||
@Override
|
||||
public int getLeastMaximum(int field) {
|
||||
switch (field) {
|
||||
case MONTH:
|
||||
@ -1659,6 +1684,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMaximum(int)
|
||||
* @since 1.2
|
||||
*/
|
||||
@Override
|
||||
public int getActualMinimum(int field) {
|
||||
if (field == DAY_OF_MONTH) {
|
||||
GregorianCalendar gc = getNormalizedCalendar();
|
||||
@ -1702,6 +1728,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMinimum(int)
|
||||
* @since 1.2
|
||||
*/
|
||||
@Override
|
||||
public int getActualMaximum(int field) {
|
||||
final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
|
||||
HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
|
||||
@ -1970,6 +1997,7 @@ public class GregorianCalendar extends Calendar {
|
||||
(internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone()
|
||||
{
|
||||
GregorianCalendar other = (GregorianCalendar) super.clone();
|
||||
@ -1987,6 +2015,7 @@ public class GregorianCalendar extends Calendar {
|
||||
return other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZone getTimeZone() {
|
||||
TimeZone zone = super.getTimeZone();
|
||||
// To share the zone by CalendarDates
|
||||
@ -1997,6 +2026,7 @@ public class GregorianCalendar extends Calendar {
|
||||
return zone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeZone(TimeZone zone) {
|
||||
super.setTimeZone(zone);
|
||||
// To share the zone by CalendarDates
|
||||
@ -2227,6 +2257,7 @@ public class GregorianCalendar extends Calendar {
|
||||
* @see #getActualMaximum(int)
|
||||
* @since 1.7
|
||||
*/
|
||||
@Override
|
||||
public int getWeeksInWeekYear() {
|
||||
GregorianCalendar gc = getNormalizedCalendar();
|
||||
int weekYear = gc.getWeekYear();
|
||||
@ -2262,8 +2293,9 @@ public class GregorianCalendar extends Calendar {
|
||||
*
|
||||
* @see Calendar#complete
|
||||
*/
|
||||
@Override
|
||||
protected void computeFields() {
|
||||
int mask = 0;
|
||||
int mask;
|
||||
if (isPartiallyNormalized()) {
|
||||
// Determine which calendar fields need to be computed.
|
||||
mask = getSetStateFields();
|
||||
@ -2598,6 +2630,7 @@ public class GregorianCalendar extends Calendar {
|
||||
*
|
||||
* @exception IllegalArgumentException if any calendar fields are invalid.
|
||||
*/
|
||||
@Override
|
||||
protected void computeTime() {
|
||||
// In non-lenient mode, perform brief checking of calendar
|
||||
// fields which have been set externally. Through this
|
||||
|
||||
@ -396,11 +396,13 @@ public interface List<E> extends Collection<E> {
|
||||
* replacing the first element.
|
||||
*
|
||||
* @param operator the operator to apply to each element
|
||||
* @throws UnsupportedOperationException if the {@code set}
|
||||
* operation is not supported by this list
|
||||
* @throws UnsupportedOperationException if this list is unmodifiable.
|
||||
* Implementations may throw this exception if an element
|
||||
* cannot be replaced or if, in general, modification is not
|
||||
* supported
|
||||
* @throws NullPointerException if the specified operator is null or
|
||||
* if the element is replaced with a null value and this list
|
||||
* does not permit null elements
|
||||
* if the operator result is a null value and this list does
|
||||
* not permit null elements
|
||||
* (<a href="Collection.html#optional-restrictions">optional</a>)
|
||||
* @since 1.8
|
||||
*/
|
||||
@ -685,4 +687,3 @@ public interface List<E> extends Collection<E> {
|
||||
return Spliterators.spliterator(this, Spliterator.ORDERED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -86,10 +86,6 @@ import java.io.Serializable;
|
||||
* Such exceptions are marked as "optional" in the specification for this
|
||||
* interface.
|
||||
*
|
||||
* <p>This interface is a member of the
|
||||
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
|
||||
* Java Collections Framework</a>.
|
||||
*
|
||||
* <p>Many methods in Collections Framework interfaces are defined
|
||||
* in terms of the {@link Object#equals(Object) equals} method. For
|
||||
* example, the specification for the {@link #containsKey(Object)
|
||||
@ -107,6 +103,17 @@ import java.io.Serializable;
|
||||
* the specified behavior of underlying {@link Object} methods wherever the
|
||||
* implementor deems it appropriate.
|
||||
*
|
||||
* <p>Some map operations which perform recursive traversal of the map may fail
|
||||
* with an exception for self-referential instances where the map directly or
|
||||
* indirectly contains itself. This includes the {@code clone()},
|
||||
* {@code equals()}, {@code hashCode()} and {@code toString()} methods.
|
||||
* Implementations may optionally handle the self-referential scenario, however
|
||||
* most current implementations do not do so.
|
||||
*
|
||||
* <p>This interface is a member of the
|
||||
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
|
||||
* Java Collections Framework</a>.
|
||||
*
|
||||
* @param <K> the type of keys maintained by this map
|
||||
* @param <V> the type of mapped values
|
||||
*
|
||||
|
||||
@ -85,6 +85,7 @@ public final class Optional<T> {
|
||||
* Constructs an instance with the value present.
|
||||
*
|
||||
* @param value the non-null value to be present
|
||||
* @throws NullPointerException if value is null
|
||||
*/
|
||||
private Optional(T value) {
|
||||
this.value = Objects.requireNonNull(value);
|
||||
@ -96,6 +97,7 @@ public final class Optional<T> {
|
||||
* @param <T> the class of the value
|
||||
* @param value the value to be present, which must be non-null
|
||||
* @return an {@code Optional} with the value present
|
||||
* @throws NullPointerException if value is null
|
||||
*/
|
||||
public static <T> Optional<T> of(T value) {
|
||||
return new Optional<>(value);
|
||||
|
||||
@ -89,7 +89,7 @@ class Random implements java.io.Serializable {
|
||||
private static final long addend = 0xBL;
|
||||
private static final long mask = (1L << 48) - 1;
|
||||
|
||||
private static final double DOUBLE_UNIT = 1.0 / (1L << 53);
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
|
||||
|
||||
// IllegalArgumentException messages
|
||||
static final String BadBound = "bound must be positive";
|
||||
|
||||
@ -386,15 +386,18 @@ public interface Set<E> extends Collection<E> {
|
||||
/**
|
||||
* Creates a {@code Spliterator} over the elements in this set.
|
||||
*
|
||||
* <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
|
||||
* {@link Spliterator#DISTINCT}. Implementations should document the
|
||||
* reporting of additional characteristic values.
|
||||
* <p>The {@code Spliterator} reports {@link Spliterator#DISTINCT}.
|
||||
* Implementations should document the reporting of additional
|
||||
* characteristic values.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation creates a
|
||||
* <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
|
||||
* from the set's {@code Iterator}. The spliterator inherits the
|
||||
* <em>fail-fast</em> properties of the set's iterator.
|
||||
* <p>
|
||||
* The created {@code Spliterator} additionally reports
|
||||
* {@link Spliterator#SIZED}.
|
||||
*
|
||||
* @implNote
|
||||
* The created {@code Spliterator} additionally reports
|
||||
|
||||
@ -223,10 +223,10 @@ public interface SortedSet<E> extends Set<E> {
|
||||
/**
|
||||
* Creates a {@code Spliterator} over the elements in this sorted set.
|
||||
*
|
||||
* <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
|
||||
* {@link Spliterator#DISTINCT}, {@link Spliterator#SORTED} and
|
||||
* {@link Spliterator#ORDERED}. Implementations should document the
|
||||
* reporting of additional characteristic values.
|
||||
* <p>The {@code Spliterator} reports {@link Spliterator#DISTINCT},
|
||||
* {@link Spliterator#SORTED} and {@link Spliterator#ORDERED}.
|
||||
* Implementations should document the reporting of additional
|
||||
* characteristic values.
|
||||
*
|
||||
* <p>The spliterator's comparator (see
|
||||
* {@link java.util.Spliterator#getComparator()}) must be {@code null} if
|
||||
@ -240,6 +240,9 @@ public interface SortedSet<E> extends Set<E> {
|
||||
* from the sorted set's {@code Iterator}. The spliterator inherits the
|
||||
* <em>fail-fast</em> properties of the set's iterator. The
|
||||
* spliterator's comparator is the same as the sorted set's comparator.
|
||||
* <p>
|
||||
* The created {@code Spliterator} additionally reports
|
||||
* {@link Spliterator#SIZED}.
|
||||
*
|
||||
* @implNote
|
||||
* The created {@code Spliterator} additionally reports
|
||||
|
||||
@ -107,29 +107,25 @@ public final class SplittableRandom {
|
||||
* Methods nextLong, nextInt, and derivatives do not return the
|
||||
* sequence (seed) values, but instead a hash-like bit-mix of
|
||||
* their bits, producing more independently distributed sequences.
|
||||
* For nextLong, the mix64 bit-mixing function computes the same
|
||||
* value as the "64-bit finalizer" function in Austin Appleby's
|
||||
* MurmurHash3 algorithm. See
|
||||
* http://code.google.com/p/smhasher/wiki/MurmurHash3 , which
|
||||
* comments: "The constants for the finalizers were generated by a
|
||||
* simple simulated-annealing algorithm, and both avalanche all
|
||||
* bits of 'h' to within 0.25% bias." The mix32 function is
|
||||
* equivalent to (int)(mix64(seed) >>> 32), but faster because it
|
||||
* omits a step that doesn't contribute to result.
|
||||
* For nextLong, the mix64 function is based on David Stafford's
|
||||
* (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
|
||||
* "Mix13" variant of the "64-bit finalizer" function in Austin
|
||||
* Appleby's MurmurHash3 algorithm (see
|
||||
* http://code.google.com/p/smhasher/wiki/MurmurHash3). The mix32
|
||||
* function is based on Stafford's Mix04 mix function, but returns
|
||||
* the upper 32 bits cast as int.
|
||||
*
|
||||
* The split operation uses the current generator to form the seed
|
||||
* and gamma for another SplittableRandom. To conservatively
|
||||
* avoid potential correlations between seed and value generation,
|
||||
* gamma selection (method nextGamma) uses the "Mix13" constants
|
||||
* for MurmurHash3 described by David Stafford
|
||||
* (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
|
||||
* To avoid potential weaknesses in bit-mixing transformations, we
|
||||
* restrict gammas to odd values with at least 12 and no more than
|
||||
* 52 bits set. Rather than rejecting candidates with too few or
|
||||
* too many bits set, method nextGamma flips some bits (which has
|
||||
* the effect of mapping at most 4 to any given gamma value).
|
||||
* This reduces the effective set of 64bit odd gamma values by
|
||||
* about 2<sup>14</sup>, a very tiny percentage, and serves as an
|
||||
* gamma selection (method mixGamma) uses different
|
||||
* (Murmurhash3's) mix constants. To avoid potential weaknesses
|
||||
* in bit-mixing transformations, we restrict gammas to odd values
|
||||
* with at least 24 0-1 or 1-0 bit transitions. Rather than
|
||||
* rejecting candidates with too few or too many bits set, method
|
||||
* mixGamma flips some bits (which has the effect of mapping at
|
||||
* most 4 to any given gamma value). This reduces the effective
|
||||
* set of 64bit odd gamma values by about 2%, and serves as an
|
||||
* automated screening for sequence constant selection that is
|
||||
* left as an empirical decision in some other hashing and crypto
|
||||
* algorithms.
|
||||
@ -140,14 +136,15 @@ public final class SplittableRandom {
|
||||
* avalanching.
|
||||
*
|
||||
* The default (no-argument) constructor, in essence, invokes
|
||||
* split() for a common "seeder" SplittableRandom. Unlike other
|
||||
* cases, this split must be performed in a thread-safe manner, so
|
||||
* we use an AtomicLong to represent the seed rather than use an
|
||||
* explicit SplittableRandom. To bootstrap the seeder, we start
|
||||
* off using a seed based on current time and host unless the
|
||||
* java.util.secureRandomSeed property is set. This serves as a
|
||||
* slimmed-down (and insecure) variant of SecureRandom that also
|
||||
* avoids stalls that may occur when using /dev/random.
|
||||
* split() for a common "defaultGen" SplittableRandom. Unlike
|
||||
* other cases, this split must be performed in a thread-safe
|
||||
* manner, so we use an AtomicLong to represent the seed rather
|
||||
* than use an explicit SplittableRandom. To bootstrap the
|
||||
* defaultGen, we start off using a seed based on current time and
|
||||
* network interface address unless the java.util.secureRandomSeed
|
||||
* property is set. This serves as a slimmed-down (and insecure)
|
||||
* variant of SecureRandom that also avoids stalls that may occur
|
||||
* when using /dev/random.
|
||||
*
|
||||
* It is a relatively simple matter to apply the basic design here
|
||||
* to use 128 bit seeds. However, emulating 128bit arithmetic and
|
||||
@ -160,17 +157,16 @@ public final class SplittableRandom {
|
||||
*/
|
||||
|
||||
/**
|
||||
* The initial gamma value for (unsplit) SplittableRandoms. Must
|
||||
* be odd with at least 12 and no more than 52 bits set. Currently
|
||||
* set to the golden ratio scaled to 64bits.
|
||||
* The golden ratio scaled to 64bits, used as the initial gamma
|
||||
* value for (unsplit) SplittableRandoms.
|
||||
*/
|
||||
private static final long INITIAL_GAMMA = 0x9e3779b97f4a7c15L;
|
||||
private static final long GOLDEN_GAMMA = 0x9e3779b97f4a7c15L;
|
||||
|
||||
/**
|
||||
* The least non-zero value returned by nextDouble(). This value
|
||||
* is scaled by a random value of 53 bits to produce a result.
|
||||
*/
|
||||
private static final double DOUBLE_UNIT = 1.0 / (1L << 53);
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53);
|
||||
|
||||
/**
|
||||
* The seed. Updated only via method nextSeed.
|
||||
@ -191,31 +187,31 @@ public final class SplittableRandom {
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes MurmurHash3 64bit mix function.
|
||||
* Computes Stafford variant 13 of 64bit mix function.
|
||||
*/
|
||||
private static long mix64(long z) {
|
||||
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
|
||||
z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
|
||||
return z ^ (z >>> 33);
|
||||
z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
|
||||
z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
|
||||
return z ^ (z >>> 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 32 high bits of mix64(z) as int.
|
||||
* Returns the 32 high bits of Stafford variant 4 mix64 function as int.
|
||||
*/
|
||||
private static int mix32(long z) {
|
||||
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
|
||||
return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
|
||||
z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L;
|
||||
return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the gamma value to use for a new split instance.
|
||||
*/
|
||||
private static long nextGamma(long z) {
|
||||
z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L; // Stafford "Mix13"
|
||||
z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
|
||||
z = (z ^ (z >>> 31)) | 1L; // force to be odd
|
||||
int n = Long.bitCount(z); // ensure enough 0 and 1 bits
|
||||
return (n < 12 || n > 52) ? z ^ 0xaaaaaaaaaaaaaaaaL : z;
|
||||
private static long mixGamma(long z) {
|
||||
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; // MurmurHash3 mix constants
|
||||
z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
|
||||
z = (z ^ (z >>> 33)) | 1L; // force to be odd
|
||||
int n = Long.bitCount(z ^ (z >>> 1)); // ensure enough transitions
|
||||
return (n < 24) ? z ^ 0xaaaaaaaaaaaaaaaaL : z;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +224,7 @@ public final class SplittableRandom {
|
||||
/**
|
||||
* The seed generator for default constructors.
|
||||
*/
|
||||
private static final AtomicLong seeder = new AtomicLong(initialSeed());
|
||||
private static final AtomicLong defaultGen = new AtomicLong(initialSeed());
|
||||
|
||||
private static long initialSeed() {
|
||||
String pp = java.security.AccessController.doPrivileged(
|
||||
@ -396,7 +392,7 @@ public final class SplittableRandom {
|
||||
* @param seed the initial seed
|
||||
*/
|
||||
public SplittableRandom(long seed) {
|
||||
this(seed, INITIAL_GAMMA);
|
||||
this(seed, GOLDEN_GAMMA);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,8 +401,10 @@ public final class SplittableRandom {
|
||||
* of those of any other instances in the current program; and
|
||||
* may, and typically does, vary across program invocations.
|
||||
*/
|
||||
public SplittableRandom() { // emulate seeder.split()
|
||||
this.gamma = nextGamma(this.seed = seeder.addAndGet(INITIAL_GAMMA));
|
||||
public SplittableRandom() { // emulate defaultGen.split()
|
||||
long s = defaultGen.getAndAdd(2 * GOLDEN_GAMMA);
|
||||
this.seed = mix64(s);
|
||||
this.gamma = mixGamma(s + GOLDEN_GAMMA);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -424,8 +422,7 @@ public final class SplittableRandom {
|
||||
* @return the new SplittableRandom instance
|
||||
*/
|
||||
public SplittableRandom split() {
|
||||
long s = nextSeed();
|
||||
return new SplittableRandom(s, nextGamma(s));
|
||||
return new SplittableRandom(nextLong(), mixGamma(nextSeed()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -194,8 +194,8 @@ public class ThreadLocalRandom extends Random {
|
||||
private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
|
||||
|
||||
// Constants from SplittableRandom
|
||||
private static final double DOUBLE_UNIT = 1.0 / (1L << 53);
|
||||
private static final float FLOAT_UNIT = 1.0f / (1 << 24);
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
|
||||
private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
|
||||
|
||||
/** Rarely-used holder for the second of a pair of Gaussians */
|
||||
private static final ThreadLocal<Double> nextLocalGaussian =
|
||||
|
||||
@ -149,7 +149,9 @@ interface Node<T> {
|
||||
/**
|
||||
* Copies the content of this {@code Node} into an array, starting at a
|
||||
* given offset into the array. It is the caller's responsibility to ensure
|
||||
* there is sufficient room in the array.
|
||||
* there is sufficient room in the array, otherwise unspecified behaviour
|
||||
* will occur if the array length is less than the number of elements
|
||||
* contained in this node.
|
||||
*
|
||||
* @param array the array into which to copy the contents of this
|
||||
* {@code Node}
|
||||
@ -258,6 +260,12 @@ interface Node<T> {
|
||||
*/
|
||||
@Override
|
||||
default T[] asArray(IntFunction<T[]> generator) {
|
||||
if (java.util.stream.Tripwire.ENABLED)
|
||||
java.util.stream.Tripwire.trip(getClass(), "{0} calling Node.OfPrimitive.asArray");
|
||||
|
||||
long size = count();
|
||||
if (size >= Nodes.MAX_ARRAY_SIZE)
|
||||
throw new IllegalArgumentException(Nodes.BAD_SIZE);
|
||||
T[] boxed = generator.apply((int) count());
|
||||
copyInto(boxed, 0);
|
||||
return boxed;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user