Merge
This commit is contained in:
commit
cda19943f0
jdk
.hgignore
make
copy
data/tzdata
src
java.base
share/classes
java
lang
Integer.javaLong.javaString.java
invoke
BoundMethodHandle.javaCallSite.javaDelegatingMethodHandle.javaDirectMethodHandle.javaInvokerBytecodeGenerator.javaInvokers.javaLambdaForm.javaLambdaFormBuffer.javaLambdaFormEditor.javaMemberName.javaMethodHandle.javaMethodHandleImpl.javaMethodHandleProxies.javaMethodHandleStatics.javaMethodHandles.javaMethodType.javaMethodTypeForm.javaSimpleMethodHandle.java
reflect
net
time
util
sun
invoke/util
reflect
AccessorGenerator.javaMethodAccessorGenerator.javaUnsafeBooleanFieldAccessorImpl.javaUnsafeByteFieldAccessorImpl.javaUnsafeCharacterFieldAccessorImpl.javaUnsafeDoubleFieldAccessorImpl.javaUnsafeFloatFieldAccessorImpl.javaUnsafeIntegerFieldAccessorImpl.javaUnsafeLongFieldAccessorImpl.javaUnsafeQualifiedBooleanFieldAccessorImpl.javaUnsafeQualifiedByteFieldAccessorImpl.javaUnsafeQualifiedCharacterFieldAccessorImpl.javaUnsafeQualifiedDoubleFieldAccessorImpl.javaUnsafeQualifiedFloatFieldAccessorImpl.javaUnsafeQualifiedIntegerFieldAccessorImpl.javaUnsafeQualifiedLongFieldAccessorImpl.javaUnsafeQualifiedShortFieldAccessorImpl.javaUnsafeQualifiedStaticBooleanFieldAccessorImpl.javaUnsafeQualifiedStaticByteFieldAccessorImpl.javaUnsafeQualifiedStaticCharacterFieldAccessorImpl.javaUnsafeQualifiedStaticDoubleFieldAccessorImpl.javaUnsafeQualifiedStaticFloatFieldAccessorImpl.javaUnsafeQualifiedStaticIntegerFieldAccessorImpl.javaUnsafeQualifiedStaticLongFieldAccessorImpl.javaUnsafeQualifiedStaticShortFieldAccessorImpl.javaUnsafeShortFieldAccessorImpl.javaUnsafeStaticBooleanFieldAccessorImpl.javaUnsafeStaticByteFieldAccessorImpl.javaUnsafeStaticCharacterFieldAccessorImpl.javaUnsafeStaticDoubleFieldAccessorImpl.javaUnsafeStaticFloatFieldAccessorImpl.javaUnsafeStaticIntegerFieldAccessorImpl.javaUnsafeStaticLongFieldAccessorImpl.javaUnsafeStaticShortFieldAccessorImpl.java
util
windows
classes/sun/nio/fs
native
java.management/share/classes/javax/management/loading
@ -1,5 +1,6 @@
|
||||
^build/
|
||||
^dist/
|
||||
^webrev
|
||||
^testoutput/
|
||||
/nbproject/private/
|
||||
^make/netbeans/.*/build/
|
||||
|
@ -208,20 +208,6 @@ BASE_CONF_FILES += $(CACERTS_DST)
|
||||
|
||||
################################################################################
|
||||
|
||||
ifeq ($(OPENJDK_TARGET_OS), solaris)
|
||||
|
||||
SUNPKCS11_CFG_SRC := $(JDK_TOPDIR)/src/java.base/share/conf/security/sunpkcs11-solaris.cfg
|
||||
SUNPKCS11_CFG_DST := $(JDK_OUTPUTDIR)/lib/security/sunpkcs11-solaris.cfg
|
||||
|
||||
$(SUNPKCS11_CFG_DST): $(SUNPKCS11_CFG_SRC)
|
||||
$(call install-file)
|
||||
|
||||
BASE_CONF_FILES += $(SUNPKCS11_CFG_DST)
|
||||
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
|
||||
$(JDK_OUTPUTDIR)/lib/net.properties: $(JDK_TOPDIR)/src/java.base/share/conf/net.properties
|
||||
$(ECHO) $(LOG_INFO) Copying $(@F)
|
||||
$(call install-file)
|
||||
|
50
jdk/make/copy/Copy-jdk.crypto.pkcs11.gmk
Normal file
50
jdk/make/copy/Copy-jdk.crypto.pkcs11.gmk
Normal file
@ -0,0 +1,50 @@
|
||||
#
|
||||
# Copyright (c) 2014, 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.
|
||||
#
|
||||
|
||||
include CopyCommon.gmk
|
||||
|
||||
################################################################################
|
||||
|
||||
ifeq ($(OPENJDK_TARGET_OS), solaris)
|
||||
|
||||
SUNPKCS11_CFG_SRC := \
|
||||
$(JDK_TOPDIR)/src/jdk.crypto.pkcs11/solaris/conf/security/sunpkcs11-solaris.cfg
|
||||
SUNPKCS11_CFG_DST := $(JDK_OUTPUTDIR)/lib/security/sunpkcs11-solaris.cfg
|
||||
|
||||
$(SUNPKCS11_CFG_DST): $(SUNPKCS11_CFG_SRC)
|
||||
$(call install-file)
|
||||
|
||||
SECURITY_PKCS11_CONF_FILES += $(SUNPKCS11_CFG_DST)
|
||||
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
|
||||
jdk.crypto.pkcs11: $(SECURITY_PKCS11_CONF_FILES)
|
||||
|
||||
all: jdk.crypto.pkcs11
|
||||
|
||||
.PHONY: all jdk.crypto.pkcs11
|
||||
|
@ -1,24 +1,24 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
tzdata2014c
|
||||
tzdata2014g
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,19 +21,16 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# <pre>
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
|
||||
# From Paul Eggert (1999-11-15):
|
||||
# To keep things manageable, we list only locations occupied year-round; see
|
||||
# <a href="http://www.comnap.aq/comnap/comnap.nsf/P/Stations/">
|
||||
# COMNAP - Stations and Bases
|
||||
# </a>
|
||||
# http://www.comnap.aq/comnap/comnap.nsf/P/Stations/
|
||||
# and
|
||||
# <a href="http://www.spri.cam.ac.uk/bob/periant.htm">
|
||||
# Summary of the Peri-Antarctic Islands (1998-07-23)
|
||||
# </a>
|
||||
# http://www.spri.cam.ac.uk/bob/periant.htm
|
||||
# for information.
|
||||
# Unless otherwise specified, we have no time zone information.
|
||||
#
|
||||
@ -78,19 +75,19 @@ Rule ChileAQ 2012 max - Sep Sun>=2 4:00u 1:00 S
|
||||
|
||||
# Argentina - year-round bases
|
||||
# Belgrano II, Confin Coast, -770227-0343737, since 1972-02-05
|
||||
# Esperanza, San Martin Land, -6323-05659, since 1952-12-17
|
||||
# Jubany, Potter Peninsula, King George Island, -6414-0602320, since 1982-01
|
||||
# Marambio, Seymour I, -6414-05637, since 1969-10-29
|
||||
# Carlini, Potter Cove, King George Island, -6414-0602320, since 1982-01
|
||||
# Esperanza, Hope Bay, -6323-05659, since 1952-12-17
|
||||
# Marambio, -6414-05637, since 1969-10-29
|
||||
# Orcadas, Laurie I, -6016-04444, since 1904-02-22
|
||||
# San Martin, Debenham I, -6807-06708, since 1951-03-21
|
||||
# San Martín, Barry I, -6808-06706, since 1951-03-21
|
||||
# (except 1960-03 / 1976-03-21)
|
||||
|
||||
# Australia - territories
|
||||
# Heard Island, McDonald Islands (uninhabited)
|
||||
# previously sealers and scientific personnel wintered
|
||||
# <a href="http://web.archive.org/web/20021204222245/http://www.dstc.qut.edu.au/DST/marg/daylight.html">
|
||||
# Margaret Turner reports
|
||||
# </a> (1999-09-30) that they're UTC+5, with no DST;
|
||||
# http://web.archive.org/web/20021204222245/http://www.dstc.qut.edu.au/DST/marg/daylight.html
|
||||
# (1999-09-30) that they're UTC+5, with no DST;
|
||||
# presumably this is when they have visitors.
|
||||
#
|
||||
# year-round bases
|
||||
@ -107,14 +104,10 @@ Rule ChileAQ 2012 max - Sep Sun>=2 4:00u 1:00 S
|
||||
# The changes occurred on 2009-10-18 at 02:00 (local times).
|
||||
#
|
||||
# Government source: (Australian Antarctic Division)
|
||||
# <a href="http://www.aad.gov.au/default.asp?casid=37079">
|
||||
# http://www.aad.gov.au/default.asp?casid=37079
|
||||
# </a>
|
||||
#
|
||||
# We have more background information here:
|
||||
# <a href="http://www.timeanddate.com/news/time/antarctica-new-times.html">
|
||||
# http://www.timeanddate.com/news/time/antarctica-new-times.html
|
||||
# </a>
|
||||
|
||||
# From Steffen Thorsen (2010-03-10):
|
||||
# We got these changes from the Australian Antarctic Division: ...
|
||||
@ -129,50 +122,49 @@ Rule ChileAQ 2012 max - Sep Sun>=2 4:00u 1:00 S
|
||||
# - Mawson station stays on UTC+5.
|
||||
#
|
||||
# Background:
|
||||
# <a href="http://www.timeanddate.com/news/time/antartica-time-changes-2010.html">
|
||||
# http://www.timeanddate.com/news/time/antartica-time-changes-2010.html
|
||||
# </a>
|
||||
|
||||
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
|
||||
Zone Antarctica/Casey 0 - zzz 1969
|
||||
8:00 - WST 2009 Oct 18 2:00
|
||||
# Western (Aus) Standard Time
|
||||
11:00 - CAST 2010 Mar 5 2:00
|
||||
# Casey Time
|
||||
8:00 - WST 2011 Oct 28 2:00
|
||||
8:00 - AWST 2009 Oct 18 2:00
|
||||
# Australian Western Std Time
|
||||
11:00 - CAST 2010 Mar 5 2:00 # Casey Time
|
||||
8:00 - AWST 2011 Oct 28 2:00
|
||||
11:00 - CAST 2012 Feb 21 17:00u
|
||||
8:00 - WST
|
||||
8:00 - AWST
|
||||
Zone Antarctica/Davis 0 - zzz 1957 Jan 13
|
||||
7:00 - DAVT 1964 Nov # Davis Time
|
||||
7:00 - DAVT 1964 Nov # Davis Time
|
||||
0 - zzz 1969 Feb
|
||||
7:00 - DAVT 2009 Oct 18 2:00
|
||||
7:00 - DAVT 2009 Oct 18 2:00
|
||||
5:00 - DAVT 2010 Mar 10 20:00u
|
||||
7:00 - DAVT 2011 Oct 28 2:00
|
||||
7:00 - DAVT 2011 Oct 28 2:00
|
||||
5:00 - DAVT 2012 Feb 21 20:00u
|
||||
7:00 - DAVT
|
||||
Zone Antarctica/Mawson 0 - zzz 1954 Feb 13
|
||||
6:00 - MAWT 2009 Oct 18 2:00
|
||||
# Mawson Time
|
||||
6:00 - MAWT 2009 Oct 18 2:00 # Mawson Time
|
||||
5:00 - MAWT
|
||||
# References:
|
||||
# <a href="http://www.antdiv.gov.au/aad/exop/sfo/casey/casey_aws.html">
|
||||
# Casey Weather (1998-02-26)
|
||||
# </a>
|
||||
# <a href="http://www.antdiv.gov.au/aad/exop/sfo/davis/video.html">
|
||||
# http://www.antdiv.gov.au/aad/exop/sfo/casey/casey_aws.html
|
||||
# Davis Station, Antarctica (1998-02-26)
|
||||
# </a>
|
||||
# <a href="http://www.antdiv.gov.au/aad/exop/sfo/mawson/video.html">
|
||||
# http://www.antdiv.gov.au/aad/exop/sfo/davis/video.html
|
||||
# Mawson Station, Antarctica (1998-02-25)
|
||||
# </a>
|
||||
# http://www.antdiv.gov.au/aad/exop/sfo/mawson/video.html
|
||||
|
||||
# Belgium - year-round base
|
||||
# Princess Elisabeth, Queen Maud Land, -713412+0231200, since 2007
|
||||
|
||||
# Brazil - year-round base
|
||||
# Comandante Ferraz, King George Island, -6205+05824, since 1983/4
|
||||
# Ferraz, King George Island, -6205+05824, since 1983/4
|
||||
|
||||
# Bulgaria - year-round base
|
||||
# St. Kliment Ohridski, Livingston Island, -623829-0602153, since 1988
|
||||
|
||||
# Chile - year-round bases and towns
|
||||
# Escudero, South Shetland Is, -621157-0585735, since 1994
|
||||
# Presidente Eduadro Frei, King George Island, -6214-05848, since 1969-03-07
|
||||
# General Bernardo O'Higgins, Antarctic Peninsula, -6319-05704, since 1948-02
|
||||
# Capitan Arturo Prat, -6230-05941
|
||||
# Frei Montalva, King George Island, -6214-05848, since 1969-03-07
|
||||
# O'Higgins, Antarctic Peninsula, -6319-05704, since 1948-02
|
||||
# Prat, -6230-05941
|
||||
# Villa Las Estrellas (a town), around the Frei base, since 1984-04-09
|
||||
# These locations have always used Santiago time; use TZ='America/Santiago'.
|
||||
|
||||
@ -180,31 +172,35 @@ Zone Antarctica/Mawson 0 - zzz 1954 Feb 13
|
||||
# Great Wall, King George Island, -6213-05858, since 1985-02-20
|
||||
# Zhongshan, Larsemann Hills, Prydz Bay, -6922+07623, since 1989-02-26
|
||||
|
||||
# France - year-round bases
|
||||
# France - year-round bases (also see "France & Italy")
|
||||
#
|
||||
# From Antoine Leca (1997-01-20):
|
||||
# Time data are from Nicole Pailleau at the IFRTP
|
||||
# Time data entries are from Nicole Pailleau at the IFRTP
|
||||
# (French Institute for Polar Research and Technology).
|
||||
# She confirms that French Southern Territories and Terre Adelie bases
|
||||
# don't observe daylight saving time, even if Terre Adelie supplies came
|
||||
# She confirms that French Southern Territories and Terre Adélie bases
|
||||
# don't observe daylight saving time, even if Terre Adélie supplies came
|
||||
# from Tasmania.
|
||||
#
|
||||
# French Southern Territories with year-round inhabitants
|
||||
#
|
||||
# Martin-de-Vivies Base, Amsterdam Island, -374105+0773155, since 1950
|
||||
# Alfred-Faure Base, Crozet Islands, -462551+0515152, since 1964
|
||||
# Port-aux-Francais, Kerguelen Islands, -492110+0701303, since 1951;
|
||||
# Alfred Faure, Possession Island, Crozet Islands, -462551+0515152, since 1964;
|
||||
# sealing & whaling stations operated variously 1802/1911+;
|
||||
# see Indian/Reunion.
|
||||
#
|
||||
# Martin-de-Viviès, Amsterdam Island, -374105+0773155, since 1950
|
||||
# Port-aux-Français, Kerguelen Islands, -492110+0701303, since 1951;
|
||||
# whaling & sealing station operated 1908/1914, 1920/1929, and 1951/1956
|
||||
#
|
||||
# St Paul Island - near Amsterdam, uninhabited
|
||||
# fishing stations operated variously 1819/1931
|
||||
#
|
||||
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
|
||||
Zone Indian/Kerguelen 0 - zzz 1950 # Port-aux-Francais
|
||||
Zone Indian/Kerguelen 0 - zzz 1950 # Port-aux-Français
|
||||
5:00 - TFT # ISO code TF Time
|
||||
#
|
||||
# year-round base in the main continent
|
||||
# Dumont-d'Urville, Ile des Petrels, -6640+14001, since 1956-11
|
||||
# Dumont d'Urville, Île des Pétrels, -6640+14001, since 1956-11
|
||||
# <http://en.wikipedia.org/wiki/Dumont_d'Urville_Station> (2005-12-05)
|
||||
#
|
||||
# Another base at Port-Martin, 50km east, began operation in 1947.
|
||||
# It was destroyed by fire on 1952-01-14.
|
||||
@ -214,20 +210,22 @@ Zone Antarctica/DumontDUrville 0 - zzz 1947
|
||||
10:00 - PMT 1952 Jan 14 # Port-Martin Time
|
||||
0 - zzz 1956 Nov
|
||||
10:00 - DDUT # Dumont-d'Urville Time
|
||||
# Reference:
|
||||
# <a href="http://en.wikipedia.org/wiki/Dumont_d'Urville_Station">
|
||||
# Dumont d'Urville Station (2005-12-05)
|
||||
# </a>
|
||||
|
||||
# France & Italy - year-round base
|
||||
# Concordia, -750600+1232000, since 2005
|
||||
|
||||
# Germany - year-round base
|
||||
# Georg von Neumayer, -7039-00815
|
||||
# Neumayer III, -704080-0081602, since 2009
|
||||
|
||||
# India - year-round base
|
||||
# Dakshin Gangotri, -7005+01200
|
||||
# India - year-round bases
|
||||
# Bharati, -692428+0761114, since 2012
|
||||
# Maitri, -704558+0114356, since 1989
|
||||
|
||||
# Italy - year-round base (also see "France & Italy")
|
||||
# Zuchelli, Terra Nova Bay, -744140+1640647, since 1986
|
||||
|
||||
# Japan - year-round bases
|
||||
# Dome Fuji, -7719+03942
|
||||
# Syowa, -690022+0393524
|
||||
# Syowa (also known as Showa), -690022+0393524, since 1957
|
||||
#
|
||||
# From Hideyuki Suzuki (1999-02-06):
|
||||
# In all Japanese stations, +0300 is used as the standard time.
|
||||
@ -239,11 +237,11 @@ Zone Antarctica/DumontDUrville 0 - zzz 1947
|
||||
Zone Antarctica/Syowa 0 - zzz 1957 Jan 29
|
||||
3:00 - SYOT # Syowa Time
|
||||
# See:
|
||||
# <a href="http://www.nipr.ac.jp/english/ara01.html">
|
||||
# NIPR Antarctic Research Activities (1999-08-17)
|
||||
# </a>
|
||||
# http://www.nipr.ac.jp/english/ara01.html
|
||||
|
||||
# S Korea - year-round base
|
||||
# Jang Bogo, Terra Nova Bay, -743700+1641205 since 2014
|
||||
# King Sejong, King George Island, -6213-05847, since 1988
|
||||
|
||||
# New Zealand - claims
|
||||
@ -287,11 +285,14 @@ Rule Troll 2005 max - Mar lastSun 1:00u 2:00 CEST
|
||||
Rule Troll 2004 max - Oct lastSun 1:00u 0:00 UTC
|
||||
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
|
||||
Zone Antarctica/Troll 0 - zzz 2005 Feb 12
|
||||
0:00 Troll %s
|
||||
0:00 Troll %s
|
||||
|
||||
# Poland - year-round base
|
||||
# Arctowski, King George Island, -620945-0582745, since 1977
|
||||
|
||||
# Romania - year-bound base
|
||||
# Law-Racoviță, Larsemann Hills, -692319+0762251, since 1986
|
||||
|
||||
# Russia - year-round bases
|
||||
# Bellingshausen, King George Island, -621159-0585337, since 1968-02-22
|
||||
# Mirny, Davis coast, -6633+09301, since 1956-02
|
||||
@ -301,8 +302,8 @@ Zone Antarctica/Troll 0 - zzz 2005 Feb 12
|
||||
# year-round from 1960/61 to 1992
|
||||
|
||||
# Vostok, since 1957-12-16, temporarily closed 1994-02/1994-11
|
||||
# <a href="http://quest.arc.nasa.gov/antarctica/QA/computers/Directions,Time,ZIP">
|
||||
# From Craig Mundell (1994-12-15)</a>:
|
||||
# From Craig Mundell (1994-12-15):
|
||||
# http://quest.arc.nasa.gov/antarctica/QA/computers/Directions,Time,ZIP
|
||||
# Vostok, which is one of the Russian stations, is set on the same
|
||||
# time as Moscow, Russia.
|
||||
#
|
||||
@ -317,7 +318,7 @@ Zone Antarctica/Troll 0 - zzz 2005 Feb 12
|
||||
#
|
||||
# From Paul Eggert (2001-05-04):
|
||||
# This seems to be hopelessly confusing, so I asked Lee Hotz about it
|
||||
# in person. He said that some Antartic locations set their local
|
||||
# in person. He said that some Antarctic locations set their local
|
||||
# time so that noon is the warmest part of the day, and that this
|
||||
# changes during the year and does not necessarily correspond to mean
|
||||
# solar noon. So the Vostok time might have been whatever the clocks
|
||||
@ -329,9 +330,12 @@ Zone Antarctica/Vostok 0 - zzz 1957 Dec 16
|
||||
|
||||
# S Africa - year-round bases
|
||||
# Marion Island, -4653+03752
|
||||
# Sanae, -7141-00250
|
||||
# SANAE IV, Vesleskarvet, Queen Maud Land, -714022-0025026, since 1997
|
||||
|
||||
# UK
|
||||
# Ukraine - year-round base
|
||||
# Vernadsky (formerly Faraday), Galindez Island, -651445-0641526, since 1954
|
||||
|
||||
# United Kingdom
|
||||
#
|
||||
# British Antarctic Territories (BAT) claims
|
||||
# South Orkney Islands
|
||||
@ -387,7 +391,7 @@ Zone Antarctica/Palmer 0 - zzz 1965
|
||||
# but that he found it more convenient to keep GMT+12
|
||||
# as supplies for the station were coming from McMurdo Sound,
|
||||
# which was on GMT+12 because New Zealand was on GMT+12 all year
|
||||
# at that time (1957). (Source: Siple's book 90 degrees SOUTH.)
|
||||
# at that time (1957). (Source: Siple's book 90 Degrees South.)
|
||||
#
|
||||
# From Susan Smith
|
||||
# http://www.cybertours.com/whs/pole10.html
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -21,15 +21,15 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# <pre>
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
|
||||
# This file provides links between current names for time zones
|
||||
# and their old names. Many names changed in late 1993.
|
||||
|
||||
# Link TARGET LINK-NAME
|
||||
Link Africa/Asmara Africa/Asmera
|
||||
Link Africa/Bamako Africa/Timbuktu
|
||||
Link Africa/Abidjan Africa/Timbuktu
|
||||
Link America/Argentina/Catamarca America/Argentina/ComodRivadavia
|
||||
Link America/Adak America/Atka
|
||||
Link America/Argentina/Buenos_Aires America/Buenos_Aires
|
||||
@ -50,8 +50,11 @@ Link America/Port_of_Spain America/Virgin
|
||||
Link Pacific/Auckland Antarctica/South_Pole
|
||||
Link Asia/Ashgabat Asia/Ashkhabad
|
||||
Link Asia/Kolkata Asia/Calcutta
|
||||
Link Asia/Chongqing Asia/Chungking
|
||||
Link Asia/Shanghai Asia/Chongqing
|
||||
Link Asia/Shanghai Asia/Chungking
|
||||
Link Asia/Dhaka Asia/Dacca
|
||||
Link Asia/Shanghai Asia/Harbin
|
||||
Link Asia/Urumqi Asia/Kashgar
|
||||
Link Asia/Kathmandu Asia/Katmandu
|
||||
Link Asia/Macau Asia/Macao
|
||||
Link Asia/Ho_Chi_Minh Asia/Saigon
|
||||
|
@ -21,7 +21,6 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# <pre>
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
|
||||
@ -37,7 +36,7 @@ Zone Etc/UTC 0 - UTC
|
||||
Zone Etc/UCT 0 - UCT
|
||||
|
||||
# The following link uses older naming conventions,
|
||||
# but it belongs here, not in the file `backward',
|
||||
# but it belongs here, not in the file 'backward',
|
||||
# as functions like gmtime load the "GMT" file to handle leap seconds properly.
|
||||
# We want this to work even on installations that omit the other older names.
|
||||
Link Etc/GMT GMT
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# <pre>
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
|
||||
|
@ -26,21 +26,21 @@
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
#
|
||||
# From Paul Eggert (2013-05-27):
|
||||
# From Paul Eggert (2014-07-18):
|
||||
# This file contains a table of two-letter country codes. Columns are
|
||||
# separated by a single tab. Lines beginning with '#' are comments.
|
||||
# Although all text currently uses ASCII encoding, this is planned to
|
||||
# change to UTF-8 soon. The columns of the table are as follows:
|
||||
#
|
||||
# This file contains a table with the following columns:
|
||||
# 1. ISO 3166-1 alpha-2 country code, current as of
|
||||
# ISO 3166-1 Newsletter VI-15 (2013-05-10). See: Updates on ISO 3166
|
||||
# ISO 3166-1 Newsletter VI-16 (2013-07-11). See: Updates on ISO 3166
|
||||
# http://www.iso.org/iso/home/standards/country_codes/updates_on_iso_3166.htm
|
||||
# 2. The usual English name for the coded region,
|
||||
# chosen so that alphabetic sorting of subsets produces helpful lists.
|
||||
# This is not the same as the English name in the ISO 3166 tables.
|
||||
#
|
||||
# Columns are separated by a single tab.
|
||||
# The table is sorted by country code.
|
||||
#
|
||||
# Lines beginning with `#' are comments.
|
||||
#
|
||||
# This table is intended as an aid for users, to help them select time
|
||||
# zone data appropriate for their practical needs. It is not intended
|
||||
# to take or endorse any position on legal or territorial claims.
|
||||
|
@ -21,7 +21,7 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# Allowance for leapseconds added to each timezone file.
|
||||
# Allowance for leap seconds added to each time zone file.
|
||||
|
||||
# This file is in the public domain.
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
# you should be able to pick up leap-seconds.list from a secondary NIST server.
|
||||
# For more about leap-seconds.list, please see
|
||||
# The NTP Timescale and Leap Seconds
|
||||
# <http://www.eecis.udel.edu/~mills/leap.html>.
|
||||
# http://www.eecis.udel.edu/~mills/leap.html
|
||||
|
||||
# The International Earth Rotation Service periodically uses leap seconds
|
||||
# to keep UTC to within 0.9 s of UT1
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# <pre>
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# <pre>
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
|
||||
|
@ -21,39 +21,27 @@
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
# TZ zone descriptions
|
||||
# tz zone descriptions (deprecated version)
|
||||
#
|
||||
# This file is in the public domain, so clarified as of
|
||||
# 2009-05-17 by Arthur David Olson.
|
||||
#
|
||||
# From Paul Eggert (2013-08-14):
|
||||
# From Paul Eggert (2014-07-31):
|
||||
# This file is intended as a backward-compatibility aid for older programs.
|
||||
# New programs should use zone1970.tab. This file is like zone1970.tab (see
|
||||
# zone1970.tab's comments), but with the following additional restrictions:
|
||||
#
|
||||
# This file contains a table where each row stands for an area that is
|
||||
# the intersection of a region identified by a country code and of a
|
||||
# zone where civil clocks have agreed since 1970. The columns of the
|
||||
# table are as follows:
|
||||
# 1. This file contains only ASCII characters.
|
||||
# 2. The first data column contains exactly one country code.
|
||||
#
|
||||
# 1. ISO 3166 2-character country code. See the file 'iso3166.tab'.
|
||||
# 2. Latitude and longitude of the area's principal location
|
||||
# in ISO 6709 sign-degrees-minutes-seconds format,
|
||||
# either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS,
|
||||
# first latitude (+ is north), then longitude (+ is east).
|
||||
# 3. Zone name used in value of TZ environment variable.
|
||||
# Please see the 'Theory' file for how zone names are chosen.
|
||||
# If multiple zones overlap a country, each has a row in the
|
||||
# table, with column 1 being duplicated.
|
||||
# 4. Comments; present if and only if the country has multiple rows.
|
||||
#
|
||||
# Columns are separated by a single tab.
|
||||
# The table is sorted first by country, then an order within the country that
|
||||
# (1) makes some geographical sense, and
|
||||
# (2) puts the most populous areas first, where that does not contradict (1).
|
||||
#
|
||||
# Lines beginning with '#' are comments.
|
||||
# Because of (2), each row stands for an area that is the intersection
|
||||
# of a region identified by a country code and of a zone where civil
|
||||
# clocks have agreed since 1970; this is a narrower definition than
|
||||
# that of zone1970.tab.
|
||||
#
|
||||
# This table is intended as an aid for users, to help them select time
|
||||
# zone data appropriate for their practical needs. It is not intended
|
||||
# to take or endorse any position on legal or territorial claims.
|
||||
# zone data entries appropriate for their practical needs. It is not
|
||||
# intended to take or endorse any position on legal or territorial claims.
|
||||
#
|
||||
#country-
|
||||
#code coordinates TZ comments
|
||||
@ -72,7 +60,7 @@ AQ -6736+06253 Antarctica/Mawson Mawson Station, Holme Bay
|
||||
AQ -6835+07758 Antarctica/Davis Davis Station, Vestfold Hills
|
||||
AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula
|
||||
AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok
|
||||
AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie
|
||||
AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Adelie Land
|
||||
AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I
|
||||
AQ -720041+0023206 Antarctica/Troll Troll Station, Queen Maud Land
|
||||
AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF)
|
||||
@ -151,7 +139,7 @@ CA +4901-08816 America/Nipigon Eastern Time - Ontario & Quebec - places that did
|
||||
CA +4823-08915 America/Thunder_Bay Eastern Time - Thunder Bay, Ontario
|
||||
CA +6344-06828 America/Iqaluit Eastern Time - east Nunavut - most locations
|
||||
CA +6608-06544 America/Pangnirtung Eastern Time - Pangnirtung, Nunavut
|
||||
CA +744144-0944945 America/Resolute Central Standard Time - Resolute, Nunavut
|
||||
CA +744144-0944945 America/Resolute Central Time - Resolute, Nunavut
|
||||
CA +484531-0913718 America/Atikokan Eastern Standard Time - Atikokan, Ontario and Southampton I, Nunavut
|
||||
CA +624900-0920459 America/Rankin_Inlet Central Time - central Nunavut
|
||||
CA +4953-09709 America/Winnipeg Central Time - Manitoba & west Ontario
|
||||
@ -176,13 +164,10 @@ CH +4723+00832 Europe/Zurich
|
||||
CI +0519-00402 Africa/Abidjan
|
||||
CK -2114-15946 Pacific/Rarotonga
|
||||
CL -3327-07040 America/Santiago most locations
|
||||
CL -2709-10926 Pacific/Easter Easter Island & Sala y Gomez
|
||||
CL -2709-10926 Pacific/Easter Easter Island
|
||||
CM +0403+00942 Africa/Douala
|
||||
CN +3114+12128 Asia/Shanghai east China - Beijing, Guangdong, Shanghai, etc.
|
||||
CN +4545+12641 Asia/Harbin Heilongjiang (except Mohe), Jilin
|
||||
CN +2934+10635 Asia/Chongqing central China - Sichuan, Yunnan, Guangxi, Shaanxi, Guizhou, etc.
|
||||
CN +4348+08735 Asia/Urumqi most of Tibet & Xinjiang
|
||||
CN +3929+07559 Asia/Kashgar west Tibet & Xinjiang
|
||||
CN +3114+12128 Asia/Shanghai Beijing Time
|
||||
CN +4348+08735 Asia/Urumqi Xinjiang Time
|
||||
CO +0436-07405 America/Bogota
|
||||
CR +0956-08405 America/Costa_Rica
|
||||
CU +2308-08222 America/Havana
|
||||
@ -364,24 +349,26 @@ RE -2052+05528 Indian/Reunion
|
||||
RO +4426+02606 Europe/Bucharest
|
||||
RS +4450+02030 Europe/Belgrade
|
||||
RU +5443+02030 Europe/Kaliningrad Moscow-01 - Kaliningrad
|
||||
RU +5545+03735 Europe/Moscow Moscow+00 - west Russia
|
||||
RU +4844+04425 Europe/Volgograd Moscow+00 - Caspian Sea
|
||||
RU +5312+05009 Europe/Samara Moscow+00 - Samara, Udmurtia
|
||||
RU +554521+0373704 Europe/Moscow Moscow+00 - west Russia
|
||||
RU +4457+03406 Europe/Simferopol Moscow+00 - Crimea
|
||||
RU +4844+04425 Europe/Volgograd Moscow+00 - Caspian Sea
|
||||
RU +5312+05009 Europe/Samara Moscow+00 (Moscow+01 after 2014-10-26) - Samara, Udmurtia
|
||||
RU +5651+06036 Asia/Yekaterinburg Moscow+02 - Urals
|
||||
RU +5500+07324 Asia/Omsk Moscow+03 - west Siberia
|
||||
RU +5502+08255 Asia/Novosibirsk Moscow+03 - Novosibirsk
|
||||
RU +5345+08707 Asia/Novokuznetsk Moscow+03 - Novokuznetsk
|
||||
RU +5345+08707 Asia/Novokuznetsk Moscow+03 (Moscow+04 after 2014-10-26) - Kemerovo
|
||||
RU +5601+09250 Asia/Krasnoyarsk Moscow+04 - Yenisei River
|
||||
RU +5216+10420 Asia/Irkutsk Moscow+05 - Lake Baikal
|
||||
RU +5203+11328 Asia/Chita Moscow+06 (Moscow+05 after 2014-10-26) - Zabaykalsky
|
||||
RU +6200+12940 Asia/Yakutsk Moscow+06 - Lena River
|
||||
RU +623923+1353314 Asia/Khandyga Moscow+06 - Tomponsky, Ust-Maysky
|
||||
RU +4310+13156 Asia/Vladivostok Moscow+07 - Amur River
|
||||
RU +4658+14242 Asia/Sakhalin Moscow+07 - Sakhalin Island
|
||||
RU +643337+1431336 Asia/Ust-Nera Moscow+07 - Oymyakonsky
|
||||
RU +5934+15048 Asia/Magadan Moscow+08 - Magadan
|
||||
RU +5301+15839 Asia/Kamchatka Moscow+08 - Kamchatka
|
||||
RU +6445+17729 Asia/Anadyr Moscow+08 - Bering Sea
|
||||
RU +5934+15048 Asia/Magadan Moscow+08 (Moscow+07 after 2014-10-26) - Magadan
|
||||
RU +6728+15343 Asia/Srednekolymsk Moscow+08 - E Sakha, N Kuril Is
|
||||
RU +5301+15839 Asia/Kamchatka Moscow+08 (Moscow+09 after 2014-10-26) - Kamchatka
|
||||
RU +6445+17729 Asia/Anadyr Moscow+08 (Moscow+09 after 2014-10-26) - Bering Sea
|
||||
RW -0157+03004 Africa/Kigali
|
||||
SA +2438+04643 Asia/Riyadh
|
||||
SB -0932+16012 Pacific/Guadalcanal
|
||||
@ -448,13 +435,13 @@ US +394421-1045903 America/Denver Mountain Time
|
||||
US +433649-1161209 America/Boise Mountain Time - south Idaho & east Oregon
|
||||
US +332654-1120424 America/Phoenix Mountain Standard Time - Arizona (except Navajo)
|
||||
US +340308-1181434 America/Los_Angeles Pacific Time
|
||||
US +550737-1313435 America/Metlakatla Pacific Standard Time - Annette Island, Alaska
|
||||
US +611305-1495401 America/Anchorage Alaska Time
|
||||
US +581807-1342511 America/Juneau Alaska Time - Alaska panhandle
|
||||
US +571035-1351807 America/Sitka Alaska Time - southeast Alaska panhandle
|
||||
US +593249-1394338 America/Yakutat Alaska Time - Alaska panhandle neck
|
||||
US +643004-1652423 America/Nome Alaska Time - west Alaska
|
||||
US +515248-1763929 America/Adak Aleutian Islands
|
||||
US +550737-1313435 America/Metlakatla Metlakatla Time - Annette Island
|
||||
US +211825-1575130 Pacific/Honolulu Hawaii
|
||||
UY -3453-05611 America/Montevideo
|
||||
UZ +3940+06648 Asia/Samarkand west Uzbekistan
|
||||
|
@ -592,37 +592,6 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as a signed {@code int} in the
|
||||
* specified {@code radix}, beginning at the specified {@code beginIndex}
|
||||
* and extending to the end of the sequence.
|
||||
*
|
||||
* <p>The method does not take steps to guard against the
|
||||
* {@code CharSequence} being mutated while parsing.
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the {@code int}
|
||||
* representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @return the signed {@code int} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex} is
|
||||
* negative, or if {@code beginIndex} is greater than
|
||||
* {@code s.length()}.
|
||||
* @throws NumberFormatException if the {@code CharSequence} does not
|
||||
* contain a parsable {@code int} in the specified
|
||||
* {@code radix}, or if {@code radix} is either smaller than
|
||||
* {@link java.lang.Character#MIN_RADIX} or larger than
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static int parseInt(CharSequence s, int radix, int beginIndex)
|
||||
throws NumberFormatException {
|
||||
// forces an implicit null check of s
|
||||
return parseInt(s, radix, beginIndex, s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as a signed {@code int} in the
|
||||
* specified {@code radix}, beginning at the specified {@code beginIndex}
|
||||
@ -633,9 +602,9 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the {@code int}
|
||||
* representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @return the signed {@code int} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
@ -650,7 +619,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static int parseInt(CharSequence s, int radix, int beginIndex, int endIndex)
|
||||
public static int parseInt(CharSequence s, int beginIndex, int endIndex, int radix)
|
||||
throws NumberFormatException {
|
||||
s = Objects.requireNonNull(s);
|
||||
|
||||
@ -690,7 +659,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
int result = 0;
|
||||
while (i < endIndex) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
int digit = Character.digit(s.charAt(i++), radix);
|
||||
int digit = Character.digit(s.charAt(i), radix);
|
||||
if (digit < 0 || result < multmin) {
|
||||
throw NumberFormatException.forCharSequence(s, beginIndex,
|
||||
endIndex, i);
|
||||
@ -700,6 +669,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
throw NumberFormatException.forCharSequence(s, beginIndex,
|
||||
endIndex, i);
|
||||
}
|
||||
i++;
|
||||
result -= digit;
|
||||
}
|
||||
return negative ? result : -result;
|
||||
@ -805,37 +775,6 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as an unsigned {@code int} in
|
||||
* the specified {@code radix}, beginning at the specified
|
||||
* {@code beginIndex} and extending to the end of the sequence.
|
||||
*
|
||||
* <p>The method does not take steps to guard against the
|
||||
* {@code CharSequence} being mutated while parsing.
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the unsigned
|
||||
* {@code int} representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @return the unsigned {@code int} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex} is
|
||||
* negative, or if {@code beginIndex} is greater than
|
||||
* {@code s.length()}.
|
||||
* @throws NumberFormatException if the {@code CharSequence} does not
|
||||
* contain a parsable unsigned {@code int} in the specified
|
||||
* {@code radix}, or if {@code radix} is either smaller than
|
||||
* {@link java.lang.Character#MIN_RADIX} or larger than
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static int parseUnsignedInt(CharSequence s, int radix, int beginIndex)
|
||||
throws NumberFormatException {
|
||||
// forces an implicit null check of s
|
||||
return parseUnsignedInt(s, radix, beginIndex, s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as an unsigned {@code int} in
|
||||
* the specified {@code radix}, beginning at the specified
|
||||
@ -846,9 +785,9 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the unsigned
|
||||
* {@code int} representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @return the unsigned {@code int} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
@ -863,7 +802,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static int parseUnsignedInt(CharSequence s, int radix, int beginIndex, int endIndex)
|
||||
public static int parseUnsignedInt(CharSequence s, int beginIndex, int endIndex, int radix)
|
||||
throws NumberFormatException {
|
||||
s = Objects.requireNonNull(s);
|
||||
|
||||
@ -881,9 +820,9 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
} else {
|
||||
if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
|
||||
(radix == 10 && len <= 9)) { // Integer.MAX_VALUE in base 10 is 10 digits
|
||||
return parseInt(s, radix, start, start + len);
|
||||
return parseInt(s, start, start + len, radix);
|
||||
} else {
|
||||
long ell = Long.parseLong(s, radix, start, start + len);
|
||||
long ell = Long.parseLong(s, start, start + len, radix);
|
||||
if ((ell & 0xffff_ffff_0000_0000L) == 0) {
|
||||
return (int) ell;
|
||||
} else {
|
||||
|
@ -604,37 +604,6 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as a signed {@code long} in
|
||||
* the specified {@code radix}, beginning at the specified {@code beginIndex}
|
||||
* and extending to the end of the sequence.
|
||||
*
|
||||
* <p>The method does not take steps to guard against the
|
||||
* {@code CharSequence} being mutated while parsing.
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the {@code long}
|
||||
* representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @return the signed {@code long} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex} is
|
||||
* negative, or if {@code beginIndex} is greater than
|
||||
* {@code s.length()}.
|
||||
* @throws NumberFormatException if the {@code CharSequence} does not
|
||||
* contain a parsable {@code long} in the specified
|
||||
* {@code radix}, or if {@code radix} is either smaller than
|
||||
* {@link java.lang.Character#MIN_RADIX} or larger than
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static long parseLong(CharSequence s, int radix, int beginIndex)
|
||||
throws NumberFormatException {
|
||||
// forces a null check of s
|
||||
return parseLong(s, radix, beginIndex, s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as a signed {@code long} in
|
||||
* the specified {@code radix}, beginning at the specified
|
||||
@ -645,9 +614,9 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the {@code long}
|
||||
* representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @return the signed {@code long} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
@ -662,7 +631,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static long parseLong(CharSequence s, int radix, int beginIndex, int endIndex)
|
||||
public static long parseLong(CharSequence s, int beginIndex, int endIndex, int radix)
|
||||
throws NumberFormatException {
|
||||
s = Objects.requireNonNull(s);
|
||||
|
||||
@ -702,7 +671,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
long result = 0;
|
||||
while (i < endIndex) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
int digit = Character.digit(s.charAt(i++), radix);
|
||||
int digit = Character.digit(s.charAt(i), radix);
|
||||
if (digit < 0 || result < multmin) {
|
||||
throw NumberFormatException.forCharSequence(s, beginIndex,
|
||||
endIndex, i);
|
||||
@ -712,6 +681,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
throw NumberFormatException.forCharSequence(s, beginIndex,
|
||||
endIndex, i);
|
||||
}
|
||||
i++;
|
||||
result -= digit;
|
||||
}
|
||||
return negative ? result : -result;
|
||||
@ -811,7 +781,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
}
|
||||
|
||||
// No need for range checks on len due to testing above.
|
||||
long first = parseLong(s, radix, 0, len - 1);
|
||||
long first = parseLong(s, 0, len - 1, radix);
|
||||
int second = Character.digit(s.charAt(len - 1), radix);
|
||||
if (second < 0) {
|
||||
throw new NumberFormatException("Bad digit at end of " + s);
|
||||
@ -880,37 +850,6 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as an unsigned {@code long} in
|
||||
* the specified {@code radix}, beginning at the specified
|
||||
* {@code beginIndex} and extending to the end of the sequence.
|
||||
*
|
||||
* <p>The method does not take steps to guard against the
|
||||
* {@code CharSequence} being mutated while parsing.
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the unsigned
|
||||
* {@code long} representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @return the unsigned {@code long} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex} is
|
||||
* negative, or if {@code beginIndex} is greater than
|
||||
* {@code s.length()}.
|
||||
* @throws NumberFormatException if the {@code CharSequence} does not
|
||||
* contain a parsable unsigned {@code long} in the specified
|
||||
* {@code radix}, or if {@code radix} is either smaller than
|
||||
* {@link java.lang.Character#MIN_RADIX} or larger than
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex)
|
||||
throws NumberFormatException {
|
||||
// forces a null check of s
|
||||
return parseUnsignedLong(s, radix, beginIndex, s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@link CharSequence} argument as an unsigned {@code long} in
|
||||
* the specified {@code radix}, beginning at the specified
|
||||
@ -921,9 +860,9 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
*
|
||||
* @param s the {@code CharSequence} containing the unsigned
|
||||
* {@code long} representation to be parsed
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @param radix the radix to be used while parsing {@code s}.
|
||||
* @return the unsigned {@code long} represented by the subsequence in
|
||||
* the specified radix.
|
||||
* @throws NullPointerException if {@code s} is null.
|
||||
@ -938,7 +877,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* {@link java.lang.Character#MAX_RADIX}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex, int endIndex)
|
||||
public static long parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
|
||||
throws NumberFormatException {
|
||||
s = Objects.requireNonNull(s);
|
||||
|
||||
@ -955,11 +894,11 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
} else {
|
||||
if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
|
||||
(radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
|
||||
return parseLong(s, radix, start, start + len);
|
||||
return parseLong(s, start, start + len, radix);
|
||||
}
|
||||
|
||||
// No need for range checks on end due to testing above.
|
||||
long first = parseLong(s, radix, start, start + len - 1);
|
||||
long first = parseLong(s, start, start + len - 1, radix);
|
||||
int second = Character.digit(s.charAt(start + len - 1), radix);
|
||||
if (second < 0) {
|
||||
throw new NumberFormatException("Bad digit at end of " +
|
||||
|
@ -2119,7 +2119,7 @@ public final class String
|
||||
* @since 1.5
|
||||
*/
|
||||
public boolean contains(CharSequence s) {
|
||||
return indexOf(s.toString()) > -1;
|
||||
return indexOf(s.toString()) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,31 +50,31 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
*
|
||||
* All bound arguments are encapsulated in dedicated species.
|
||||
*/
|
||||
/* non-public */ abstract class BoundMethodHandle extends MethodHandle {
|
||||
/*non-public*/ abstract class BoundMethodHandle extends MethodHandle {
|
||||
|
||||
/* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
|
||||
/*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
|
||||
super(type, form);
|
||||
assert(speciesData() == speciesData(form));
|
||||
}
|
||||
|
||||
//
|
||||
// BMH API and internals
|
||||
//
|
||||
|
||||
static MethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
|
||||
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
|
||||
// for some type signatures, there exist pre-defined concrete BMH classes
|
||||
try {
|
||||
switch (xtype) {
|
||||
case L_TYPE:
|
||||
if (true) return bindSingle(type, form, x); // Use known fast path.
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor[0].invokeBasic(type, form, x);
|
||||
return bindSingle(type, form, x); // Use known fast path.
|
||||
case I_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x));
|
||||
case J_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor[0].invokeBasic(type, form, (long) x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor().invokeBasic(type, form, (long) x);
|
||||
case F_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor[0].invokeBasic(type, form, (float) x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor().invokeBasic(type, form, (float) x);
|
||||
case D_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor[0].invokeBasic(type, form, (double) x);
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor().invokeBasic(type, form, (double) x);
|
||||
default : throw newInternalError("unexpected xtype: " + xtype);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
@ -82,49 +82,61 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
}
|
||||
}
|
||||
|
||||
static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
|
||||
return new Species_L(type, form, x);
|
||||
/*non-public*/
|
||||
LambdaFormEditor editor() {
|
||||
return form.editor();
|
||||
}
|
||||
|
||||
MethodHandle cloneExtend(MethodType type, LambdaForm form, BasicType xtype, Object x) {
|
||||
try {
|
||||
switch (xtype) {
|
||||
case L_TYPE: return copyWithExtendL(type, form, x);
|
||||
case I_TYPE: return copyWithExtendI(type, form, ValueConversions.widenSubword(x));
|
||||
case J_TYPE: return copyWithExtendJ(type, form, (long) x);
|
||||
case F_TYPE: return copyWithExtendF(type, form, (float) x);
|
||||
case D_TYPE: return copyWithExtendD(type, form, (double) x);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw newInternalError(t);
|
||||
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
|
||||
return Species_L.make(type, form, x);
|
||||
}
|
||||
|
||||
@Override // there is a default binder in the super class, for 'L' types only
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentL(int pos, Object value) {
|
||||
return editor().bindArgumentL(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentI(int pos, int value) {
|
||||
return editor().bindArgumentI(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentJ(int pos, long value) {
|
||||
return editor().bindArgumentJ(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentF(int pos, float value) {
|
||||
return editor().bindArgumentF(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentD(int pos, double value) {
|
||||
return editor().bindArgumentD(this, pos, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
BoundMethodHandle rebind() {
|
||||
if (!tooComplex()) {
|
||||
return this;
|
||||
}
|
||||
throw newInternalError("unexpected type: " + xtype);
|
||||
return makeReinvoker(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
|
||||
MethodType type = type().dropParameterTypes(pos, pos+1);
|
||||
LambdaForm form = internalForm().bind(1+pos, speciesData());
|
||||
return cloneExtend(type, form, basicType, value);
|
||||
private boolean tooComplex() {
|
||||
return (fieldCount() > FIELD_COUNT_THRESHOLD ||
|
||||
form.expressionCount() > FORM_EXPRESSION_THRESHOLD);
|
||||
}
|
||||
private static final int FIELD_COUNT_THRESHOLD = 12; // largest convenient BMH field count
|
||||
private static final int FORM_EXPRESSION_THRESHOLD = 24; // largest convenient BMH expression count
|
||||
|
||||
@Override
|
||||
MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
|
||||
LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos + drops));
|
||||
try {
|
||||
return copyWith(srcType, form);
|
||||
} catch (Throwable t) {
|
||||
throw newInternalError(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle permuteArguments(MethodType newType, int[] reorder) {
|
||||
try {
|
||||
return copyWith(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
|
||||
} catch (Throwable t) {
|
||||
throw newInternalError(t);
|
||||
}
|
||||
/**
|
||||
* A reinvoker MH has this form:
|
||||
* {@code lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }}
|
||||
*/
|
||||
static BoundMethodHandle makeReinvoker(MethodHandle target) {
|
||||
LambdaForm form = DelegatingMethodHandle.makeReinvokerForm(
|
||||
target, MethodTypeForm.LF_REBIND,
|
||||
Species_L.SPECIES_DATA, Species_L.SPECIES_DATA.getterFunction(0));
|
||||
return Species_L.make(target.type(), form, target);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,14 +145,22 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
*/
|
||||
/*non-public*/ abstract SpeciesData speciesData();
|
||||
|
||||
/*non-public*/ static SpeciesData speciesData(LambdaForm form) {
|
||||
Object c = form.names[0].constraint;
|
||||
if (c instanceof SpeciesData)
|
||||
return (SpeciesData) c;
|
||||
// if there is no BMH constraint, then use the null constraint
|
||||
return SpeciesData.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of fields in this BMH. Equivalent to speciesData().fieldCount().
|
||||
*/
|
||||
/*non-public*/ abstract int fieldCount();
|
||||
|
||||
@Override
|
||||
final Object internalProperties() {
|
||||
return "/BMH="+internalValues();
|
||||
Object internalProperties() {
|
||||
return "\n& BMH="+internalValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -178,15 +198,6 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg);
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg);
|
||||
|
||||
// The following is a grossly irregular hack:
|
||||
@Override MethodHandle reinvokerTarget() {
|
||||
try {
|
||||
return (MethodHandle) arg(0);
|
||||
} catch (Throwable ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// concrete BMH classes required to close bootstrap loops
|
||||
//
|
||||
@ -198,8 +209,6 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
super(mt, lf);
|
||||
this.argL0 = argL0;
|
||||
}
|
||||
// The following is a grossly irregular hack:
|
||||
@Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
|
||||
@Override
|
||||
/*non-public*/ SpeciesData speciesData() {
|
||||
return SPECIES_DATA;
|
||||
@ -219,7 +228,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -227,7 +236,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -235,7 +244,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -243,7 +252,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -251,7 +260,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -268,18 +277,20 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* The fields are immutable; their values are fully specified at object construction.
|
||||
* Each BMH type supplies an array of getter functions which may be used in lambda forms.
|
||||
* A BMH is constructed by cloning a shorter BMH and adding one or more new field values.
|
||||
* As a degenerate and common case, the "shorter BMH" can be missing, and contributes zero prior fields.
|
||||
* The shortest possible BMH has zero fields; its class is SimpleMethodHandle.
|
||||
* BMH species are not interrelated by subtyping, even though it would appear that
|
||||
* a shorter BMH could serve as a supertype of a longer one which extends it.
|
||||
*/
|
||||
static class SpeciesData {
|
||||
final String typeChars;
|
||||
final BasicType[] typeCodes;
|
||||
final Class<? extends BoundMethodHandle> clazz;
|
||||
private final String typeChars;
|
||||
private final BasicType[] typeCodes;
|
||||
private final Class<? extends BoundMethodHandle> clazz;
|
||||
// Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
|
||||
// Therefore, we need a non-final link in the chain. Use array elements.
|
||||
final MethodHandle[] constructor;
|
||||
final MethodHandle[] getters;
|
||||
final NamedFunction[] nominalGetters;
|
||||
final SpeciesData[] extensions;
|
||||
@Stable private final MethodHandle[] constructor;
|
||||
@Stable private final MethodHandle[] getters;
|
||||
@Stable private final NamedFunction[] nominalGetters;
|
||||
@Stable private final SpeciesData[] extensions;
|
||||
|
||||
/*non-public*/ int fieldCount() {
|
||||
return typeCodes.length;
|
||||
@ -290,9 +301,14 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
/*non-public*/ char fieldTypeChar(int i) {
|
||||
return typeChars.charAt(i);
|
||||
}
|
||||
|
||||
Object fieldSignature() {
|
||||
return typeChars;
|
||||
}
|
||||
public Class<? extends BoundMethodHandle> fieldHolder() {
|
||||
return clazz;
|
||||
}
|
||||
public String toString() {
|
||||
return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+typeChars+"]";
|
||||
return "SpeciesData<"+fieldSignature()+">";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,7 +317,20 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* getter.
|
||||
*/
|
||||
NamedFunction getterFunction(int i) {
|
||||
return nominalGetters[i];
|
||||
NamedFunction nf = nominalGetters[i];
|
||||
assert(nf.memberDeclaringClassOrNull() == fieldHolder());
|
||||
assert(nf.returnType() == fieldType(i));
|
||||
return nf;
|
||||
}
|
||||
|
||||
NamedFunction[] getterFunctions() {
|
||||
return nominalGetters;
|
||||
}
|
||||
|
||||
MethodHandle[] getterHandles() { return getters; }
|
||||
|
||||
MethodHandle constructor() {
|
||||
return constructor[0];
|
||||
}
|
||||
|
||||
static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
|
||||
@ -324,7 +353,7 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
private void initForBootstrap() {
|
||||
assert(!INIT_DONE);
|
||||
if (constructor[0] == null) {
|
||||
if (constructor() == null) {
|
||||
String types = typeChars;
|
||||
Factory.makeCtors(clazz, types, this.constructor);
|
||||
Factory.makeGetters(clazz, types, this.getters);
|
||||
@ -508,19 +537,19 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
* return new Species_LLI(mt, lf, argL0, argL1, argI2);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
* return SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
* return SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
* return SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
* return SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
* return SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
@ -575,16 +604,6 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// emit implementation of reinvokerTarget()
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
|
||||
mv.visitTypeInsn(CHECKCAST, MH);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// emit implementation of speciesData()
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
|
||||
mv.visitCode();
|
||||
@ -653,16 +672,14 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
char btChar = type.basicTypeChar();
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE);
|
||||
mv.visitCode();
|
||||
// return SPECIES_DATA.extendWith(t).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
|
||||
// return SPECIES_DATA.extendWith(t).constructor().invokeBasic(mt, lf, argL0, ..., narg)
|
||||
// obtain constructor
|
||||
mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
|
||||
int iconstInsn = ICONST_0 + ord;
|
||||
assert(iconstInsn <= ICONST_5);
|
||||
mv.visitInsn(iconstInsn);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false);
|
||||
mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
|
||||
mv.visitInsn(ICONST_0);
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false);
|
||||
// load mt, lf
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
|
@ -102,7 +102,7 @@ public class CallSite {
|
||||
*/
|
||||
/*package-private*/
|
||||
CallSite(MethodType type) {
|
||||
target = type.invokers().uninitializedCallSite();
|
||||
target = makeUninitializedCallSite(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,27 +211,40 @@ public class CallSite {
|
||||
public abstract MethodHandle dynamicInvoker();
|
||||
|
||||
/*non-public*/ MethodHandle makeDynamicInvoker() {
|
||||
MethodHandle getTarget = GET_TARGET.bindReceiver(this);
|
||||
MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this);
|
||||
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
|
||||
return MethodHandles.foldArguments(invoker, getTarget);
|
||||
}
|
||||
|
||||
private static final MethodHandle GET_TARGET;
|
||||
private static final MethodHandle THROW_UCS;
|
||||
static {
|
||||
try {
|
||||
GET_TARGET = IMPL_LOOKUP.
|
||||
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
THROW_UCS = IMPL_LOOKUP.
|
||||
findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
|
||||
/*package-private*/
|
||||
static Empty uninitializedCallSite() {
|
||||
private static Object uninitializedCallSite(Object... ignore) {
|
||||
throw new IllegalStateException("uninitialized call site");
|
||||
}
|
||||
|
||||
private MethodHandle makeUninitializedCallSite(MethodType targetType) {
|
||||
MethodType basicType = targetType.basicType();
|
||||
MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS);
|
||||
if (invoker == null) {
|
||||
invoker = THROW_UCS.asType(basicType);
|
||||
invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker);
|
||||
}
|
||||
// unchecked view is OK since no values will be received or returned
|
||||
return invoker.viewAsType(targetType, false);
|
||||
}
|
||||
|
||||
// unsafe stuff:
|
||||
private static final long TARGET_OFFSET;
|
||||
static {
|
||||
@ -319,7 +332,7 @@ public class CallSite {
|
||||
throw new ClassCastException("bootstrap method failed to produce a CallSite");
|
||||
}
|
||||
if (!site.getTarget().type().equals(type))
|
||||
throw new WrongMethodTypeException("wrong type: "+site.getTarget());
|
||||
throw wrongTargetType(site.getTarget(), type);
|
||||
} catch (Throwable ex) {
|
||||
BootstrapMethodError bex;
|
||||
if (ex instanceof BootstrapMethodError)
|
||||
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* A method handle whose invocation behavior is determined by a target.
|
||||
* The delegating MH itself can hold extra "intentions" beyond the simple behavior.
|
||||
* @author jrose
|
||||
*/
|
||||
/*non-public*/
|
||||
abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
protected DelegatingMethodHandle(MethodHandle target) {
|
||||
this(target.type(), target);
|
||||
}
|
||||
|
||||
protected DelegatingMethodHandle(MethodType type, MethodHandle target) {
|
||||
super(type, chooseDelegatingForm(target));
|
||||
}
|
||||
|
||||
/** Define this to extract the delegated target which supplies the invocation behavior. */
|
||||
abstract protected MethodHandle getTarget();
|
||||
|
||||
@Override
|
||||
abstract MethodHandle asTypeUncached(MethodType newType);
|
||||
|
||||
@Override
|
||||
MemberName internalMemberName() {
|
||||
return getTarget().internalMemberName();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isInvokeSpecial() {
|
||||
return getTarget().isInvokeSpecial();
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> internalCallerClass() {
|
||||
return getTarget().internalCallerClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
// FIXME: rethink 'copyWith' protocol; it is too low-level for use on all MHs
|
||||
throw newIllegalArgumentException("do not use this");
|
||||
}
|
||||
|
||||
@Override
|
||||
String internalProperties() {
|
||||
return "\n& Class="+getClass().getSimpleName()+
|
||||
"\n& Target="+getTarget().debugString();
|
||||
}
|
||||
|
||||
@Override
|
||||
BoundMethodHandle rebind() {
|
||||
return getTarget().rebind();
|
||||
}
|
||||
|
||||
private static LambdaForm chooseDelegatingForm(MethodHandle target) {
|
||||
if (target instanceof SimpleMethodHandle)
|
||||
return target.internalForm(); // no need for an indirection
|
||||
return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget);
|
||||
}
|
||||
|
||||
/** Create a LF which simply reinvokes a target of the given basic type. */
|
||||
static LambdaForm makeReinvokerForm(MethodHandle target,
|
||||
int whichCache,
|
||||
Object constraint,
|
||||
NamedFunction getTargetFn) {
|
||||
MethodType mtype = target.type().basicType();
|
||||
boolean customized = (whichCache < 0 ||
|
||||
mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY);
|
||||
LambdaForm form;
|
||||
if (!customized) {
|
||||
form = mtype.form().cachedLambdaForm(whichCache);
|
||||
if (form != null) return form;
|
||||
}
|
||||
final int THIS_DMH = 0;
|
||||
final int ARG_BASE = 1;
|
||||
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int NEXT_MH = customized ? -1 : nameCursor++;
|
||||
final int REINVOKE = nameCursor++;
|
||||
LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||
assert(names.length == nameCursor);
|
||||
names[THIS_DMH] = names[THIS_DMH].withConstraint(constraint);
|
||||
Object[] targetArgs;
|
||||
if (customized) {
|
||||
targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
|
||||
names[REINVOKE] = new LambdaForm.Name(target, targetArgs); // the invoker is the target itself
|
||||
} else {
|
||||
names[NEXT_MH] = new LambdaForm.Name(getTargetFn, names[THIS_DMH]);
|
||||
targetArgs = Arrays.copyOfRange(names, THIS_DMH, ARG_LIMIT, Object[].class);
|
||||
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
|
||||
names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs);
|
||||
}
|
||||
String debugString;
|
||||
switch(whichCache) {
|
||||
case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break;
|
||||
case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break;
|
||||
default: debugString = "MH.reinvoke"; break;
|
||||
}
|
||||
form = new LambdaForm(debugString, ARG_LIMIT, names);
|
||||
if (!customized) {
|
||||
form = mtype.form().setCachedLambdaForm(whichCache, form);
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
private static final NamedFunction NF_getTarget;
|
||||
static {
|
||||
try {
|
||||
NF_getTarget = new NamedFunction(DelegatingMethodHandle.class
|
||||
.getDeclaredMethod("getTarget"));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -59,6 +59,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind());
|
||||
m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null);
|
||||
if (m != null && m.isPublic()) {
|
||||
assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong
|
||||
member = m;
|
||||
}
|
||||
}
|
||||
@ -125,61 +126,31 @@ class DirectMethodHandle extends MethodHandle {
|
||||
return new Constructor(mtype, lform, ctor, init, instanceClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
BoundMethodHandle rebind() {
|
||||
return BoundMethodHandle.makeReinvoker(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses
|
||||
return new DirectMethodHandle(mt, lf, member);
|
||||
}
|
||||
|
||||
@Override
|
||||
String internalProperties() {
|
||||
return "/DMH="+member.toString();
|
||||
return "\n& DMH.MN="+internalMemberName();
|
||||
}
|
||||
|
||||
//// Implementation methods.
|
||||
@Override
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
return new DirectMethodHandle(newType, form, member);
|
||||
}
|
||||
@Override
|
||||
@ForceInline
|
||||
MemberName internalMemberName() {
|
||||
return member;
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
|
||||
// If the member needs dispatching, do so.
|
||||
if (pos == 0 && basicType == L_TYPE) {
|
||||
DirectMethodHandle concrete = maybeRebind(value);
|
||||
if (concrete != null)
|
||||
return concrete.bindReceiver(value);
|
||||
}
|
||||
return super.bindArgument(pos, basicType, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle bindReceiver(Object receiver) {
|
||||
// If the member needs dispatching, do so.
|
||||
DirectMethodHandle concrete = maybeRebind(receiver);
|
||||
if (concrete != null)
|
||||
return concrete.bindReceiver(receiver);
|
||||
return super.bindReceiver(receiver);
|
||||
}
|
||||
|
||||
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
|
||||
|
||||
private DirectMethodHandle maybeRebind(Object receiver) {
|
||||
if (receiver != null) {
|
||||
switch (member.getReferenceKind()) {
|
||||
case REF_invokeInterface:
|
||||
case REF_invokeVirtual:
|
||||
// Pre-dispatch the member.
|
||||
Class<?> concreteClass = receiver.getClass();
|
||||
MemberName concrete = new MemberName(concreteClass, member.getName(), member.getMethodType(), REF_invokeSpecial);
|
||||
concrete = IMPL_NAMES.resolveOrNull(REF_invokeSpecial, concrete, concreteClass);
|
||||
if (concrete != null)
|
||||
return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LF which can invoke the given method.
|
||||
* Cache and share this structure among all methods with
|
||||
@ -260,9 +231,10 @@ class DirectMethodHandle extends MethodHandle {
|
||||
} else {
|
||||
names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
|
||||
}
|
||||
assert(findDirectMethodHandle(names[GET_MEMBER]) == 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!
|
||||
int result = LambdaForm.LAST_RESULT;
|
||||
int result = LAST_RESULT;
|
||||
if (doesAlloc) {
|
||||
assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one
|
||||
System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
|
||||
@ -277,6 +249,16 @@ class DirectMethodHandle extends MethodHandle {
|
||||
return lform;
|
||||
}
|
||||
|
||||
static Object findDirectMethodHandle(Name name) {
|
||||
if (name.function == Lazy.NF_internalMemberName ||
|
||||
name.function == Lazy.NF_internalMemberNameEnsureInit ||
|
||||
name.function == Lazy.NF_constructorMethod) {
|
||||
assert(name.arguments.length == 1);
|
||||
return name.arguments[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void maybeCompile(LambdaForm lform, MemberName m) {
|
||||
if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
|
||||
// Help along bootstrapping...
|
||||
@ -389,8 +371,8 @@ class DirectMethodHandle extends MethodHandle {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
return new Special(newType, form, member);
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Special(mt, lf, member);
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,8 +389,8 @@ class DirectMethodHandle extends MethodHandle {
|
||||
assert(initMethod.isResolved());
|
||||
}
|
||||
@Override
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
return new Constructor(newType, form, member, initMethod, instanceClass);
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Constructor(mt, lf, member, initMethod, instanceClass);
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,8 +419,8 @@ class DirectMethodHandle extends MethodHandle {
|
||||
return fieldType.cast(obj);
|
||||
}
|
||||
@Override
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
return new Accessor(newType, form, member, fieldOffset);
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Accessor(mt, lf, member, fieldOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,8 +462,8 @@ class DirectMethodHandle extends MethodHandle {
|
||||
return fieldType.cast(obj);
|
||||
}
|
||||
@Override
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
return new StaticAccessor(newType, form, member, staticBase, staticOffset);
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new StaticAccessor(mt, lf, member, staticBase, staticOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,21 +25,20 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import sun.invoke.util.VerifyAccess;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
|
||||
import sun.invoke.util.VerifyAccess;
|
||||
import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
||||
/**
|
||||
@ -74,7 +73,11 @@ class InvokerBytecodeGenerator {
|
||||
private final LambdaForm lambdaForm;
|
||||
private final String invokerName;
|
||||
private final MethodType invokerType;
|
||||
private final int[] localsMap;
|
||||
|
||||
/** Info about local variables in compiled lambda form */
|
||||
private final int[] localsMap; // index
|
||||
private final BasicType[] localTypes; // basic type
|
||||
private final Class<?>[] localClasses; // type
|
||||
|
||||
/** ASM bytecode generation. */
|
||||
private ClassWriter cw;
|
||||
@ -83,6 +86,7 @@ class InvokerBytecodeGenerator {
|
||||
private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
|
||||
private static final Class<?> HOST_CLASS = LambdaForm.class;
|
||||
|
||||
/** Main constructor; other constructors delegate to this one. */
|
||||
private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
|
||||
String className, String invokerName, MethodType invokerType) {
|
||||
if (invokerName.contains(".")) {
|
||||
@ -98,18 +102,26 @@ class InvokerBytecodeGenerator {
|
||||
this.lambdaForm = lambdaForm;
|
||||
this.invokerName = invokerName;
|
||||
this.invokerType = invokerType;
|
||||
this.localsMap = new int[localsMapSize];
|
||||
this.localsMap = new int[localsMapSize+1];
|
||||
// last entry of localsMap is count of allocated local slots
|
||||
this.localTypes = new BasicType[localsMapSize+1];
|
||||
this.localClasses = new Class<?>[localsMapSize+1];
|
||||
}
|
||||
|
||||
/** For generating LambdaForm interpreter entry points. */
|
||||
private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
|
||||
this(null, invokerType.parameterCount(),
|
||||
className, invokerName, invokerType);
|
||||
// Create an array to map name indexes to locals indexes.
|
||||
localTypes[localTypes.length - 1] = V_TYPE;
|
||||
for (int i = 0; i < localsMap.length; i++) {
|
||||
localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
|
||||
if (i < invokerType.parameterCount())
|
||||
localTypes[i] = basicType(invokerType.parameterType(i));
|
||||
}
|
||||
}
|
||||
|
||||
/** For generating customized code for a single LambdaForm. */
|
||||
private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
|
||||
this(form, form.names.length,
|
||||
className, form.debugName, invokerType);
|
||||
@ -117,7 +129,11 @@ class InvokerBytecodeGenerator {
|
||||
Name[] names = form.names;
|
||||
for (int i = 0, index = 0; i < localsMap.length; i++) {
|
||||
localsMap[i] = index;
|
||||
index += names[i].type.basicTypeSlots();
|
||||
if (i < names.length) {
|
||||
BasicType type = names[i].type();
|
||||
index += type.basicTypeSlots();
|
||||
localTypes[i] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +164,6 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
static void maybeDump(final String className, final byte[] classFile) {
|
||||
if (DUMP_CLASS_FILES) {
|
||||
System.out.println("dump: " + className);
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
@ -156,6 +171,7 @@ class InvokerBytecodeGenerator {
|
||||
String dumpName = className;
|
||||
//dumpName = dumpName.replace('/', '-');
|
||||
File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
|
||||
System.out.println("dump: " + dumpFile);
|
||||
dumpFile.getParentFile().mkdirs();
|
||||
FileOutputStream file = new FileOutputStream(dumpFile);
|
||||
file.write(classFile);
|
||||
@ -204,7 +220,7 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
String constantPlaceholder(Object arg) {
|
||||
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
|
||||
if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid
|
||||
if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>"; // debugging aid
|
||||
if (cpPatches.containsKey(cpPlaceholder)) {
|
||||
throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
|
||||
}
|
||||
@ -225,6 +241,17 @@ class InvokerBytecodeGenerator {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static String debugString(Object arg) {
|
||||
if (arg instanceof MethodHandle) {
|
||||
MethodHandle mh = (MethodHandle) arg;
|
||||
MemberName member = mh.internalMemberName();
|
||||
if (member != null)
|
||||
return member.toString();
|
||||
return mh.debugString();
|
||||
}
|
||||
return arg.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the number of constant pool entries from a given class file.
|
||||
*
|
||||
@ -400,6 +427,64 @@ class InvokerBytecodeGenerator {
|
||||
emitStoreInsn(L_TYPE, index);
|
||||
}
|
||||
|
||||
private byte arrayTypeCode(Wrapper elementType) {
|
||||
switch (elementType) {
|
||||
case BOOLEAN: return Opcodes.T_BOOLEAN;
|
||||
case BYTE: return Opcodes.T_BYTE;
|
||||
case CHAR: return Opcodes.T_CHAR;
|
||||
case SHORT: return Opcodes.T_SHORT;
|
||||
case INT: return Opcodes.T_INT;
|
||||
case LONG: return Opcodes.T_LONG;
|
||||
case FLOAT: return Opcodes.T_FLOAT;
|
||||
case DOUBLE: return Opcodes.T_DOUBLE;
|
||||
case OBJECT: return 0; // in place of Opcodes.T_OBJECT
|
||||
default: throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError {
|
||||
assert(aaop == Opcodes.AASTORE || aaop == Opcodes.AALOAD);
|
||||
int xas;
|
||||
switch (tcode) {
|
||||
case Opcodes.T_BOOLEAN: xas = Opcodes.BASTORE; break;
|
||||
case Opcodes.T_BYTE: xas = Opcodes.BASTORE; break;
|
||||
case Opcodes.T_CHAR: xas = Opcodes.CASTORE; break;
|
||||
case Opcodes.T_SHORT: xas = Opcodes.SASTORE; break;
|
||||
case Opcodes.T_INT: xas = Opcodes.IASTORE; break;
|
||||
case Opcodes.T_LONG: xas = Opcodes.LASTORE; break;
|
||||
case Opcodes.T_FLOAT: xas = Opcodes.FASTORE; break;
|
||||
case Opcodes.T_DOUBLE: xas = Opcodes.DASTORE; break;
|
||||
case 0: xas = Opcodes.AASTORE; break;
|
||||
default: throw new InternalError();
|
||||
}
|
||||
return xas - Opcodes.AASTORE + aaop;
|
||||
}
|
||||
|
||||
|
||||
private void freeFrameLocal(int oldFrameLocal) {
|
||||
int i = indexForFrameLocal(oldFrameLocal);
|
||||
if (i < 0) return;
|
||||
BasicType type = localTypes[i];
|
||||
int newFrameLocal = makeLocalTemp(type);
|
||||
mv.visitVarInsn(loadInsnOpcode(type), oldFrameLocal);
|
||||
mv.visitVarInsn(storeInsnOpcode(type), newFrameLocal);
|
||||
assert(localsMap[i] == oldFrameLocal);
|
||||
localsMap[i] = newFrameLocal;
|
||||
assert(indexForFrameLocal(oldFrameLocal) < 0);
|
||||
}
|
||||
private int indexForFrameLocal(int frameLocal) {
|
||||
for (int i = 0; i < localsMap.length; i++) {
|
||||
if (localsMap[i] == frameLocal && localTypes[i] != V_TYPE)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
private int makeLocalTemp(BasicType type) {
|
||||
int frameLocal = localsMap[localsMap.length - 1];
|
||||
localsMap[localsMap.length - 1] = frameLocal + type.basicTypeSlots();
|
||||
return frameLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a boxing call.
|
||||
*
|
||||
@ -421,41 +506,79 @@ class InvokerBytecodeGenerator {
|
||||
String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
|
||||
String name = wrapper.primitiveSimpleName() + "Value";
|
||||
String desc = "()" + wrapper.basicTypeChar();
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
|
||||
emitReferenceCast(wrapper.wrapperType(), null);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an implicit conversion.
|
||||
* Emit an implicit conversion for an argument which must be of the given pclass.
|
||||
* This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
|
||||
*
|
||||
* @param ptype type of value present on stack
|
||||
* @param pclass type of value required on stack
|
||||
* @param arg compile-time representation of value on stack (Node, constant) or null if none
|
||||
*/
|
||||
private void emitImplicitConversion(BasicType ptype, Class<?> pclass) {
|
||||
private void emitImplicitConversion(BasicType ptype, Class<?> pclass, Object arg) {
|
||||
assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller
|
||||
if (pclass == ptype.basicTypeClass() && ptype != L_TYPE)
|
||||
return; // nothing to do
|
||||
switch (ptype) {
|
||||
case L_TYPE:
|
||||
if (VerifyType.isNullConversion(Object.class, pclass))
|
||||
case L_TYPE:
|
||||
if (VerifyType.isNullConversion(Object.class, pclass, false)) {
|
||||
if (PROFILE_LEVEL > 0)
|
||||
emitReferenceCast(Object.class, arg);
|
||||
return;
|
||||
}
|
||||
emitReferenceCast(pclass, arg);
|
||||
return;
|
||||
case I_TYPE:
|
||||
if (!VerifyType.isNullConversion(int.class, pclass, false))
|
||||
emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
|
||||
return;
|
||||
if (isStaticallyNameable(pclass)) {
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(pclass));
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false);
|
||||
if (pclass.isArray())
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
|
||||
}
|
||||
return;
|
||||
case I_TYPE:
|
||||
if (!VerifyType.isNullConversion(int.class, pclass))
|
||||
emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
|
||||
return;
|
||||
}
|
||||
throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
|
||||
throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass);
|
||||
}
|
||||
|
||||
/** Update localClasses type map. Return true if the information is already present. */
|
||||
private boolean assertStaticType(Class<?> cls, Name n) {
|
||||
int local = n.index();
|
||||
Class<?> aclass = localClasses[local];
|
||||
if (aclass != null && (aclass == cls || cls.isAssignableFrom(aclass))) {
|
||||
return true; // type info is already present
|
||||
} else if (aclass == null || aclass.isAssignableFrom(cls)) {
|
||||
localClasses[local] = cls; // type info can be improved
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void emitReferenceCast(Class<?> cls, Object arg) {
|
||||
Name writeBack = null; // local to write back result
|
||||
if (arg instanceof Name) {
|
||||
Name n = (Name) arg;
|
||||
if (assertStaticType(cls, n))
|
||||
return; // this cast was already performed
|
||||
if (lambdaForm.useCount(n) > 1) {
|
||||
// This guy gets used more than once.
|
||||
writeBack = n;
|
||||
}
|
||||
}
|
||||
if (isStaticallyNameable(cls)) {
|
||||
String sig = getInternalName(cls);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(cls));
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false);
|
||||
if (Object[].class.isAssignableFrom(cls))
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
|
||||
else if (PROFILE_LEVEL > 0)
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
|
||||
}
|
||||
if (writeBack != null) {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
emitAstoreInsn(writeBack.index());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -477,7 +600,11 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
private static String getInternalName(Class<?> c) {
|
||||
assert(VerifyAccess.isTypeVisible(c, Object.class));
|
||||
if (c == Object.class) return OBJ;
|
||||
else if (c == Object[].class) return OBJARY;
|
||||
else if (c == Class.class) return CLS;
|
||||
else if (c == MethodHandle.class) return MH;
|
||||
assert(VerifyAccess.isTypeVisible(c, Object.class)) : c.getName();
|
||||
return c.getName().replace('.', '/');
|
||||
}
|
||||
|
||||
@ -506,39 +633,62 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
// iterate over the form's names, generating bytecode instructions for each
|
||||
// start iterating at the first name following the arguments
|
||||
Name onStack = null;
|
||||
for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
|
||||
Name name = lambdaForm.names[i];
|
||||
MemberName member = name.function.member();
|
||||
|
||||
if (isSelectAlternative(i)) {
|
||||
emitSelectAlternative(name, lambdaForm.names[i + 1]);
|
||||
i++; // skip MH.invokeBasic of the selectAlternative result
|
||||
} else if (isGuardWithCatch(i)) {
|
||||
emitGuardWithCatch(i);
|
||||
i = i+2; // Jump to the end of GWC idiom
|
||||
} else if (isStaticallyInvocable(member)) {
|
||||
emitStoreResult(onStack);
|
||||
onStack = name; // unless otherwise modified below
|
||||
MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
|
||||
switch (intr) {
|
||||
case SELECT_ALTERNATIVE:
|
||||
assert isSelectAlternative(i);
|
||||
onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
|
||||
i++; // skip MH.invokeBasic of the selectAlternative result
|
||||
continue;
|
||||
case GUARD_WITH_CATCH:
|
||||
assert isGuardWithCatch(i);
|
||||
onStack = emitGuardWithCatch(i);
|
||||
i = i+2; // Jump to the end of GWC idiom
|
||||
continue;
|
||||
case NEW_ARRAY:
|
||||
Class<?> rtype = name.function.methodType().returnType();
|
||||
if (isStaticallyNameable(rtype)) {
|
||||
emitNewArray(name);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ARRAY_LOAD:
|
||||
emitArrayLoad(name);
|
||||
continue;
|
||||
case ARRAY_STORE:
|
||||
emitArrayStore(name);
|
||||
continue;
|
||||
case IDENTITY:
|
||||
assert(name.arguments.length == 1);
|
||||
emitPushArguments(name);
|
||||
continue;
|
||||
case ZERO:
|
||||
assert(name.arguments.length == 0);
|
||||
emitConst(name.type.basicTypeWrapper().zero());
|
||||
continue;
|
||||
case NONE:
|
||||
// no intrinsic associated
|
||||
break;
|
||||
default:
|
||||
throw newInternalError("Unknown intrinsic: "+intr);
|
||||
}
|
||||
|
||||
MemberName member = name.function.member();
|
||||
if (isStaticallyInvocable(member)) {
|
||||
emitStaticInvoke(member, name);
|
||||
} else {
|
||||
emitInvoke(name);
|
||||
}
|
||||
|
||||
// Update cached form name's info in case an intrinsic spanning multiple names was encountered.
|
||||
name = lambdaForm.names[i];
|
||||
member = name.function.member();
|
||||
|
||||
// store the result from evaluating to the target name in a local if required
|
||||
// (if this is the last value, i.e., the one that is going to be returned,
|
||||
// avoid store/load/return and just return)
|
||||
if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
|
||||
// return value - do nothing
|
||||
} else if (name.type != V_TYPE) {
|
||||
// non-void: actually assign
|
||||
emitStoreInsn(name.type, name.index());
|
||||
}
|
||||
}
|
||||
|
||||
// return statement
|
||||
emitReturn();
|
||||
emitReturn(onStack);
|
||||
|
||||
classFileEpilogue();
|
||||
bogusMethod(lambdaForm);
|
||||
@ -548,29 +698,43 @@ class InvokerBytecodeGenerator {
|
||||
return classFile;
|
||||
}
|
||||
|
||||
void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); }
|
||||
void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); }
|
||||
|
||||
void emitArrayOp(Name name, int arrayOpcode) {
|
||||
assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE;
|
||||
Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
|
||||
assert elementType != null;
|
||||
emitPushArguments(name);
|
||||
if (elementType.isPrimitive()) {
|
||||
Wrapper w = Wrapper.forPrimitiveType(elementType);
|
||||
arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
|
||||
}
|
||||
mv.visitInsn(arrayOpcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an invoke for the given name.
|
||||
*/
|
||||
void emitInvoke(Name name) {
|
||||
assert(!isLinkerMethodInvoke(name)); // should use the static path for these
|
||||
if (true) {
|
||||
// push receiver
|
||||
MethodHandle target = name.function.resolvedHandle;
|
||||
assert(target != null) : name.exprString();
|
||||
mv.visitLdcInsn(constantPlaceholder(target));
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
|
||||
emitReferenceCast(MethodHandle.class, target);
|
||||
} else {
|
||||
// load receiver
|
||||
emitAloadInsn(0);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
|
||||
emitReferenceCast(MethodHandle.class, null);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
|
||||
// TODO more to come
|
||||
}
|
||||
|
||||
// push arguments
|
||||
for (int i = 0; i < name.arguments.length; i++) {
|
||||
emitPushArgument(name, i);
|
||||
}
|
||||
emitPushArguments(name);
|
||||
|
||||
// invocation
|
||||
MethodType type = name.function.methodType();
|
||||
@ -585,6 +749,10 @@ class InvokerBytecodeGenerator {
|
||||
//MethodHandle.class already covered
|
||||
};
|
||||
|
||||
static boolean isStaticallyInvocable(Name name) {
|
||||
return isStaticallyInvocable(name.function.member());
|
||||
}
|
||||
|
||||
static boolean isStaticallyInvocable(MemberName member) {
|
||||
if (member == null) return false;
|
||||
if (member.isConstructor()) return false;
|
||||
@ -611,6 +779,8 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
static boolean isStaticallyNameable(Class<?> cls) {
|
||||
if (cls == Object.class)
|
||||
return true;
|
||||
while (cls.isArray())
|
||||
cls = cls.getComponentType();
|
||||
if (cls.isPrimitive())
|
||||
@ -631,12 +801,17 @@ class InvokerBytecodeGenerator {
|
||||
return false;
|
||||
}
|
||||
|
||||
void emitStaticInvoke(Name name) {
|
||||
emitStaticInvoke(name.function.member(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an invoke for the given name, using the MemberName directly.
|
||||
*/
|
||||
void emitStaticInvoke(MemberName member, Name name) {
|
||||
assert(member.equals(name.function.member()));
|
||||
String cname = getInternalName(member.getDeclaringClass());
|
||||
Class<?> defc = member.getDeclaringClass();
|
||||
String cname = getInternalName(defc);
|
||||
String mname = member.getName();
|
||||
String mtype;
|
||||
byte refKind = member.getReferenceKind();
|
||||
@ -653,9 +828,7 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
// push arguments
|
||||
for (int i = 0; i < name.arguments.length; i++) {
|
||||
emitPushArgument(name, i);
|
||||
}
|
||||
emitPushArguments(name);
|
||||
|
||||
// invocation
|
||||
if (member.isMethod()) {
|
||||
@ -666,6 +839,52 @@ class InvokerBytecodeGenerator {
|
||||
mtype = MethodType.toFieldDescriptorString(member.getFieldType());
|
||||
mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
|
||||
}
|
||||
// Issue a type assertion for the result, so we can avoid casts later.
|
||||
if (name.type == L_TYPE) {
|
||||
Class<?> rtype = member.getInvocationType().returnType();
|
||||
assert(!rtype.isPrimitive());
|
||||
if (rtype != Object.class && !rtype.isInterface()) {
|
||||
assertStaticType(rtype, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void emitNewArray(Name name) throws InternalError {
|
||||
Class<?> rtype = name.function.methodType().returnType();
|
||||
if (name.arguments.length == 0) {
|
||||
// The array will be a constant.
|
||||
Object emptyArray;
|
||||
try {
|
||||
emptyArray = name.function.resolvedHandle.invoke();
|
||||
} catch (Throwable ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
|
||||
assert(emptyArray.getClass() == rtype); // exact typing
|
||||
mv.visitLdcInsn(constantPlaceholder(emptyArray));
|
||||
emitReferenceCast(rtype, emptyArray);
|
||||
return;
|
||||
}
|
||||
Class<?> arrayElementType = rtype.getComponentType();
|
||||
assert(arrayElementType != null);
|
||||
emitIconstInsn(name.arguments.length);
|
||||
int xas = Opcodes.AASTORE;
|
||||
if (!arrayElementType.isPrimitive()) {
|
||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
|
||||
} else {
|
||||
byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
|
||||
xas = arrayInsnOpcode(tc, xas);
|
||||
mv.visitIntInsn(Opcodes.NEWARRAY, tc);
|
||||
}
|
||||
// store arguments
|
||||
for (int i = 0; i < name.arguments.length; i++) {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
emitIconstInsn(i);
|
||||
emitPushArgument(name, i);
|
||||
mv.visitInsn(xas);
|
||||
}
|
||||
// the array is left on the stack
|
||||
assertStaticType(rtype, name);
|
||||
}
|
||||
int refKindOpcode(byte refKind) {
|
||||
switch (refKind) {
|
||||
@ -707,6 +926,21 @@ class InvokerBytecodeGenerator {
|
||||
!member.isPublic() && !member.isStatic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if MemberName is a call to MethodHandle.linkToStatic, etc.
|
||||
*/
|
||||
private boolean isLinkerMethodInvoke(Name name) {
|
||||
if (name.function == null)
|
||||
return false;
|
||||
if (name.arguments.length < 1)
|
||||
return false; // must have MH argument
|
||||
MemberName member = name.function.member();
|
||||
return member != null &&
|
||||
member.getDeclaringClass() == MethodHandle.class &&
|
||||
!member.isPublic() && member.isStatic() &&
|
||||
member.getName().startsWith("linkTo");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if i-th name is a call to MethodHandleImpl.selectAlternative.
|
||||
*/
|
||||
@ -755,7 +989,9 @@ class InvokerBytecodeGenerator {
|
||||
* t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
|
||||
* }</pre></blockquote>
|
||||
*/
|
||||
private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
|
||||
private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
|
||||
assert isStaticallyInvocable(invokeBasicName);
|
||||
|
||||
Name receiver = (Name) invokeBasicName.arguments[0];
|
||||
|
||||
Label L_fallback = new Label();
|
||||
@ -763,15 +999,15 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
// load test result
|
||||
emitPushArgument(selectAlternativeName, 0);
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
|
||||
// if_icmpne L_fallback
|
||||
mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
|
||||
mv.visitJumpInsn(Opcodes.IFEQ, L_fallback);
|
||||
|
||||
// invoke selectAlternativeName.arguments[1]
|
||||
Class<?>[] preForkClasses = localClasses.clone();
|
||||
emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
|
||||
emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
|
||||
emitInvoke(invokeBasicName);
|
||||
emitStaticInvoke(invokeBasicName);
|
||||
|
||||
// goto L_done
|
||||
mv.visitJumpInsn(Opcodes.GOTO, L_done);
|
||||
@ -780,12 +1016,17 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitLabel(L_fallback);
|
||||
|
||||
// invoke selectAlternativeName.arguments[2]
|
||||
System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
|
||||
emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
|
||||
emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
|
||||
emitInvoke(invokeBasicName);
|
||||
emitStaticInvoke(invokeBasicName);
|
||||
|
||||
// L_done:
|
||||
mv.visitLabel(L_done);
|
||||
// for now do not bother to merge typestate; just reset to the dominator state
|
||||
System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
|
||||
|
||||
return invokeBasicName; // return what's on stack
|
||||
}
|
||||
|
||||
/**
|
||||
@ -808,7 +1049,7 @@ class InvokerBytecodeGenerator {
|
||||
* return a3.invokeBasic(ex, a6, a7);
|
||||
* }}
|
||||
*/
|
||||
private void emitGuardWithCatch(int pos) {
|
||||
private Name emitGuardWithCatch(int pos) {
|
||||
Name args = lambdaForm.names[pos];
|
||||
Name invoker = lambdaForm.names[pos+1];
|
||||
Name result = lambdaForm.names[pos+2];
|
||||
@ -859,6 +1100,12 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(L_done);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void emitPushArguments(Name args) {
|
||||
emitPushArguments(args, 0);
|
||||
}
|
||||
|
||||
private void emitPushArguments(Name args, int start) {
|
||||
@ -878,7 +1125,7 @@ class InvokerBytecodeGenerator {
|
||||
if (arg instanceof Name) {
|
||||
Name n = (Name) arg;
|
||||
emitLoadInsn(n.type, n.index());
|
||||
emitImplicitConversion(n.type, ptype);
|
||||
emitImplicitConversion(n.type, ptype, n);
|
||||
} else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
|
||||
emitConst(arg);
|
||||
} else {
|
||||
@ -886,15 +1133,25 @@ class InvokerBytecodeGenerator {
|
||||
emitConst(arg);
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(arg));
|
||||
emitImplicitConversion(L_TYPE, ptype);
|
||||
emitImplicitConversion(L_TYPE, ptype, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the name to its local, if necessary.
|
||||
*/
|
||||
private void emitStoreResult(Name name) {
|
||||
if (name != null && name.type != V_TYPE) {
|
||||
// non-void: actually assign
|
||||
emitStoreInsn(name.type, name.index());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
|
||||
*/
|
||||
private void emitReturn() {
|
||||
private void emitReturn(Name onStack) {
|
||||
// return statement
|
||||
Class<?> rclass = invokerType.returnType();
|
||||
BasicType rtype = lambdaForm.returnType();
|
||||
@ -907,12 +1164,11 @@ class InvokerBytecodeGenerator {
|
||||
LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
|
||||
|
||||
// put return value on the stack if it is not already there
|
||||
if (lambdaForm.result != lambdaForm.names.length - 1 ||
|
||||
lambdaForm.result < lambdaForm.arity) {
|
||||
emitLoadInsn(rn.type, lambdaForm.result);
|
||||
if (rn != onStack) {
|
||||
emitLoadInsn(rtype, lambdaForm.result);
|
||||
}
|
||||
|
||||
emitImplicitConversion(rtype, rclass);
|
||||
emitImplicitConversion(rtype, rclass, rn);
|
||||
|
||||
// generate actual return statement
|
||||
emitReturnInsn(rtype);
|
||||
|
@ -25,8 +25,9 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import sun.invoke.empty.Empty;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
@ -40,52 +41,63 @@ class Invokers {
|
||||
// exact type (sans leading target MH) for the outgoing call
|
||||
private final MethodType targetType;
|
||||
|
||||
// FIXME: Get rid of the invokers that are not useful.
|
||||
|
||||
// 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
|
||||
private /*lazy*/ MethodHandle erasedInvoker;
|
||||
// FIXME: get rid of
|
||||
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
|
||||
|
||||
// general invoker for the outgoing call
|
||||
private /*lazy*/ MethodHandle generalInvoker;
|
||||
|
||||
// general invoker for the outgoing call, uses varargs
|
||||
private /*lazy*/ MethodHandle varargsInvoker;
|
||||
|
||||
// general invoker for the outgoing call; accepts a trailing Object[]
|
||||
private final /*lazy*/ MethodHandle[] spreadInvokers;
|
||||
|
||||
// invoker for an unbound callsite
|
||||
private /*lazy*/ MethodHandle uninitializedCallSite;
|
||||
// Cached adapter information:
|
||||
private final @Stable MethodHandle[] invokers = new MethodHandle[INV_LIMIT];
|
||||
// Indexes into invokers:
|
||||
static final int
|
||||
INV_EXACT = 0, // MethodHandles.exactInvoker
|
||||
INV_GENERIC = 1, // MethodHandles.invoker (generic invocation)
|
||||
INV_BASIC = 2, // MethodHandles.basicInvoker
|
||||
INV_LIMIT = 3;
|
||||
|
||||
/** Compute and cache information common to all collecting adapters
|
||||
* that implement members of the erasure-family of the given erased type.
|
||||
*/
|
||||
/*non-public*/ Invokers(MethodType targetType) {
|
||||
this.targetType = targetType;
|
||||
this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle exactInvoker() {
|
||||
MethodHandle invoker = exactInvoker;
|
||||
MethodHandle invoker = cachedInvoker(INV_EXACT);
|
||||
if (invoker != null) return invoker;
|
||||
invoker = makeExactOrGeneralInvoker(true);
|
||||
exactInvoker = invoker;
|
||||
return invoker;
|
||||
return setCachedInvoker(INV_EXACT, invoker);
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle generalInvoker() {
|
||||
MethodHandle invoker = generalInvoker;
|
||||
/*non-public*/ MethodHandle genericInvoker() {
|
||||
MethodHandle invoker = cachedInvoker(INV_GENERIC);
|
||||
if (invoker != null) return invoker;
|
||||
invoker = makeExactOrGeneralInvoker(false);
|
||||
generalInvoker = invoker;
|
||||
return invoker;
|
||||
return setCachedInvoker(INV_GENERIC, invoker);
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle basicInvoker() {
|
||||
MethodHandle invoker = cachedInvoker(INV_BASIC);
|
||||
if (invoker != null) return invoker;
|
||||
MethodType basicType = targetType.basicType();
|
||||
if (basicType != targetType) {
|
||||
// double cache; not used significantly
|
||||
return setCachedInvoker(INV_BASIC, basicType.invokers().basicInvoker());
|
||||
}
|
||||
invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_BASIC_INV);
|
||||
if (invoker == null) {
|
||||
MemberName method = invokeBasicMethod(basicType);
|
||||
invoker = DirectMethodHandle.make(method);
|
||||
assert(checkInvoker(invoker));
|
||||
invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_BASIC_INV, invoker);
|
||||
}
|
||||
return setCachedInvoker(INV_BASIC, invoker);
|
||||
}
|
||||
|
||||
private MethodHandle cachedInvoker(int idx) {
|
||||
return invokers[idx];
|
||||
}
|
||||
|
||||
private synchronized MethodHandle setCachedInvoker(int idx, final MethodHandle invoker) {
|
||||
// Simulate a CAS, to avoid racy duplication of results.
|
||||
MethodHandle prev = invokers[idx];
|
||||
if (prev != null) return prev;
|
||||
return invokers[idx] = invoker;
|
||||
}
|
||||
|
||||
private MethodHandle makeExactOrGeneralInvoker(boolean isExact) {
|
||||
@ -95,7 +107,7 @@ class Invokers {
|
||||
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));
|
||||
invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke(whichName, mtype), false);
|
||||
assert(checkInvoker(invoker));
|
||||
maybeCompileToBytecode(invoker);
|
||||
return invoker;
|
||||
@ -110,21 +122,6 @@ class Invokers {
|
||||
}
|
||||
}
|
||||
|
||||
/*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());
|
||||
@ -145,87 +142,42 @@ class Invokers {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: get rid of
|
||||
/*non-public*/ MethodHandle erasedInvoker() {
|
||||
MethodHandle xinvoker = exactInvoker();
|
||||
MethodHandle invoker = erasedInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
MethodType erasedType = targetType.erase();
|
||||
invoker = xinvoker.asType(erasedType.invokerType());
|
||||
erasedInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create an invoker which passes unchanged a given number of arguments
|
||||
* and spreads the rest from a trailing array argument.
|
||||
* The invoker target type is the post-spread type {@code (TYPEOF(uarg*), TYPEOF(sarg*))=>RT}.
|
||||
* All the {@code sarg}s must have a common type {@code C}. (If there are none, {@code Object} is assumed.}
|
||||
* @param leadingArgCount the number of unchanged (non-spread) arguments
|
||||
* @return {@code invoker.invokeExact(mh, uarg*, C[]{sarg*}) := (RT)mh.invoke(uarg*, sarg*)}
|
||||
*/
|
||||
/*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) {
|
||||
MethodHandle vaInvoker = spreadInvokers[leadingArgCount];
|
||||
if (vaInvoker != null) return vaInvoker;
|
||||
int spreadArgCount = targetType.parameterCount() - leadingArgCount;
|
||||
MethodType spreadInvokerType = targetType
|
||||
.replaceParameterTypes(leadingArgCount, targetType.parameterCount(), Object[].class);
|
||||
if (targetType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
|
||||
// Factor sinvoker.invoke(mh, a) into ginvoker.asSpreader().invoke(mh, a)
|
||||
// where ginvoker.invoke(mh, a*) => mh.invoke(a*).
|
||||
MethodHandle genInvoker = generalInvoker();
|
||||
vaInvoker = genInvoker.asSpreader(Object[].class, spreadArgCount);
|
||||
} else {
|
||||
// Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]).
|
||||
// Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a)
|
||||
// where filter(mh) == mh.asSpreader(Object[], spreadArgCount)
|
||||
MethodHandle arrayInvoker = MethodHandles.exactInvoker(spreadInvokerType);
|
||||
MethodHandle makeSpreader;
|
||||
try {
|
||||
makeSpreader = IMPL_LOOKUP
|
||||
.findVirtual(MethodHandle.class, "asSpreader",
|
||||
MethodType.methodType(MethodHandle.class, Class.class, int.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
makeSpreader = MethodHandles.insertArguments(makeSpreader, 1, Object[].class, spreadArgCount);
|
||||
vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
|
||||
MethodType postSpreadType = targetType;
|
||||
Class<?> argArrayType = impliedRestargType(postSpreadType, leadingArgCount);
|
||||
if (postSpreadType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
|
||||
return genericInvoker().asSpreader(argArrayType, spreadArgCount);
|
||||
}
|
||||
assert(vaInvoker.type().equals(spreadInvokerType.invokerType()));
|
||||
maybeCompileToBytecode(vaInvoker);
|
||||
spreadInvokers[leadingArgCount] = vaInvoker;
|
||||
return vaInvoker;
|
||||
// Cannot build a generic invoker here of type ginvoker.invoke(mh, a*[254]).
|
||||
// Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a)
|
||||
// where filter(mh) == mh.asSpreader(Object[], spreadArgCount)
|
||||
MethodType preSpreadType = postSpreadType
|
||||
.replaceParameterTypes(leadingArgCount, postSpreadType.parameterCount(), argArrayType);
|
||||
MethodHandle arrayInvoker = MethodHandles.invoker(preSpreadType);
|
||||
MethodHandle makeSpreader = MethodHandles.insertArguments(Lazy.MH_asSpreader, 1, argArrayType, spreadArgCount);
|
||||
return MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle varargsInvoker() {
|
||||
MethodHandle vaInvoker = varargsInvoker;
|
||||
if (vaInvoker != null) return vaInvoker;
|
||||
vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType());
|
||||
varargsInvoker = vaInvoker;
|
||||
return vaInvoker;
|
||||
}
|
||||
|
||||
private static MethodHandle THROW_UCS = null;
|
||||
|
||||
/*non-public*/ MethodHandle uninitializedCallSite() {
|
||||
MethodHandle invoker = uninitializedCallSite;
|
||||
if (invoker != null) return invoker;
|
||||
if (targetType.parameterCount() > 0) {
|
||||
MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
|
||||
Invokers invokers0 = type0.invokers();
|
||||
invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
|
||||
0, targetType.parameterList());
|
||||
assert(invoker.type().equals(targetType));
|
||||
uninitializedCallSite = invoker;
|
||||
return invoker;
|
||||
private static Class<?> impliedRestargType(MethodType restargType, int fromPos) {
|
||||
if (restargType.isGeneric()) return Object[].class; // can be nothing else
|
||||
int maxPos = restargType.parameterCount();
|
||||
if (fromPos >= maxPos) return Object[].class; // reasonable default
|
||||
Class<?> argType = restargType.parameterType(fromPos);
|
||||
for (int i = fromPos+1; i < maxPos; i++) {
|
||||
if (argType != restargType.parameterType(i))
|
||||
throw newIllegalArgumentException("need homogeneous rest arguments", restargType);
|
||||
}
|
||||
invoker = THROW_UCS;
|
||||
if (invoker == null) {
|
||||
try {
|
||||
THROW_UCS = invoker = IMPL_LOOKUP
|
||||
.findStatic(CallSite.class, "uninitializedCallSite",
|
||||
MethodType.methodType(Empty.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
|
||||
invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount());
|
||||
assert(invoker.type().equals(targetType));
|
||||
uninitializedCallSite = invoker;
|
||||
return invoker;
|
||||
if (argType == Object.class) return Object[].class;
|
||||
return Array.newInstance(argType, 0).getClass();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@ -308,7 +260,9 @@ class Invokers {
|
||||
: Arrays.asList(mtype, customized, which, nameCursor, names.length);
|
||||
if (MTYPE_ARG >= INARG_LIMIT) {
|
||||
assert(names[MTYPE_ARG] == null);
|
||||
NamedFunction getter = BoundMethodHandle.getSpeciesData("L").getterFunction(0);
|
||||
BoundMethodHandle.SpeciesData speciesData = BoundMethodHandle.speciesData_L();
|
||||
names[THIS_MH] = names[THIS_MH].withConstraint(speciesData);
|
||||
NamedFunction getter = speciesData.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)
|
||||
}
|
||||
@ -360,9 +314,6 @@ class Invokers {
|
||||
Object checkGenericType(Object mhObj, Object expectedObj) {
|
||||
MethodHandle mh = (MethodHandle) mhObj;
|
||||
MethodType expected = (MethodType) expectedObj;
|
||||
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*),
|
||||
@ -436,27 +387,40 @@ class Invokers {
|
||||
}
|
||||
|
||||
// Local constant functions:
|
||||
private static final NamedFunction NF_checkExactType;
|
||||
private static final NamedFunction NF_checkGenericType;
|
||||
private static final NamedFunction NF_asType;
|
||||
private static final NamedFunction NF_getCallSiteTarget;
|
||||
private static final NamedFunction
|
||||
NF_checkExactType,
|
||||
NF_checkGenericType,
|
||||
NF_getCallSiteTarget;
|
||||
static {
|
||||
try {
|
||||
NF_checkExactType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkExactType", Object.class, Object.class));
|
||||
NF_checkGenericType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkGenericType", Object.class, Object.class));
|
||||
NF_asType = new NamedFunction(MethodHandle.class
|
||||
.getDeclaredMethod("asType", MethodType.class));
|
||||
NF_getCallSiteTarget = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("getCallSiteTarget", Object.class));
|
||||
NF_checkExactType.resolve();
|
||||
NF_checkGenericType.resolve();
|
||||
NF_getCallSiteTarget.resolve();
|
||||
// bound
|
||||
NamedFunction nfs[] = {
|
||||
NF_checkExactType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkExactType", Object.class, Object.class)),
|
||||
NF_checkGenericType = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkGenericType", Object.class, Object.class)),
|
||||
NF_getCallSiteTarget = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("getCallSiteTarget", 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);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Lazy {
|
||||
private static final MethodHandle MH_asSpreader;
|
||||
|
||||
static {
|
||||
try {
|
||||
MH_asSpreader = IMPL_LOOKUP.findVirtual(MethodHandle.class, "asSpreader",
|
||||
MethodType.methodType(MethodHandle.class, Class.class, int.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,10 @@ package java.lang.invoke;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@ -125,8 +124,7 @@ class LambdaForm {
|
||||
MemberName vmentry; // low-level behavior, or null if not yet prepared
|
||||
private boolean isCompiled;
|
||||
|
||||
// Caches for common structural transforms:
|
||||
LambdaForm[] bindCache;
|
||||
Object transformCache; // managed by LambdaFormEditor
|
||||
|
||||
public static final int VOID_RESULT = -1, LAST_RESULT = -2;
|
||||
|
||||
@ -214,6 +212,13 @@ class LambdaForm {
|
||||
}
|
||||
return btypes;
|
||||
}
|
||||
static byte[] basicTypesOrd(BasicType[] btypes) {
|
||||
byte[] ords = new byte[btypes.length];
|
||||
for (int i = 0; i < btypes.length; i++) {
|
||||
ords[i] = (byte)btypes[i].ordinal();
|
||||
}
|
||||
return ords;
|
||||
}
|
||||
static boolean isBasicTypeChar(char c) {
|
||||
return "LIJFDV".indexOf(c) >= 0;
|
||||
}
|
||||
@ -243,7 +248,12 @@ class LambdaForm {
|
||||
this.result = fixResult(result, names);
|
||||
this.names = names.clone();
|
||||
this.debugName = fixDebugName(debugName);
|
||||
normalize();
|
||||
int maxOutArity = normalize();
|
||||
if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
|
||||
// Cannot use LF interpreter on very high arity expressions.
|
||||
assert(maxOutArity <= MethodType.MAX_JVM_ARITY);
|
||||
compileToBytecode();
|
||||
}
|
||||
}
|
||||
|
||||
LambdaForm(String debugName,
|
||||
@ -348,9 +358,12 @@ class LambdaForm {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Renumber and/or replace params so that they are interned and canonically numbered. */
|
||||
private void normalize() {
|
||||
/** Renumber and/or replace params so that they are interned and canonically numbered.
|
||||
* @return maximum argument list length among the names (since we have to pass over them anyway)
|
||||
*/
|
||||
private int normalize() {
|
||||
Name[] oldNames = null;
|
||||
int maxOutArity = 0;
|
||||
int changesStart = 0;
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
Name n = names[i];
|
||||
@ -361,6 +374,8 @@ class LambdaForm {
|
||||
}
|
||||
names[i] = n.cloneWithIndex(i);
|
||||
}
|
||||
if (n.arguments != null && maxOutArity < n.arguments.length)
|
||||
maxOutArity = n.arguments.length;
|
||||
}
|
||||
if (oldNames != null) {
|
||||
int startFixing = arity;
|
||||
@ -387,6 +402,7 @@ class LambdaForm {
|
||||
}
|
||||
assert(nameRefsAreLegal());
|
||||
}
|
||||
return maxOutArity;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,7 +414,7 @@ class LambdaForm {
|
||||
* This allows Name references to be freely reused to construct
|
||||
* fresh lambdas, without confusion.
|
||||
*/
|
||||
private boolean nameRefsAreLegal() {
|
||||
boolean nameRefsAreLegal() {
|
||||
assert(arity >= 0 && arity <= names.length);
|
||||
assert(result >= -1 && result < names.length);
|
||||
// Do all names possess an index consistent with their local definition order?
|
||||
@ -439,8 +455,20 @@ class LambdaForm {
|
||||
|
||||
/** Report the N-th argument type. */
|
||||
BasicType parameterType(int n) {
|
||||
return parameter(n).type;
|
||||
}
|
||||
|
||||
/** Report the N-th argument name. */
|
||||
Name parameter(int n) {
|
||||
assert(n < arity);
|
||||
return names[n].type;
|
||||
Name param = names[n];
|
||||
assert(param.isParam());
|
||||
return param;
|
||||
}
|
||||
|
||||
/** Report the N-th argument type constraint. */
|
||||
Object parameterConstraint(int n) {
|
||||
return parameter(n).constraint;
|
||||
}
|
||||
|
||||
/** Report the arity. */
|
||||
@ -448,6 +476,11 @@ class LambdaForm {
|
||||
return arity;
|
||||
}
|
||||
|
||||
/** Report the number of expressions (non-parameter names). */
|
||||
int expressionCount() {
|
||||
return names.length - arity;
|
||||
}
|
||||
|
||||
/** Return the method type corresponding to my basic type signature. */
|
||||
MethodType methodType() {
|
||||
return signatureType(basicTypeSignature());
|
||||
@ -464,7 +497,7 @@ class LambdaForm {
|
||||
return sig.indexOf('_');
|
||||
}
|
||||
static BasicType signatureReturn(String sig) {
|
||||
return basicType(sig.charAt(signatureArity(sig)+1));
|
||||
return basicType(sig.charAt(signatureArity(sig) + 1));
|
||||
}
|
||||
static boolean isValidSignature(String sig) {
|
||||
int arity = sig.indexOf('_');
|
||||
@ -582,21 +615,12 @@ class LambdaForm {
|
||||
isCompiled = true;
|
||||
return vmentry;
|
||||
} catch (Error | Exception ex) {
|
||||
throw newInternalError("compileToBytecode", ex);
|
||||
throw newInternalError(this.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
|
||||
static {
|
||||
int capacity = 512; // expect many distinct signatures over time
|
||||
float loadFactor = 0.75f; // normal default
|
||||
int writers = 1;
|
||||
PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
|
||||
}
|
||||
|
||||
private static Map<String,LambdaForm> computeInitialPreparedForms() {
|
||||
private static void computeInitialPreparedForms() {
|
||||
// Find all predefined invokers and associate them with canonical empty lambda forms.
|
||||
HashMap<String,LambdaForm> forms = new HashMap<>();
|
||||
for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
|
||||
if (!m.isStatic() || !m.isPackage()) continue;
|
||||
MethodType mt = m.getMethodType();
|
||||
@ -607,13 +631,9 @@ class LambdaForm {
|
||||
assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
|
||||
LambdaForm form = new LambdaForm(sig);
|
||||
form.vmentry = m;
|
||||
form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
|
||||
// FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
|
||||
forms.put(sig, form);
|
||||
form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, form);
|
||||
}
|
||||
}
|
||||
//System.out.println("computeInitialPreparedForms => "+forms);
|
||||
return forms;
|
||||
}
|
||||
|
||||
// Set this false to disable use of the interpret_L methods defined in this file.
|
||||
@ -647,13 +667,11 @@ class LambdaForm {
|
||||
}
|
||||
private static LambdaForm getPreparedForm(String sig) {
|
||||
MethodType mtype = signatureType(sig);
|
||||
//LambdaForm prep = PREPARED_FORMS.get(sig);
|
||||
LambdaForm prep = mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
|
||||
if (prep != null) return prep;
|
||||
assert(isValidSignature(sig));
|
||||
prep = new LambdaForm(sig);
|
||||
prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
|
||||
//LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
|
||||
return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
|
||||
}
|
||||
|
||||
@ -709,10 +727,7 @@ class LambdaForm {
|
||||
/** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
|
||||
private static final int COMPILE_THRESHOLD;
|
||||
static {
|
||||
if (MethodHandleStatics.COMPILE_THRESHOLD != null)
|
||||
COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
|
||||
else
|
||||
COMPILE_THRESHOLD = 30; // default value
|
||||
COMPILE_THRESHOLD = Math.max(-1, MethodHandleStatics.COMPILE_THRESHOLD);
|
||||
}
|
||||
private int invocationCounter = 0;
|
||||
|
||||
@ -728,7 +743,9 @@ class LambdaForm {
|
||||
for (int i = argumentValues.length; i < values.length; i++) {
|
||||
values[i] = interpretName(names[i], values);
|
||||
}
|
||||
return (result < 0) ? null : values[result];
|
||||
Object rv = (result < 0) ? null : values[result];
|
||||
assert(resultCheck(argumentValues, rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ -785,28 +802,6 @@ class LambdaForm {
|
||||
return rval;
|
||||
}
|
||||
|
||||
//** This transform is applied (statically) to every name.function. */
|
||||
/*
|
||||
private static MethodHandle eraseSubwordTypes(MethodHandle mh) {
|
||||
MethodType mt = mh.type();
|
||||
if (mt.hasPrimitives()) {
|
||||
mt = mt.changeReturnType(eraseSubwordType(mt.returnType()));
|
||||
for (int i = 0; i < mt.parameterCount(); i++) {
|
||||
mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i)));
|
||||
}
|
||||
mh = MethodHandles.explicitCastArguments(mh, mt);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
private static Class<?> eraseSubwordType(Class<?> type) {
|
||||
if (!type.isPrimitive()) return type;
|
||||
if (type == int.class) return type;
|
||||
Wrapper w = Wrapper.forPrimitiveType(type);
|
||||
if (w.isSubwordOrInt()) return int.class;
|
||||
return type;
|
||||
}
|
||||
*/
|
||||
|
||||
static void traceInterpreter(String event, Object obj, Object... args) {
|
||||
if (TRACE_INTERPRETER) {
|
||||
System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : ""));
|
||||
@ -819,8 +814,16 @@ class LambdaForm {
|
||||
assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length";
|
||||
// also check that the leading (receiver) argument is somehow bound to this LF:
|
||||
assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0];
|
||||
assert(((MethodHandle)argumentValues[0]).internalForm() == this);
|
||||
MethodHandle mh = (MethodHandle) argumentValues[0];
|
||||
assert(mh.internalForm() == this);
|
||||
// note: argument #0 could also be an interface wrapper, in the future
|
||||
argumentTypesMatch(basicTypeSignature(), argumentValues);
|
||||
return true;
|
||||
}
|
||||
private boolean resultCheck(Object[] argumentValues, Object result) {
|
||||
MethodHandle mh = (MethodHandle) argumentValues[0];
|
||||
MethodType mt = mh.type();
|
||||
assert(valueMatches(returnType(), mt.returnType(), result));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -839,7 +842,7 @@ class LambdaForm {
|
||||
if (i == arity) buf.append(")=>{");
|
||||
Name n = names[i];
|
||||
if (i >= arity) buf.append("\n ");
|
||||
buf.append(n);
|
||||
buf.append(n.paramString());
|
||||
if (i < arity) {
|
||||
if (i+1 < arity) buf.append(",");
|
||||
continue;
|
||||
@ -847,6 +850,7 @@ class LambdaForm {
|
||||
buf.append("=").append(n.exprString());
|
||||
buf.append(";");
|
||||
}
|
||||
if (arity == names.length) buf.append(")=>{");
|
||||
buf.append(result < 0 ? "void" : names[result]).append("}");
|
||||
if (TRACE_INTERPRETER) {
|
||||
// Extra verbosity:
|
||||
@ -856,135 +860,19 @@ class LambdaForm {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply immediate binding for a Name in this form indicated by its position relative to the form.
|
||||
* The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not
|
||||
* accepted as valid.
|
||||
*/
|
||||
LambdaForm bindImmediate(int pos, BasicType basicType, Object value) {
|
||||
// must be an argument, and the types must match
|
||||
assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value);
|
||||
|
||||
int arity2 = arity - 1;
|
||||
Name[] names2 = new Name[names.length - 1];
|
||||
for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2
|
||||
Name n = names[r];
|
||||
if (n.isParam()) {
|
||||
if (n.index == pos) {
|
||||
// do not copy over the argument that is to be replaced with a literal,
|
||||
// but adjust the write index
|
||||
--w;
|
||||
} else {
|
||||
names2[w] = new Name(w, n.type);
|
||||
}
|
||||
} else {
|
||||
Object[] arguments2 = new Object[n.arguments.length];
|
||||
for (int i = 0; i < n.arguments.length; ++i) {
|
||||
Object arg = n.arguments[i];
|
||||
if (arg instanceof Name) {
|
||||
int ni = ((Name) arg).index;
|
||||
if (ni == pos) {
|
||||
arguments2[i] = value;
|
||||
} else if (ni < pos) {
|
||||
// replacement position not yet passed
|
||||
arguments2[i] = names2[ni];
|
||||
} else {
|
||||
// replacement position passed
|
||||
arguments2[i] = names2[ni - 1];
|
||||
}
|
||||
} else {
|
||||
arguments2[i] = arg;
|
||||
}
|
||||
}
|
||||
names2[w] = new Name(n.function, arguments2);
|
||||
names2[w].initIndex(w);
|
||||
}
|
||||
}
|
||||
|
||||
int result2 = result == -1 ? -1 : result - 1;
|
||||
return new LambdaForm(debugName, arity2, names2, result2);
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof LambdaForm && equals((LambdaForm)obj);
|
||||
}
|
||||
|
||||
LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) {
|
||||
Name name = names[namePos];
|
||||
BoundMethodHandle.SpeciesData newData = oldData.extendWith(name.type);
|
||||
return bind(name, new Name(newData.getterFunction(oldData.fieldCount()), names[0]), oldData, newData);
|
||||
public boolean equals(LambdaForm that) {
|
||||
if (this.result != that.result) return false;
|
||||
return Arrays.equals(this.names, that.names);
|
||||
}
|
||||
LambdaForm bind(Name name, Name binding,
|
||||
BoundMethodHandle.SpeciesData oldData,
|
||||
BoundMethodHandle.SpeciesData newData) {
|
||||
int pos = name.index;
|
||||
assert(name.isParam());
|
||||
assert(!binding.isParam());
|
||||
assert(name.type == binding.type);
|
||||
assert(0 <= pos && pos < arity && names[pos] == name);
|
||||
assert(binding.function.memberDeclaringClassOrNull() == newData.clazz);
|
||||
assert(oldData.getters.length == newData.getters.length-1);
|
||||
if (bindCache != null) {
|
||||
LambdaForm form = bindCache[pos];
|
||||
if (form != null) {
|
||||
assert(form.contains(binding)) : "form << " + form + " >> does not contain binding << " + binding + " >>";
|
||||
return form;
|
||||
}
|
||||
} else {
|
||||
bindCache = new LambdaForm[arity];
|
||||
}
|
||||
assert(nameRefsAreLegal());
|
||||
int arity2 = arity-1;
|
||||
Name[] names2 = names.clone();
|
||||
names2[pos] = binding; // we might move this in a moment
|
||||
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
int firstOldRef = -1;
|
||||
for (int i = 0; i < names2.length; i++) {
|
||||
Name n = names[i];
|
||||
if (n.function != null &&
|
||||
n.function.memberDeclaringClassOrNull() == oldData.clazz) {
|
||||
MethodHandle oldGetter = n.function.resolvedHandle;
|
||||
MethodHandle newGetter = null;
|
||||
for (int j = 0; j < oldData.getters.length; j++) {
|
||||
if (oldGetter == oldData.getters[j])
|
||||
newGetter = newData.getters[j];
|
||||
}
|
||||
if (newGetter != null) {
|
||||
if (firstOldRef < 0) firstOldRef = i;
|
||||
Name n2 = new Name(newGetter, n.arguments);
|
||||
names2[i] = n2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk over the new list of names once, in forward order.
|
||||
// Replace references to 'name' with 'binding'.
|
||||
// Replace data structure references to the old BMH species with the new.
|
||||
// This might cause a ripple effect, but it will settle in one pass.
|
||||
assert(firstOldRef < 0 || firstOldRef > pos);
|
||||
for (int i = pos+1; i < names2.length; i++) {
|
||||
if (i <= arity2) continue;
|
||||
names2[i] = names2[i].replaceNames(names, names2, pos, i);
|
||||
}
|
||||
|
||||
// (a0, a1, name=a2, a3, a4) => (a0, a1, a3, a4, binding)
|
||||
int insPos = pos;
|
||||
for (; insPos+1 < names2.length; insPos++) {
|
||||
Name n = names2[insPos+1];
|
||||
if (n.isSiblingBindingBefore(binding)) {
|
||||
names2[insPos] = n;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
names2[insPos] = binding;
|
||||
|
||||
// Since we moved some stuff, maybe update the result reference:
|
||||
int result2 = result;
|
||||
if (result2 == pos)
|
||||
result2 = insPos;
|
||||
else if (result2 > pos && result2 <= insPos)
|
||||
result2 -= 1;
|
||||
|
||||
return bindCache[pos] = new LambdaForm(debugName, arity2, names2, result2);
|
||||
public int hashCode() {
|
||||
return result + 31 * Arrays.hashCode(names);
|
||||
}
|
||||
LambdaFormEditor editor() {
|
||||
return LambdaFormEditor.lambdaFormEditor(this);
|
||||
}
|
||||
|
||||
boolean contains(Name name) {
|
||||
@ -1000,16 +888,16 @@ class LambdaForm {
|
||||
}
|
||||
|
||||
LambdaForm addArguments(int pos, BasicType... types) {
|
||||
assert(pos <= arity);
|
||||
// names array has MH in slot 0; skip it.
|
||||
int argpos = pos + 1;
|
||||
assert(argpos <= arity);
|
||||
int length = names.length;
|
||||
int inTypes = types.length;
|
||||
Name[] names2 = Arrays.copyOf(names, length + inTypes);
|
||||
int arity2 = arity + inTypes;
|
||||
int result2 = result;
|
||||
if (result2 >= arity)
|
||||
if (result2 >= argpos)
|
||||
result2 += inTypes;
|
||||
// names array has MH in slot 0; skip it.
|
||||
int argpos = pos + 1;
|
||||
// Note: The LF constructor will rename names2[argpos...].
|
||||
// Make space for new arguments (shift temporaries).
|
||||
System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos);
|
||||
@ -1102,8 +990,9 @@ class LambdaForm {
|
||||
}
|
||||
NamedFunction(MemberName member, MethodHandle resolvedHandle) {
|
||||
this.member = member;
|
||||
//resolvedHandle = eraseSubwordTypes(resolvedHandle);
|
||||
this.resolvedHandle = resolvedHandle;
|
||||
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
|
||||
//assert(!isInvokeBasic());
|
||||
}
|
||||
NamedFunction(MethodType basicInvokerType) {
|
||||
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
|
||||
@ -1114,6 +1003,13 @@ class LambdaForm {
|
||||
// necessary to pass BigArityTest
|
||||
this.member = Invokers.invokeBasicMethod(basicInvokerType);
|
||||
}
|
||||
assert(isInvokeBasic());
|
||||
}
|
||||
|
||||
private boolean isInvokeBasic() {
|
||||
return member != null &&
|
||||
member.isMethodHandleInvoke() &&
|
||||
"invokeBasic".equals(member.getName());
|
||||
}
|
||||
|
||||
// The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
|
||||
@ -1169,7 +1065,7 @@ class LambdaForm {
|
||||
if (LambdaForm.signatureReturn(sig) == V_TYPE)
|
||||
srcType = srcType.changeReturnType(void.class);
|
||||
MethodTypeForm typeForm = srcType.form();
|
||||
typeForm.namedFunctionInvoker = DirectMethodHandle.make(m);
|
||||
typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, DirectMethodHandle.make(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1179,85 +1075,104 @@ class LambdaForm {
|
||||
/** void return type invokers. */
|
||||
@Hidden
|
||||
static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 0);
|
||||
assert(arityCheck(0, void.class, mh, a));
|
||||
mh.invokeBasic();
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 1);
|
||||
assert(arityCheck(1, void.class, mh, a));
|
||||
mh.invokeBasic(a[0]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 2);
|
||||
assert(arityCheck(2, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 3);
|
||||
assert(arityCheck(3, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1], a[2]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 4);
|
||||
assert(arityCheck(4, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1], a[2], a[3]);
|
||||
return null;
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 5);
|
||||
assert(arityCheck(5, void.class, mh, a));
|
||||
mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
|
||||
return null;
|
||||
}
|
||||
/** Object return type invokers. */
|
||||
@Hidden
|
||||
static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 0);
|
||||
assert(arityCheck(0, mh, a));
|
||||
return mh.invokeBasic();
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 1);
|
||||
assert(arityCheck(1, mh, a));
|
||||
return mh.invokeBasic(a[0]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 2);
|
||||
assert(arityCheck(2, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 3);
|
||||
assert(arityCheck(3, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1], a[2]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 4);
|
||||
assert(arityCheck(4, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1], a[2], a[3]);
|
||||
}
|
||||
@Hidden
|
||||
static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable {
|
||||
assert(a.length == 5);
|
||||
assert(arityCheck(5, mh, a));
|
||||
return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
|
||||
}
|
||||
private static boolean arityCheck(int arity, MethodHandle mh, Object[] a) {
|
||||
return arityCheck(arity, Object.class, mh, a);
|
||||
}
|
||||
private static boolean arityCheck(int arity, Class<?> rtype, MethodHandle mh, Object[] a) {
|
||||
assert(a.length == arity)
|
||||
: Arrays.asList(a.length, arity);
|
||||
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
|
||||
: Arrays.asList(mh, rtype, arity);
|
||||
MemberName member = mh.internalMemberName();
|
||||
if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
|
||||
assert(arity > 0);
|
||||
assert(a[0] instanceof MethodHandle);
|
||||
MethodHandle mh2 = (MethodHandle) a[0];
|
||||
assert(mh2.type().basicType() == MethodType.genericMethodType(arity-1).changeReturnType(rtype))
|
||||
: Arrays.asList(member, mh2, rtype, arity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static final MethodType INVOKER_METHOD_TYPE =
|
||||
MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
|
||||
|
||||
private static MethodHandle computeInvoker(MethodTypeForm typeForm) {
|
||||
MethodHandle mh = typeForm.namedFunctionInvoker;
|
||||
typeForm = typeForm.basicType().form(); // normalize to basic type
|
||||
MethodHandle mh = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV);
|
||||
if (mh != null) return mh;
|
||||
MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while
|
||||
mh = DirectMethodHandle.make(invoker);
|
||||
MethodHandle mh2 = typeForm.namedFunctionInvoker;
|
||||
MethodHandle mh2 = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV);
|
||||
if (mh2 != null) return mh2; // benign race
|
||||
if (!mh.type().equals(INVOKER_METHOD_TYPE))
|
||||
throw newInternalError(mh.debugString());
|
||||
return typeForm.namedFunctionInvoker = mh;
|
||||
return typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, mh);
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@ -1365,6 +1280,11 @@ class LambdaForm {
|
||||
public boolean isConstantZero() {
|
||||
return this.equals(constantZero(returnType()));
|
||||
}
|
||||
|
||||
public MethodHandleImpl.Intrinsic intrinsicName() {
|
||||
return resolvedHandle == null ? MethodHandleImpl.Intrinsic.NONE
|
||||
: resolvedHandle.intrinsicName();
|
||||
}
|
||||
}
|
||||
|
||||
public static String basicTypeSignature(MethodType type) {
|
||||
@ -1411,6 +1331,7 @@ class LambdaForm {
|
||||
final BasicType type;
|
||||
private short index;
|
||||
final NamedFunction function;
|
||||
final Object constraint; // additional type information, if not null
|
||||
@Stable final Object[] arguments;
|
||||
|
||||
private Name(int index, BasicType type, NamedFunction function, Object[] arguments) {
|
||||
@ -1418,8 +1339,18 @@ class LambdaForm {
|
||||
this.type = type;
|
||||
this.function = function;
|
||||
this.arguments = arguments;
|
||||
this.constraint = null;
|
||||
assert(this.index == index);
|
||||
}
|
||||
private Name(Name that, Object constraint) {
|
||||
this.index = that.index;
|
||||
this.type = that.type;
|
||||
this.function = that.function;
|
||||
this.arguments = that.arguments;
|
||||
this.constraint = constraint;
|
||||
assert(constraint == null || isParam()); // only params have constraints
|
||||
assert(constraint == null || constraint instanceof BoundMethodHandle.SpeciesData || constraint instanceof Class);
|
||||
}
|
||||
Name(MethodHandle function, Object... arguments) {
|
||||
this(new NamedFunction(function), arguments);
|
||||
}
|
||||
@ -1431,7 +1362,7 @@ class LambdaForm {
|
||||
this(new NamedFunction(function), arguments);
|
||||
}
|
||||
Name(NamedFunction function, Object... arguments) {
|
||||
this(-1, function.returnType(), function, arguments = arguments.clone());
|
||||
this(-1, function.returnType(), function, arguments = Arrays.copyOf(arguments, arguments.length, Object[].class));
|
||||
assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString();
|
||||
for (int i = 0; i < arguments.length; i++)
|
||||
assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString();
|
||||
@ -1467,7 +1398,11 @@ class LambdaForm {
|
||||
}
|
||||
Name cloneWithIndex(int i) {
|
||||
Object[] newArguments = (arguments == null) ? null : arguments.clone();
|
||||
return new Name(i, type, function, newArguments);
|
||||
return new Name(i, type, function, newArguments).withConstraint(constraint);
|
||||
}
|
||||
Name withConstraint(Object constraint) {
|
||||
if (constraint == this.constraint) return this;
|
||||
return new Name(this, constraint);
|
||||
}
|
||||
Name replaceName(Name oldName, Name newName) { // FIXME: use replaceNames uniformly
|
||||
if (oldName == newName) return this;
|
||||
@ -1487,7 +1422,11 @@ class LambdaForm {
|
||||
if (!replaced) return this;
|
||||
return new Name(function, arguments);
|
||||
}
|
||||
/** In the arguments of this Name, replace oldNames[i] pairwise by newNames[i].
|
||||
* Limit such replacements to {@code start<=i<end}. Return possibly changed self.
|
||||
*/
|
||||
Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
|
||||
if (start >= end) return this;
|
||||
@SuppressWarnings("LocalVariableHidesMemberVariable")
|
||||
Object[] arguments = this.arguments;
|
||||
boolean replaced = false;
|
||||
@ -1539,9 +1478,17 @@ class LambdaForm {
|
||||
return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar();
|
||||
}
|
||||
public String debugString() {
|
||||
String s = toString();
|
||||
String s = paramString();
|
||||
return (function == null) ? s : s + "=" + exprString();
|
||||
}
|
||||
public String paramString() {
|
||||
String s = toString();
|
||||
Object c = constraint;
|
||||
if (c == null)
|
||||
return s;
|
||||
if (c instanceof Class) c = ((Class<?>)c).getSimpleName();
|
||||
return s + "/" + c;
|
||||
}
|
||||
public String exprString() {
|
||||
if (function == null) return toString();
|
||||
StringBuilder buf = new StringBuilder(function.toString());
|
||||
@ -1572,34 +1519,6 @@ class LambdaForm {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
boolean isSiblingBindingBefore(Name binding) {
|
||||
assert(!binding.isParam());
|
||||
if (isParam()) return true;
|
||||
if (function.equals(binding.function) &&
|
||||
arguments.length == binding.arguments.length) {
|
||||
boolean sawInt = false;
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
Object a1 = arguments[i];
|
||||
Object a2 = binding.arguments[i];
|
||||
if (!a1.equals(a2)) {
|
||||
if (a1 instanceof Integer && a2 instanceof Integer) {
|
||||
if (sawInt) continue;
|
||||
sawInt = true;
|
||||
if ((int)a1 < (int)a2) continue; // still might be true
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return sawInt;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return the index of the last occurrence of n in the argument array.
|
||||
* Return -1 if the name is not used.
|
||||
*/
|
||||
@ -1690,6 +1609,7 @@ class LambdaForm {
|
||||
static Name internArgument(Name n) {
|
||||
assert(n.isParam()) : "not param: " + n;
|
||||
assert(n.index < INTERNED_ARGUMENT_LIMIT);
|
||||
if (n.constraint != null) return n;
|
||||
return argument(n.index, n.type);
|
||||
}
|
||||
static Name[] arguments(int extra, String types) {
|
||||
@ -1858,37 +1778,6 @@ class LambdaForm {
|
||||
@interface Hidden {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Smoke-test for the invokers used in this file.
|
||||
static void testMethodHandleLinkers() throws Throwable {
|
||||
MemberName.Factory lookup = MemberName.getFactory();
|
||||
MemberName asList_MN = new MemberName(Arrays.class, "asList",
|
||||
MethodType.methodType(List.class, Object[].class),
|
||||
REF_invokeStatic);
|
||||
//MethodHandleNatives.resolve(asList_MN, null);
|
||||
asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class);
|
||||
System.out.println("about to call "+asList_MN);
|
||||
Object[] abc = { "a", "bc" };
|
||||
List<?> lst = (List<?>) MethodHandle.linkToStatic(abc, asList_MN);
|
||||
System.out.println("lst="+lst);
|
||||
MemberName toString_MN = new MemberName(Object.class.getMethod("toString"));
|
||||
String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN);
|
||||
toString_MN = new MemberName(Object.class.getMethod("toString"), true);
|
||||
String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN);
|
||||
System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
|
||||
MemberName toArray_MN = new MemberName(List.class.getMethod("toArray"));
|
||||
Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN);
|
||||
System.out.println("toArray="+Arrays.toString(arr));
|
||||
}
|
||||
static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
|
||||
// Requires these definitions in MethodHandle:
|
||||
static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable;
|
||||
static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable;
|
||||
static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable;
|
||||
static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable;
|
||||
*/
|
||||
|
||||
private static final HashMap<String,Integer> DEBUG_NAME_COUNTERS;
|
||||
static {
|
||||
if (debugEnabled())
|
||||
@ -1901,7 +1790,7 @@ class LambdaForm {
|
||||
static {
|
||||
createIdentityForms();
|
||||
if (USE_PREDEFINED_INTERPRET_METHODS)
|
||||
PREPARED_FORMS.putAll(computeInitialPreparedForms());
|
||||
computeInitialPreparedForms();
|
||||
NamedFunction.initializeInvokers();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2014, 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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
|
||||
/** Working storage for an LF that is being transformed.
|
||||
* Similarly to a StringBuffer, the editing can take place in multiple steps.
|
||||
*/
|
||||
final class LambdaFormBuffer {
|
||||
private int arity, length;
|
||||
private Name[] names;
|
||||
private Name[] originalNames; // snapshot of pre-transaction names
|
||||
private byte flags;
|
||||
private int firstChange;
|
||||
private Name resultName;
|
||||
private String debugName;
|
||||
private ArrayList<Name> dups;
|
||||
|
||||
private static final int F_TRANS = 0x10, F_OWNED = 0x03;
|
||||
|
||||
LambdaFormBuffer(LambdaForm lf) {
|
||||
this(lf.arity, lf.names, lf.result);
|
||||
debugName = lf.debugName;
|
||||
assert(lf.nameRefsAreLegal());
|
||||
}
|
||||
|
||||
private LambdaFormBuffer(int arity, Name[] names, int result) {
|
||||
this.arity = arity;
|
||||
setNames(names);
|
||||
if (result == LAST_RESULT) result = length - 1;
|
||||
if (result >= 0 && names[result].type != V_TYPE)
|
||||
resultName = names[result];
|
||||
}
|
||||
|
||||
private LambdaForm lambdaForm() {
|
||||
assert(!inTrans()); // need endEdit call to tidy things up
|
||||
return new LambdaForm(debugName, arity, nameArray(), resultIndex());
|
||||
}
|
||||
|
||||
Name name(int i) {
|
||||
assert(i < length);
|
||||
return names[i];
|
||||
}
|
||||
|
||||
Name[] nameArray() {
|
||||
return Arrays.copyOf(names, length);
|
||||
}
|
||||
|
||||
int resultIndex() {
|
||||
if (resultName == null) return VOID_RESULT;
|
||||
int index = indexOf(resultName, names);
|
||||
assert(index >= 0);
|
||||
return index;
|
||||
}
|
||||
|
||||
void setNames(Name[] names2) {
|
||||
names = originalNames = names2; // keep a record of where everything was to start with
|
||||
length = names2.length;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
private boolean verifyArity() {
|
||||
for (int i = 0; i < arity && i < firstChange; i++) {
|
||||
assert(names[i].isParam()) : "#" + i + "=" + names[i];
|
||||
}
|
||||
for (int i = arity; i < length; i++) {
|
||||
assert(!names[i].isParam()) : "#" + i + "=" + names[i];
|
||||
}
|
||||
for (int i = length; i < names.length; i++) {
|
||||
assert(names[i] == null) : "#" + i + "=" + names[i];
|
||||
}
|
||||
// check resultName also
|
||||
if (resultName != null) {
|
||||
int resultIndex = indexOf(resultName, names);
|
||||
assert(resultIndex >= 0) : "not found: " + resultName.exprString() + Arrays.asList(names);
|
||||
assert(names[resultIndex] == resultName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean verifyFirstChange() {
|
||||
assert(inTrans());
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (names[i] != originalNames[i]) {
|
||||
assert(firstChange == i) : Arrays.asList(firstChange, i, originalNames[i].exprString(), Arrays.asList(names));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
assert(firstChange == length) : Arrays.asList(firstChange, Arrays.asList(names));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int indexOf(NamedFunction fn, NamedFunction[] fns) {
|
||||
for (int i = 0; i < fns.length; i++) {
|
||||
if (fns[i] == fn) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int indexOf(Name n, Name[] ns) {
|
||||
for (int i = 0; i < ns.length; i++) {
|
||||
if (ns[i] == n) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean inTrans() {
|
||||
return (flags & F_TRANS) != 0;
|
||||
}
|
||||
|
||||
int ownedCount() {
|
||||
return flags & F_OWNED;
|
||||
}
|
||||
|
||||
void growNames(int insertPos, int growLength) {
|
||||
int oldLength = length;
|
||||
int newLength = oldLength + growLength;
|
||||
int oc = ownedCount();
|
||||
if (oc == 0 || newLength > names.length) {
|
||||
names = Arrays.copyOf(names, (names.length + growLength) * 5 / 4);
|
||||
if (oc == 0) {
|
||||
flags++;
|
||||
oc++;
|
||||
assert(ownedCount() == oc);
|
||||
}
|
||||
}
|
||||
if (originalNames != null && originalNames.length < names.length) {
|
||||
originalNames = Arrays.copyOf(originalNames, names.length);
|
||||
if (oc == 1) {
|
||||
flags++;
|
||||
oc++;
|
||||
assert(ownedCount() == oc);
|
||||
}
|
||||
}
|
||||
if (growLength == 0) return;
|
||||
int insertEnd = insertPos + growLength;
|
||||
int tailLength = oldLength - insertPos;
|
||||
System.arraycopy(names, insertPos, names, insertEnd, tailLength);
|
||||
Arrays.fill(names, insertPos, insertEnd, null);
|
||||
if (originalNames != null) {
|
||||
System.arraycopy(originalNames, insertPos, originalNames, insertEnd, tailLength);
|
||||
Arrays.fill(originalNames, insertPos, insertEnd, null);
|
||||
}
|
||||
length = newLength;
|
||||
if (firstChange >= insertPos) {
|
||||
firstChange += growLength;
|
||||
}
|
||||
}
|
||||
|
||||
int lastIndexOf(Name n) {
|
||||
int result = -1;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (names[i] == n) result = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** We have just overwritten the name at pos1 with the name at pos2.
|
||||
* This means that there are two copies of the name, which we will have to fix later.
|
||||
*/
|
||||
private void noteDuplicate(int pos1, int pos2) {
|
||||
Name n = names[pos1];
|
||||
assert(n == names[pos2]);
|
||||
assert(originalNames[pos1] != null); // something was replaced at pos1
|
||||
assert(originalNames[pos2] == null || originalNames[pos2] == n);
|
||||
if (dups == null) {
|
||||
dups = new ArrayList<>();
|
||||
}
|
||||
dups.add(n);
|
||||
}
|
||||
|
||||
/** Replace duplicate names by nulls, and remove all nulls. */
|
||||
private void clearDuplicatesAndNulls() {
|
||||
if (dups != null) {
|
||||
// Remove duplicates.
|
||||
assert(ownedCount() >= 1);
|
||||
for (Name dup : dups) {
|
||||
for (int i = firstChange; i < length; i++) {
|
||||
if (names[i] == dup && originalNames[i] != dup) {
|
||||
names[i] = null;
|
||||
assert(Arrays.asList(names).contains(dup));
|
||||
break; // kill only one dup
|
||||
}
|
||||
}
|
||||
}
|
||||
dups.clear();
|
||||
}
|
||||
// Now that we are done with originalNames, remove "killed" names.
|
||||
int oldLength = length;
|
||||
for (int i = firstChange; i < length; i++) {
|
||||
if (names[i] == null) {
|
||||
System.arraycopy(names, i + 1, names, i, (--length - i));
|
||||
--i; // restart loop at this position
|
||||
}
|
||||
}
|
||||
if (length < oldLength) {
|
||||
Arrays.fill(names, length, oldLength, null);
|
||||
}
|
||||
assert(!Arrays.asList(names).subList(0, length).contains(null));
|
||||
}
|
||||
|
||||
/** Create a private, writable copy of names.
|
||||
* Preserve the original copy, for reference.
|
||||
*/
|
||||
void startEdit() {
|
||||
assert(verifyArity());
|
||||
int oc = ownedCount();
|
||||
assert(!inTrans()); // no nested transactions
|
||||
flags |= F_TRANS;
|
||||
Name[] oldNames = names;
|
||||
Name[] ownBuffer = (oc == 2 ? originalNames : null);
|
||||
assert(ownBuffer != oldNames);
|
||||
if (ownBuffer != null && ownBuffer.length >= length) {
|
||||
names = copyNamesInto(ownBuffer);
|
||||
} else {
|
||||
// make a new buffer to hold the names
|
||||
final int SLOP = 2;
|
||||
names = Arrays.copyOf(oldNames, Math.max(length + SLOP, oldNames.length));
|
||||
if (oc < 2) ++flags;
|
||||
assert(ownedCount() == oc + 1);
|
||||
}
|
||||
originalNames = oldNames;
|
||||
assert(originalNames != names);
|
||||
firstChange = length;
|
||||
assert(inTrans());
|
||||
}
|
||||
|
||||
private void changeName(int i, Name name) {
|
||||
assert(inTrans());
|
||||
assert(i < length);
|
||||
Name oldName = names[i];
|
||||
assert(oldName == originalNames[i]); // no multiple changes
|
||||
assert(verifyFirstChange());
|
||||
if (ownedCount() == 0)
|
||||
growNames(0, 0);
|
||||
names[i] = name;
|
||||
if (firstChange > i) {
|
||||
firstChange = i;
|
||||
}
|
||||
if (resultName != null && resultName == oldName) {
|
||||
resultName = name;
|
||||
}
|
||||
}
|
||||
|
||||
/** Change the result name. Null means a void result. */
|
||||
void setResult(Name name) {
|
||||
assert(name == null || lastIndexOf(name) >= 0);
|
||||
resultName = name;
|
||||
}
|
||||
|
||||
/** Finish a transaction. */
|
||||
LambdaForm endEdit() {
|
||||
assert(verifyFirstChange());
|
||||
// Assuming names have been changed pairwise from originalNames[i] to names[i],
|
||||
// update arguments to ensure referential integrity.
|
||||
for (int i = Math.max(firstChange, arity); i < length; i++) {
|
||||
Name name = names[i];
|
||||
if (name == null) continue; // space for removed duplicate
|
||||
Name newName = name.replaceNames(originalNames, names, firstChange, i);
|
||||
if (newName != name) {
|
||||
names[i] = newName;
|
||||
if (resultName == name) {
|
||||
resultName = newName;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(inTrans());
|
||||
flags &= ~F_TRANS;
|
||||
clearDuplicatesAndNulls();
|
||||
originalNames = null;
|
||||
// If any parameters have been changed, then reorder them as needed.
|
||||
// This is a "sheep-and-goats" stable sort, pushing all non-parameters
|
||||
// to the right of all parameters.
|
||||
if (firstChange < arity) {
|
||||
Name[] exprs = new Name[arity - firstChange];
|
||||
int argp = firstChange, exprp = 0;
|
||||
for (int i = firstChange; i < arity; i++) {
|
||||
Name name = names[i];
|
||||
if (name.isParam()) {
|
||||
names[argp++] = name;
|
||||
} else {
|
||||
exprs[exprp++] = name;
|
||||
}
|
||||
}
|
||||
assert(exprp == (arity - argp));
|
||||
// copy the exprs just after the last remaining param
|
||||
System.arraycopy(exprs, 0, names, argp, exprp);
|
||||
// adjust arity
|
||||
arity -= exprp;
|
||||
}
|
||||
assert(verifyArity());
|
||||
return lambdaForm();
|
||||
}
|
||||
|
||||
private Name[] copyNamesInto(Name[] buffer) {
|
||||
System.arraycopy(names, 0, buffer, 0, length);
|
||||
Arrays.fill(buffer, length, buffer.length, null);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** Replace any Name whose function is in oldFns with a copy
|
||||
* whose function is in the corresponding position in newFns.
|
||||
* Only do this if the arguments are exactly equal to the given.
|
||||
*/
|
||||
LambdaFormBuffer replaceFunctions(NamedFunction[] oldFns, NamedFunction[] newFns,
|
||||
Object... forArguments) {
|
||||
assert(inTrans());
|
||||
if (oldFns.length == 0) return this;
|
||||
for (int i = arity; i < length; i++) {
|
||||
Name n = names[i];
|
||||
int nfi = indexOf(n.function, oldFns);
|
||||
if (nfi >= 0 && Arrays.equals(n.arguments, forArguments)) {
|
||||
changeName(i, new Name(newFns[nfi], n.arguments));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void replaceName(int pos, Name binding) {
|
||||
assert(inTrans());
|
||||
assert(verifyArity());
|
||||
assert(pos < arity);
|
||||
Name param = names[pos];
|
||||
assert(param.isParam());
|
||||
assert(param.type == binding.type);
|
||||
changeName(pos, binding);
|
||||
}
|
||||
|
||||
/** Replace a parameter by a fresh parameter. */
|
||||
LambdaFormBuffer renameParameter(int pos, Name newParam) {
|
||||
assert(newParam.isParam());
|
||||
replaceName(pos, newParam);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replace a parameter by a fresh expression. */
|
||||
LambdaFormBuffer replaceParameterByNewExpression(int pos, Name binding) {
|
||||
assert(!binding.isParam());
|
||||
assert(lastIndexOf(binding) < 0); // else use replaceParameterByCopy
|
||||
replaceName(pos, binding);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replace a parameter by another parameter or expression already in the form. */
|
||||
LambdaFormBuffer replaceParameterByCopy(int pos, int valuePos) {
|
||||
assert(pos != valuePos);
|
||||
replaceName(pos, names[valuePos]);
|
||||
noteDuplicate(pos, valuePos); // temporarily, will occur twice in the names array
|
||||
return this;
|
||||
}
|
||||
|
||||
private void insertName(int pos, Name expr, boolean isParameter) {
|
||||
assert(inTrans());
|
||||
assert(verifyArity());
|
||||
assert(isParameter ? pos <= arity : pos >= arity);
|
||||
growNames(pos, 1);
|
||||
if (isParameter) arity += 1;
|
||||
changeName(pos, expr);
|
||||
}
|
||||
|
||||
/** Insert a fresh expression. */
|
||||
LambdaFormBuffer insertExpression(int pos, Name expr) {
|
||||
assert(!expr.isParam());
|
||||
insertName(pos, expr, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Insert a fresh parameter. */
|
||||
LambdaFormBuffer insertParameter(int pos, Name param) {
|
||||
assert(param.isParam());
|
||||
insertName(pos, param, true);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,825 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
/** Transforms on LFs.
|
||||
* A lambda-form editor can derive new LFs from its base LF.
|
||||
* The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes.
|
||||
* To support this caching, a LF has an optional pointer to its editor.
|
||||
*/
|
||||
class LambdaFormEditor {
|
||||
final LambdaForm lambdaForm;
|
||||
|
||||
private LambdaFormEditor(LambdaForm lambdaForm) {
|
||||
this.lambdaForm = lambdaForm;
|
||||
}
|
||||
|
||||
// Factory method.
|
||||
static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
|
||||
// TO DO: Consider placing intern logic here, to cut down on duplication.
|
||||
// lambdaForm = findPreexistingEquivalent(lambdaForm)
|
||||
return new LambdaFormEditor(lambdaForm);
|
||||
}
|
||||
|
||||
/** A description of a cached transform, possibly associated with the result of the transform.
|
||||
* The logical content is a sequence of byte values, starting with a Kind.ordinal value.
|
||||
* The sequence is unterminated, ending with an indefinite number of zero bytes.
|
||||
* Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
|
||||
*/
|
||||
private static final class Transform {
|
||||
final long packedBytes;
|
||||
final byte[] fullBytes;
|
||||
final LambdaForm result; // result of transform, or null, if there is none available
|
||||
|
||||
private enum Kind {
|
||||
NO_KIND, // necessary because ordinal must be greater than zero
|
||||
BIND_ARG, ADD_ARG, DUP_ARG,
|
||||
SPREAD_ARGS,
|
||||
FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
|
||||
COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
|
||||
FOLD_ARGS, FOLD_ARGS_TO_VOID,
|
||||
PERMUTE_ARGS
|
||||
//maybe add more for guard with test, catch exception, pointwise type conversions
|
||||
}
|
||||
|
||||
private static final boolean STRESS_TEST = false; // turn on to disable most packing
|
||||
private static final int
|
||||
PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4),
|
||||
PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1,
|
||||
PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE);
|
||||
|
||||
private static long packedBytes(byte[] bytes) {
|
||||
if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0;
|
||||
long pb = 0;
|
||||
int bitset = 0;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
int b = bytes[i] & 0xFF;
|
||||
bitset |= b;
|
||||
pb |= (long)b << (i * PACKED_BYTE_SIZE);
|
||||
}
|
||||
if (!inRange(bitset))
|
||||
return 0;
|
||||
return pb;
|
||||
}
|
||||
private static long packedBytes(int b0, int b1) {
|
||||
assert(inRange(b0 | b1));
|
||||
return ( (b0 << 0*PACKED_BYTE_SIZE)
|
||||
| (b1 << 1*PACKED_BYTE_SIZE));
|
||||
}
|
||||
private static long packedBytes(int b0, int b1, int b2) {
|
||||
assert(inRange(b0 | b1 | b2));
|
||||
return ( (b0 << 0*PACKED_BYTE_SIZE)
|
||||
| (b1 << 1*PACKED_BYTE_SIZE)
|
||||
| (b2 << 2*PACKED_BYTE_SIZE));
|
||||
}
|
||||
private static long packedBytes(int b0, int b1, int b2, int b3) {
|
||||
assert(inRange(b0 | b1 | b2 | b3));
|
||||
return ( (b0 << 0*PACKED_BYTE_SIZE)
|
||||
| (b1 << 1*PACKED_BYTE_SIZE)
|
||||
| (b2 << 2*PACKED_BYTE_SIZE)
|
||||
| (b3 << 3*PACKED_BYTE_SIZE));
|
||||
}
|
||||
private static boolean inRange(int bitset) {
|
||||
assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte
|
||||
return ((bitset & ~PACKED_BYTE_MASK) == 0);
|
||||
}
|
||||
private static byte[] fullBytes(int... byteValues) {
|
||||
byte[] bytes = new byte[byteValues.length];
|
||||
int i = 0;
|
||||
for (int bv : byteValues) {
|
||||
bytes[i++] = bval(bv);
|
||||
}
|
||||
assert(packedBytes(bytes) == 0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private byte byteAt(int i) {
|
||||
long pb = packedBytes;
|
||||
if (pb == 0) {
|
||||
if (i >= fullBytes.length) return 0;
|
||||
return fullBytes[i];
|
||||
}
|
||||
assert(fullBytes == null);
|
||||
if (i > PACKED_BYTE_MAX_LENGTH) return 0;
|
||||
int pos = (i * PACKED_BYTE_SIZE);
|
||||
return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
|
||||
}
|
||||
|
||||
Kind kind() { return Kind.values()[byteAt(0)]; }
|
||||
|
||||
private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
|
||||
this.packedBytes = packedBytes;
|
||||
this.fullBytes = fullBytes;
|
||||
this.result = result;
|
||||
}
|
||||
private Transform(long packedBytes) {
|
||||
this(packedBytes, null, null);
|
||||
assert(packedBytes != 0);
|
||||
}
|
||||
private Transform(byte[] fullBytes) {
|
||||
this(0, fullBytes, null);
|
||||
}
|
||||
|
||||
private static byte bval(int b) {
|
||||
assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
|
||||
return (byte)b;
|
||||
}
|
||||
private static byte bval(Kind k) {
|
||||
return bval(k.ordinal());
|
||||
}
|
||||
static Transform of(Kind k, int b1) {
|
||||
byte b0 = bval(k);
|
||||
if (inRange(b0 | b1))
|
||||
return new Transform(packedBytes(b0, b1));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1));
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2) {
|
||||
byte b0 = (byte) k.ordinal();
|
||||
if (inRange(b0 | b1 | b2))
|
||||
return new Transform(packedBytes(b0, b1, b2));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1, b2));
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2, int b3) {
|
||||
byte b0 = (byte) k.ordinal();
|
||||
if (inRange(b0 | b1 | b2 | b3))
|
||||
return new Transform(packedBytes(b0, b1, b2, b3));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1, b2, b3));
|
||||
}
|
||||
private static final byte[] NO_BYTES = {};
|
||||
static Transform of(Kind k, int... b123) {
|
||||
return ofBothArrays(k, b123, NO_BYTES);
|
||||
}
|
||||
static Transform of(Kind k, int b1, byte[] b234) {
|
||||
return ofBothArrays(k, new int[]{ b1 }, b234);
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2, byte[] b345) {
|
||||
return ofBothArrays(k, new int[]{ b1, b2 }, b345);
|
||||
}
|
||||
private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) {
|
||||
byte[] fullBytes = new byte[1 + b123.length + b456.length];
|
||||
int i = 0;
|
||||
fullBytes[i++] = bval(k);
|
||||
for (int bv : b123) {
|
||||
fullBytes[i++] = bval(bv);
|
||||
}
|
||||
for (byte bv : b456) {
|
||||
fullBytes[i++] = bv;
|
||||
}
|
||||
long packedBytes = packedBytes(fullBytes);
|
||||
if (packedBytes != 0)
|
||||
return new Transform(packedBytes);
|
||||
else
|
||||
return new Transform(fullBytes);
|
||||
}
|
||||
|
||||
Transform withResult(LambdaForm result) {
|
||||
return new Transform(this.packedBytes, this.fullBytes, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Transform && equals((Transform)obj);
|
||||
}
|
||||
public boolean equals(Transform that) {
|
||||
return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes);
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (packedBytes != 0) {
|
||||
assert(fullBytes == null);
|
||||
return Long.hashCode(packedBytes);
|
||||
}
|
||||
return Arrays.hashCode(fullBytes);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
long bits = packedBytes;
|
||||
if (bits != 0) {
|
||||
buf.append("(");
|
||||
while (bits != 0) {
|
||||
buf.append(bits & PACKED_BYTE_MASK);
|
||||
bits >>>= PACKED_BYTE_SIZE;
|
||||
if (bits != 0) buf.append(",");
|
||||
}
|
||||
buf.append(")");
|
||||
}
|
||||
if (fullBytes != null) {
|
||||
buf.append("unpacked");
|
||||
buf.append(Arrays.toString(fullBytes));
|
||||
}
|
||||
if (result != null) {
|
||||
buf.append(" result=");
|
||||
buf.append(result);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** Find a previously cached transform equivalent to the given one, and return its result. */
|
||||
private LambdaForm getInCache(Transform key) {
|
||||
assert(key.result == null);
|
||||
// The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap.
|
||||
Object c = lambdaForm.transformCache;
|
||||
Transform k = null;
|
||||
if (c instanceof ConcurrentHashMap) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
|
||||
k = m.get(key);
|
||||
} else if (c == null) {
|
||||
return null;
|
||||
} else if (c instanceof Transform) {
|
||||
// one-element cache avoids overhead of an array
|
||||
Transform t = (Transform)c;
|
||||
if (t.equals(key)) k = t;
|
||||
} else {
|
||||
Transform[] ta = (Transform[])c;
|
||||
for (int i = 0; i < ta.length; i++) {
|
||||
Transform t = ta[i];
|
||||
if (t == null) break;
|
||||
if (t.equals(key)) { k = t; break; }
|
||||
}
|
||||
}
|
||||
assert(k == null || key.equals(k));
|
||||
return k == null ? null : k.result;
|
||||
}
|
||||
|
||||
/** Arbitrary but reasonable limits on Transform[] size for cache. */
|
||||
private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
|
||||
|
||||
/** Cache a transform with its result, and return that result.
|
||||
* But if an equivalent transform has already been cached, return its result instead.
|
||||
*/
|
||||
private LambdaForm putInCache(Transform key, LambdaForm form) {
|
||||
key = key.withResult(form);
|
||||
for (int pass = 0; ; pass++) {
|
||||
Object c = lambdaForm.transformCache;
|
||||
if (c instanceof ConcurrentHashMap) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
|
||||
Transform k = m.putIfAbsent(key, key);
|
||||
return k != null ? k.result : form;
|
||||
}
|
||||
assert(pass == 0);
|
||||
synchronized (lambdaForm) {
|
||||
c = lambdaForm.transformCache;
|
||||
if (c instanceof ConcurrentHashMap)
|
||||
continue;
|
||||
if (c == null) {
|
||||
lambdaForm.transformCache = key;
|
||||
return form;
|
||||
}
|
||||
Transform[] ta;
|
||||
if (c instanceof Transform) {
|
||||
Transform k = (Transform)c;
|
||||
if (k.equals(key)) {
|
||||
return k.result;
|
||||
}
|
||||
// expand one-element cache to small array
|
||||
ta = new Transform[MIN_CACHE_ARRAY_SIZE];
|
||||
ta[0] = k;
|
||||
lambdaForm.transformCache = c = ta;
|
||||
} else {
|
||||
// it is already expanded
|
||||
ta = (Transform[])c;
|
||||
}
|
||||
int len = ta.length;
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
Transform k = ta[i];
|
||||
if (k == null) {
|
||||
break;
|
||||
}
|
||||
if (k.equals(key)) {
|
||||
return k.result;
|
||||
}
|
||||
}
|
||||
if (i < len) {
|
||||
// just fall through to cache update
|
||||
} else if (len < MAX_CACHE_ARRAY_SIZE) {
|
||||
len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE);
|
||||
ta = Arrays.copyOf(ta, len);
|
||||
lambdaForm.transformCache = ta;
|
||||
} else {
|
||||
ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2);
|
||||
for (Transform k : ta) {
|
||||
m.put(k, k);
|
||||
}
|
||||
lambdaForm.transformCache = m;
|
||||
// The second iteration will update for this query, concurrently.
|
||||
continue;
|
||||
}
|
||||
ta[i] = key;
|
||||
return form;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LambdaFormBuffer buffer() {
|
||||
return new LambdaFormBuffer(lambdaForm);
|
||||
}
|
||||
|
||||
/// Editing methods for method handles. These need to have fast paths.
|
||||
|
||||
private BoundMethodHandle.SpeciesData oldSpeciesData() {
|
||||
return BoundMethodHandle.speciesData(lambdaForm);
|
||||
}
|
||||
private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
|
||||
return oldSpeciesData().extendWith(type);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = L_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendL(type2, form2, value);
|
||||
}
|
||||
BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = I_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendI(type2, form2, value);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = J_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendJ(type2, form2, value);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = F_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendF(type2, form2, value);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = D_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendD(type2, form2, value);
|
||||
}
|
||||
|
||||
private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) {
|
||||
assert(mh.form == lambdaForm);
|
||||
assert(mh.form.names[1+pos].type == bt);
|
||||
assert(BasicType.basicType(mh.type().parameterType(pos)) == bt);
|
||||
return mh.type().dropParameterTypes(pos, pos+1);
|
||||
}
|
||||
|
||||
/// Editing methods for lambda forms.
|
||||
// Each editing method can (potentially) cache the edited LF so that it can be reused later.
|
||||
|
||||
LambdaForm bindArgumentForm(int pos) {
|
||||
Transform key = Transform.of(Transform.Kind.BIND_ARG, pos);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos));
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
Name newBaseAddress;
|
||||
NamedFunction getter = newData.getterFunction(oldData.fieldCount());
|
||||
|
||||
if (pos != 0) {
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress));
|
||||
} else {
|
||||
// cannot bind the MH arg itself, unless oldData is empty
|
||||
assert(oldData == BoundMethodHandle.SpeciesData.EMPTY);
|
||||
newBaseAddress = new Name(L_TYPE).withConstraint(newData);
|
||||
buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress));
|
||||
buf.insertParameter(0, newBaseAddress);
|
||||
}
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm addArgumentForm(int pos, BasicType type) {
|
||||
Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity+1);
|
||||
assert(form.parameterType(pos) == type);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
buf.insertParameter(pos, new Name(type));
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm dupArgumentForm(int srcPos, int dstPos) {
|
||||
Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity-1);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(lambdaForm.parameter(srcPos).constraint == null);
|
||||
assert(lambdaForm.parameter(dstPos).constraint == null);
|
||||
buf.replaceParameterByCopy(dstPos, srcPos);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) {
|
||||
Class<?> elementType = arrayType.getComponentType();
|
||||
Class<?> erasedArrayType = arrayType;
|
||||
if (!elementType.isPrimitive())
|
||||
erasedArrayType = Object[].class;
|
||||
BasicType bt = basicType(elementType);
|
||||
int elementTypeKey = bt.ordinal();
|
||||
if (bt.basicTypeClass() != elementType) {
|
||||
if (elementType.isPrimitive()) {
|
||||
elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
}
|
||||
Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - arrayLength + 1);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(pos <= MethodType.MAX_JVM_ARITY);
|
||||
assert(pos + arrayLength <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot spread the MH arg itself
|
||||
|
||||
Name spreadParam = new Name(L_TYPE);
|
||||
Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength);
|
||||
|
||||
// insert the new expressions
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos++, checkSpread);
|
||||
// adjust the arguments
|
||||
MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType);
|
||||
for (int i = 0; i < arrayLength; i++) {
|
||||
Name loadArgument = new Name(aload, spreadParam, i);
|
||||
buf.insertExpression(exprPos + i, loadArgument);
|
||||
buf.replaceParameterByCopy(pos + i, exprPos + i);
|
||||
}
|
||||
buf.insertParameter(pos, spreadParam);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm collectArgumentsForm(int pos, MethodType collectorType) {
|
||||
int collectorArity = collectorType.parameterCount();
|
||||
boolean dropResult = (collectorType.returnType() == void.class);
|
||||
if (collectorArity == 1 && !dropResult) {
|
||||
return filterArgumentForm(pos, basicType(collectorType.parameterType(0)));
|
||||
}
|
||||
BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList());
|
||||
Transform.Kind kind = (dropResult
|
||||
? Transform.Kind.COLLECT_ARGS_TO_VOID
|
||||
: Transform.Kind.COLLECT_ARGS);
|
||||
if (dropResult && collectorArity == 0) pos = 1; // pure side effect
|
||||
Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes));
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity);
|
||||
return form;
|
||||
}
|
||||
form = makeArgumentCombinationForm(pos, collectorType, false, dropResult);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm collectArgumentArrayForm(int pos, MethodHandle arrayCollector) {
|
||||
MethodType collectorType = arrayCollector.type();
|
||||
int collectorArity = collectorType.parameterCount();
|
||||
assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY);
|
||||
Class<?> arrayType = collectorType.returnType();
|
||||
Class<?> elementType = arrayType.getComponentType();
|
||||
BasicType argType = basicType(elementType);
|
||||
int argTypeKey = argType.ordinal();
|
||||
if (argType.basicTypeClass() != elementType) {
|
||||
// return null if it requires more metadata (like String[].class)
|
||||
if (!elementType.isPrimitive())
|
||||
return null;
|
||||
argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
|
||||
Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
|
||||
Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - 1 + collectorArity);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(pos + 1 <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot filter the MH arg itself
|
||||
|
||||
Name[] newParams = new Name[collectorArity];
|
||||
for (int i = 0; i < collectorArity; i++) {
|
||||
newParams[i] = new Name(pos + i, argType);
|
||||
}
|
||||
Name callCombiner = new Name(arrayCollector, (Object[]) /*...*/ newParams);
|
||||
|
||||
// insert the new expression
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos, callCombiner);
|
||||
|
||||
// insert new arguments
|
||||
int argPos = pos + 1; // skip result parameter
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(argPos++, newParam);
|
||||
}
|
||||
assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length);
|
||||
buf.replaceParameterByCopy(pos, exprPos+newParams.length);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm filterArgumentForm(int pos, BasicType newType) {
|
||||
Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity);
|
||||
assert(form.parameterType(pos) == newType);
|
||||
return form;
|
||||
}
|
||||
|
||||
BasicType oldType = lambdaForm.parameterType(pos);
|
||||
MethodType filterType = MethodType.methodType(oldType.basicTypeClass(),
|
||||
newType.basicTypeClass());
|
||||
form = makeArgumentCombinationForm(pos, filterType, false, false);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
private LambdaForm makeArgumentCombinationForm(int pos,
|
||||
MethodType combinerType,
|
||||
boolean keepArguments, boolean dropResult) {
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
int resultArity = (dropResult ? 0 : 1);
|
||||
|
||||
assert(pos <= MethodType.MAX_JVM_ARITY);
|
||||
assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot filter the MH arg itself
|
||||
assert(combinerType == combinerType.basicType());
|
||||
assert(combinerType.returnType() != void.class || dropResult);
|
||||
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
|
||||
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
Name newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
|
||||
Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
|
||||
Object[] combinerArgs = new Object[1 + combinerArity];
|
||||
combinerArgs[0] = getCombiner;
|
||||
Name[] newParams;
|
||||
if (keepArguments) {
|
||||
newParams = new Name[0];
|
||||
System.arraycopy(lambdaForm.names, pos + resultArity,
|
||||
combinerArgs, 1, combinerArity);
|
||||
} else {
|
||||
newParams = new Name[combinerArity];
|
||||
BasicType[] newTypes = basicTypes(combinerType.parameterList());
|
||||
for (int i = 0; i < newTypes.length; i++) {
|
||||
newParams[i] = new Name(pos + i, newTypes[i]);
|
||||
}
|
||||
System.arraycopy(newParams, 0,
|
||||
combinerArgs, 1, combinerArity);
|
||||
}
|
||||
Name callCombiner = new Name(combinerType, combinerArgs);
|
||||
|
||||
// insert the two new expressions
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos+0, getCombiner);
|
||||
buf.insertExpression(exprPos+1, callCombiner);
|
||||
|
||||
// insert new arguments, if needed
|
||||
int argPos = pos + resultArity; // skip result parameter
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(argPos++, newParam);
|
||||
}
|
||||
assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length);
|
||||
if (!dropResult) {
|
||||
buf.replaceParameterByCopy(pos, exprPos+1+newParams.length);
|
||||
}
|
||||
|
||||
return buf.endEdit();
|
||||
}
|
||||
|
||||
LambdaForm filterReturnForm(BasicType newType, boolean constantZero) {
|
||||
Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN);
|
||||
Transform key = Transform.of(kind, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity);
|
||||
assert(form.returnType() == newType);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
int insPos = lambdaForm.names.length;
|
||||
Name callFilter;
|
||||
if (constantZero) {
|
||||
// Synthesize a constant zero value for the given type.
|
||||
if (newType == V_TYPE)
|
||||
callFilter = null;
|
||||
else
|
||||
callFilter = new Name(constantZero(newType));
|
||||
} else {
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
|
||||
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
Name newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
|
||||
Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
|
||||
buf.insertExpression(insPos++, getFilter);
|
||||
BasicType oldType = lambdaForm.returnType();
|
||||
if (oldType == V_TYPE) {
|
||||
MethodType filterType = MethodType.methodType(newType.basicTypeClass());
|
||||
callFilter = new Name(filterType, getFilter);
|
||||
} else {
|
||||
MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass());
|
||||
callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]);
|
||||
}
|
||||
}
|
||||
|
||||
if (callFilter != null)
|
||||
buf.insertExpression(insPos++, callFilter);
|
||||
buf.setResult(callFilter);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS);
|
||||
Transform key = Transform.of(kind, foldPos, combinerArity);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0));
|
||||
return form;
|
||||
}
|
||||
form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm permuteArgumentsForm(int skip, int[] reorder) {
|
||||
assert(skip == 1); // skip only the leading MH argument, names[0]
|
||||
int length = lambdaForm.names.length;
|
||||
int outArgs = reorder.length;
|
||||
int inTypes = 0;
|
||||
boolean nullPerm = true;
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
int inArg = reorder[i];
|
||||
if (inArg != i) nullPerm = false;
|
||||
inTypes = Math.max(inTypes, inArg+1);
|
||||
}
|
||||
assert(skip + reorder.length == lambdaForm.arity);
|
||||
if (nullPerm) return lambdaForm; // do not bother to cache
|
||||
Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == skip+inTypes) : form;
|
||||
return form;
|
||||
}
|
||||
|
||||
BasicType[] types = new BasicType[inTypes];
|
||||
for (int i = 0; i < outArgs; i++) {
|
||||
int inArg = reorder[i];
|
||||
types[inArg] = lambdaForm.names[skip + i].type;
|
||||
}
|
||||
assert (skip + outArgs == lambdaForm.arity);
|
||||
assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip));
|
||||
int pos = 0;
|
||||
while (pos < outArgs && reorder[pos] == pos) {
|
||||
pos += 1;
|
||||
}
|
||||
Name[] names2 = new Name[length - outArgs + inTypes];
|
||||
System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos);
|
||||
int bodyLength = length - lambdaForm.arity;
|
||||
System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength);
|
||||
int arity2 = names2.length - bodyLength;
|
||||
int result2 = lambdaForm.result;
|
||||
if (result2 >= 0) {
|
||||
if (result2 < skip + outArgs) {
|
||||
result2 = reorder[result2 - skip];
|
||||
} else {
|
||||
result2 = result2 - outArgs + inTypes;
|
||||
}
|
||||
}
|
||||
for (int j = pos; j < outArgs; j++) {
|
||||
Name n = lambdaForm.names[skip + j];
|
||||
int i = reorder[j];
|
||||
Name n2 = names2[skip + i];
|
||||
if (n2 == null) {
|
||||
names2[skip + i] = n2 = new Name(types[i]);
|
||||
} else {
|
||||
assert (n2.type == types[i]);
|
||||
}
|
||||
for (int k = arity2; k < names2.length; k++) {
|
||||
names2[k] = names2[k].replaceName(n, n2);
|
||||
}
|
||||
}
|
||||
for (int i = skip + pos; i < arity2; i++) {
|
||||
if (names2[i] == null) {
|
||||
names2[i] = argument(i, types[i - skip]);
|
||||
}
|
||||
}
|
||||
for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) {
|
||||
int i = j - lambdaForm.arity + arity2;
|
||||
Name n = lambdaForm.names[j];
|
||||
Name n2 = names2[i];
|
||||
if (n != n2) {
|
||||
for (int k = i + 1; k < names2.length; k++) {
|
||||
names2[k] = names2[k].replaceName(n, n2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) {
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
assert (names[skip + i].isParam());
|
||||
assert (names[skip + i].type == types[reorder[i]]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -327,10 +327,6 @@ import java.util.Objects;
|
||||
assert(getReferenceKind() == oldKind);
|
||||
assert(MethodHandleNatives.refKindIsValid(refKind));
|
||||
flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
|
||||
// if (isConstructor() && refKind != REF_newInvokeSpecial)
|
||||
// flags += (IS_METHOD - IS_CONSTRUCTOR);
|
||||
// else if (refKind == REF_newInvokeSpecial && isMethod())
|
||||
// flags += (IS_CONSTRUCTOR - IS_METHOD);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -344,9 +340,11 @@ import java.util.Objects;
|
||||
return !testFlags(mask, 0);
|
||||
}
|
||||
|
||||
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
|
||||
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
|
||||
* Also returns true for the non-public MH.invokeBasic.
|
||||
*/
|
||||
public boolean isMethodHandleInvoke() {
|
||||
final int bits = MH_INVOKE_MODS;
|
||||
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
|
||||
final int negs = Modifier.STATIC;
|
||||
if (testFlags(bits | negs, bits) &&
|
||||
clazz == MethodHandle.class) {
|
||||
@ -355,7 +353,14 @@ import java.util.Objects;
|
||||
return false;
|
||||
}
|
||||
public static boolean isMethodHandleInvokeName(String name) {
|
||||
return name.equals("invoke") || name.equals("invokeExact");
|
||||
switch (name) {
|
||||
case "invoke":
|
||||
case "invokeExact":
|
||||
case "invokeBasic": // internal sig-poly method
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
|
||||
|
||||
@ -720,16 +725,8 @@ import java.util.Objects;
|
||||
init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
|
||||
initResolved(false);
|
||||
}
|
||||
/** Create a field or type name from the given components: Declaring class, name, type.
|
||||
* 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, Class<?> type, Void unused) {
|
||||
this(defClass, name, type, REF_NONE);
|
||||
initResolved(false);
|
||||
}
|
||||
/** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
|
||||
/** Create a method or constructor name from the given components:
|
||||
* Declaring class, name, type, reference kind.
|
||||
* 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 last argument is optional, a boolean which requests REF_invokeSpecial.
|
||||
|
@ -27,12 +27,8 @@ package java.lang.invoke;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.lang.invoke.LambdaForm.BasicType;
|
||||
import sun.invoke.util.*;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
|
||||
/**
|
||||
* A method handle is a typed, directly executable reference to an underlying method,
|
||||
@ -625,15 +621,8 @@ public abstract class MethodHandle {
|
||||
* @see MethodHandles#spreadInvoker
|
||||
*/
|
||||
public Object invokeWithArguments(Object... arguments) throws Throwable {
|
||||
int argc = arguments == null ? 0 : arguments.length;
|
||||
@SuppressWarnings("LocalVariableHidesMemberVariable")
|
||||
MethodType type = type();
|
||||
if (type.parameterCount() != argc || isVarargsCollector()) {
|
||||
// simulate invoke
|
||||
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
|
||||
}
|
||||
MethodHandle invoker = type.invokers().varargsInvoker();
|
||||
return invoker.invokeExact(this, arguments);
|
||||
MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
|
||||
return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -763,18 +752,26 @@ public abstract class MethodHandle {
|
||||
return this;
|
||||
}
|
||||
// Return 'this.asTypeCache' if the conversion is already memoized.
|
||||
MethodHandle atc = asTypeCached(newType);
|
||||
if (atc != null) {
|
||||
return atc;
|
||||
}
|
||||
return asTypeUncached(newType);
|
||||
}
|
||||
|
||||
private MethodHandle asTypeCached(MethodType newType) {
|
||||
MethodHandle atc = asTypeCache;
|
||||
if (atc != null && newType == atc.type) {
|
||||
return atc;
|
||||
}
|
||||
return asTypeUncached(newType);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
return asTypeCache = MethodHandleImpl.makePairwiseConvert(this, newType, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -867,34 +864,48 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray
|
||||
* @see #asCollector
|
||||
*/
|
||||
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
|
||||
asSpreaderChecks(arrayType, arrayLength);
|
||||
int spreadArgPos = type.parameterCount() - arrayLength;
|
||||
return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
|
||||
MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
|
||||
int arity = type().parameterCount();
|
||||
int spreadArgPos = arity - arrayLength;
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
MethodHandle afterSpread = this.asType(postSpreadType);
|
||||
BoundMethodHandle mh = afterSpread.rebind();
|
||||
LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength);
|
||||
MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType);
|
||||
return mh.copyWith(preSpreadType, lform);
|
||||
} else {
|
||||
return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
|
||||
}
|
||||
}
|
||||
|
||||
private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
|
||||
/**
|
||||
* See if {@code asSpreader} can be validly called with the given arguments.
|
||||
* Return the type of the method handle call after spreading but before conversions.
|
||||
*/
|
||||
private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
|
||||
spreadArrayChecks(arrayType, arrayLength);
|
||||
int nargs = type().parameterCount();
|
||||
if (nargs < arrayLength || arrayLength < 0)
|
||||
throw newIllegalArgumentException("bad spread array length");
|
||||
if (arrayType != Object[].class && arrayLength != 0) {
|
||||
boolean sawProblem = false;
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
for (int i = nargs - arrayLength; i < nargs; i++) {
|
||||
if (!MethodType.canConvert(arrayElement, type().parameterType(i))) {
|
||||
sawProblem = true;
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
MethodType mtype = type();
|
||||
boolean match = true, fail = false;
|
||||
for (int i = nargs - arrayLength; i < nargs; i++) {
|
||||
Class<?> ptype = mtype.parameterType(i);
|
||||
if (ptype != arrayElement) {
|
||||
match = false;
|
||||
if (!MethodType.canConvert(arrayElement, ptype)) {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sawProblem) {
|
||||
ArrayList<Class<?>> ptypes = new ArrayList<>(type().parameterList());
|
||||
for (int i = nargs - arrayLength; i < nargs; i++) {
|
||||
ptypes.set(i, arrayElement);
|
||||
}
|
||||
// elicit an error:
|
||||
this.asType(MethodType.methodType(type().returnType(), ptypes));
|
||||
}
|
||||
}
|
||||
if (match) return mtype;
|
||||
MethodType needType = mtype.asSpreaderType(arrayType, arrayLength);
|
||||
if (!fail) return needType;
|
||||
// elicit an error:
|
||||
this.asType(needType);
|
||||
throw newInternalError("should not return", null);
|
||||
}
|
||||
|
||||
private void spreadArrayChecks(Class<?> arrayType, int arrayLength) {
|
||||
@ -984,16 +995,31 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
*/
|
||||
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
|
||||
asCollectorChecks(arrayType, arrayLength);
|
||||
int collectArgPos = type().parameterCount()-1;
|
||||
MethodHandle target = this;
|
||||
if (arrayType != type().parameterType(collectArgPos))
|
||||
target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
|
||||
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
|
||||
return MethodHandles.collectArguments(target, collectArgPos, collector);
|
||||
int collectArgPos = type().parameterCount() - 1;
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
BoundMethodHandle mh = rebind();
|
||||
MethodType resultType = type().asCollectorType(arrayType, arrayLength);
|
||||
MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray);
|
||||
if (lform != null) {
|
||||
return mh.copyWith(resultType, lform);
|
||||
}
|
||||
lform = mh.editor().collectArgumentsForm(1 + collectArgPos, newArray.type().basicType());
|
||||
return mh.copyWithExtendL(resultType, lform, newArray);
|
||||
} else {
|
||||
MethodHandle target = this;
|
||||
if (arrayType != type().parameterType(collectArgPos))
|
||||
target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true);
|
||||
MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
return MethodHandles.collectArguments(target, collectArgPos, collector);
|
||||
}
|
||||
}
|
||||
|
||||
// private API: return true if last param exactly matches arrayType
|
||||
private boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||
/**
|
||||
* See if {@code asCollector} can be validly called with the given arguments.
|
||||
* Return false if the last parameter is not an exact match to arrayType.
|
||||
*/
|
||||
/*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||
spreadArrayChecks(arrayType, arrayLength);
|
||||
int nargs = type().parameterCount();
|
||||
if (nargs != 0) {
|
||||
@ -1155,7 +1181,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
* @see #asFixedArity
|
||||
*/
|
||||
public MethodHandle asVarargsCollector(Class<?> arrayType) {
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
arrayType.getClass(); // explicit NPE
|
||||
boolean lastMatch = asCollectorChecks(arrayType, 0);
|
||||
if (isVarargsCollector() && lastMatch)
|
||||
return this;
|
||||
@ -1257,14 +1283,8 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
* @see MethodHandles#insertArguments
|
||||
*/
|
||||
public MethodHandle bindTo(Object x) {
|
||||
Class<?> ptype;
|
||||
@SuppressWarnings("LocalVariableHidesMemberVariable")
|
||||
MethodType type = type();
|
||||
if (type.parameterCount() == 0 ||
|
||||
(ptype = type.parameterType(0)).isPrimitive())
|
||||
throw newIllegalArgumentException("no leading reference parameter", x);
|
||||
x = ptype.cast(x); // throw CCE if needed
|
||||
return bindReceiver(x);
|
||||
x = type.leadingReferenceParameter().cast(x); // throw CCE if needed
|
||||
return bindArgumentL(0, x);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1284,14 +1304,17 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (DEBUG_METHOD_HANDLE_NAMES) return debugString();
|
||||
if (DEBUG_METHOD_HANDLE_NAMES) return "MethodHandle"+debugString();
|
||||
return standardString();
|
||||
}
|
||||
String standardString() {
|
||||
return "MethodHandle"+type;
|
||||
}
|
||||
/** Return a string with a several lines describing the method handle structure.
|
||||
* This string would be suitable for display in an IDE debugger.
|
||||
*/
|
||||
String debugString() {
|
||||
return standardString()+"/LF="+internalForm()+internalProperties();
|
||||
return type+" : "+internalForm()+internalProperties();
|
||||
}
|
||||
|
||||
//// Implementation methods.
|
||||
@ -1300,22 +1323,42 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
|
||||
// Other transforms to do: convert, explicitCast, permute, drop, filter, fold, GWT, catch
|
||||
|
||||
BoundMethodHandle bindArgumentL(int pos, Object value) {
|
||||
return rebind().bindArgumentL(pos, value);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
|
||||
if (!member.isVarargs()) return this;
|
||||
int argc = type().parameterCount();
|
||||
if (argc != 0) {
|
||||
Class<?> arrayType = type().parameterType(argc-1);
|
||||
if (arrayType.isArray()) {
|
||||
return MethodHandleImpl.makeVarargsCollector(this, arrayType);
|
||||
}
|
||||
Class<?> arrayType = type().lastParameterType();
|
||||
if (arrayType.isArray()) {
|
||||
return MethodHandleImpl.makeVarargsCollector(this, arrayType);
|
||||
}
|
||||
throw member.makeAccessException("cannot make variable arity", null);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle viewAsType(MethodType newType) {
|
||||
MethodHandle viewAsType(MethodType newType, boolean strict) {
|
||||
// No actual conversions, just a new view of the same method.
|
||||
return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
|
||||
// Note that this operation must not produce a DirectMethodHandle,
|
||||
// because retyped DMHs, like any transformed MHs,
|
||||
// cannot be cracked into MethodHandleInfo.
|
||||
assert viewAsTypeChecks(newType, strict);
|
||||
BoundMethodHandle mh = rebind();
|
||||
assert(!((MethodHandle)mh instanceof DirectMethodHandle));
|
||||
return mh.copyWith(newType, mh.form);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
boolean viewAsTypeChecks(MethodType newType, boolean strict) {
|
||||
if (strict) {
|
||||
assert(type().isViewableAs(newType, true))
|
||||
: Arrays.asList(this, newType);
|
||||
} else {
|
||||
assert(type().basicType().isViewableAs(newType.basicType(), true))
|
||||
: Arrays.asList(this, newType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decoding
|
||||
@ -1336,9 +1379,15 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle withInternalMemberName(MemberName member) {
|
||||
MethodHandleImpl.Intrinsic intrinsicName() {
|
||||
// no special intrinsic meaning to most MHs
|
||||
return MethodHandleImpl.Intrinsic.NONE;
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial) {
|
||||
if (member != null) {
|
||||
return MethodHandleImpl.makeWrappedMember(this, member);
|
||||
return MethodHandleImpl.makeWrappedMember(this, member, isInvokeSpecial);
|
||||
} else if (internalMemberName() == null) {
|
||||
// The required internaMemberName is null, and this MH (like most) doesn't have one.
|
||||
return this;
|
||||
@ -1362,7 +1411,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
|
||||
/*non-public*/
|
||||
Object internalProperties() {
|
||||
// Override to something like "/FOO=bar"
|
||||
// Override to something to follow this.form, like "\n& FOO=bar"
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -1370,95 +1419,14 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
//// Sub-classes can override these default implementations.
|
||||
//// All these methods assume arguments are already validated.
|
||||
|
||||
/*non-public*/ MethodHandle convertArguments(MethodType newType) {
|
||||
// Override this if it can be improved.
|
||||
return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
|
||||
// Override this if it can be improved.
|
||||
return rebind().bindArgument(pos, basicType, value);
|
||||
}
|
||||
abstract MethodHandle copyWith(MethodType mt, LambdaForm lf);
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle bindReceiver(Object receiver) {
|
||||
// Override this if it can be improved.
|
||||
return bindArgument(0, L_TYPE, receiver);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
|
||||
// Override this if it can be improved.
|
||||
return rebind().dropArguments(srcType, pos, drops);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle permuteArguments(MethodType newType, int[] reorder) {
|
||||
// Override this if it can be improved.
|
||||
return rebind().permuteArguments(newType, reorder);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle rebind() {
|
||||
// Bind 'this' into a new invoker, of the known class BMH.
|
||||
MethodType type2 = type();
|
||||
LambdaForm form2 = reinvokerForm(this);
|
||||
// form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
|
||||
return BoundMethodHandle.bindSingle(type2, form2, this);
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
MethodHandle reinvokerTarget() {
|
||||
throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
|
||||
}
|
||||
|
||||
/** Create a LF which simply reinvokes a target of the given basic type.
|
||||
* The target MH must override {@link #reinvokerTarget} to provide the target.
|
||||
/** Require this method handle to be a BMH, or else replace it with a "wrapper" BMH.
|
||||
* Many transforms are implemented only for BMHs.
|
||||
* @return a behaviorally equivalent BMH
|
||||
*/
|
||||
static LambdaForm reinvokerForm(MethodHandle target) {
|
||||
MethodType mtype = target.type().basicType();
|
||||
LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
|
||||
if (reinvoker != null) return reinvoker;
|
||||
if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY)
|
||||
return makeReinvokerForm(target.type(), target); // cannot cache this
|
||||
reinvoker = makeReinvokerForm(mtype, null);
|
||||
return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker);
|
||||
}
|
||||
private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) {
|
||||
boolean customized = (customTargetOrNull != null);
|
||||
MethodHandle MH_invokeBasic = customized ? null : MethodHandles.basicInvoker(mtype);
|
||||
final int THIS_BMH = 0;
|
||||
final int ARG_BASE = 1;
|
||||
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int NEXT_MH = customized ? -1 : nameCursor++;
|
||||
final int REINVOKE = nameCursor++;
|
||||
LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||
Object[] targetArgs;
|
||||
MethodHandle targetMH;
|
||||
if (customized) {
|
||||
targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
|
||||
targetMH = customTargetOrNull;
|
||||
} else {
|
||||
names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
|
||||
targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
|
||||
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
|
||||
targetMH = MethodHandles.basicInvoker(mtype);
|
||||
}
|
||||
names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs);
|
||||
return new LambdaForm("BMH.reinvoke", ARG_LIMIT, names);
|
||||
}
|
||||
|
||||
private static final LambdaForm.NamedFunction NF_reinvokerTarget;
|
||||
static {
|
||||
try {
|
||||
NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
|
||||
.getDeclaredMethod("reinvokerTarget"));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
abstract BoundMethodHandle rebind();
|
||||
|
||||
/**
|
||||
* Replace the old lambda form of this method handle with a new one.
|
||||
@ -1470,6 +1438,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
/*non-public*/
|
||||
void updateForm(LambdaForm newForm) {
|
||||
if (form == newForm) return;
|
||||
assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic());
|
||||
// ISSUE: Should we have a memory fence here?
|
||||
UNSAFE.putObject(this, FORM_OFFSET, newForm);
|
||||
this.form.prepare(); // as in MethodHandle.<init>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -33,6 +33,7 @@ import java.util.ArrayList;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* This class consists exclusively of static methods that help adapt
|
||||
@ -148,7 +149,7 @@ public class MethodHandleProxies {
|
||||
public static
|
||||
<T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) {
|
||||
if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers()))
|
||||
throw new IllegalArgumentException("not a public interface: "+intfc.getName());
|
||||
throw newIllegalArgumentException("not a public interface", intfc.getName());
|
||||
final MethodHandle mh;
|
||||
if (System.getSecurityManager() != null) {
|
||||
final Class<?> caller = Reflection.getCallerClass();
|
||||
@ -165,7 +166,7 @@ public class MethodHandleProxies {
|
||||
}
|
||||
final Method[] methods = getSingleNameMethods(intfc);
|
||||
if (methods == null)
|
||||
throw new IllegalArgumentException("not a single-method interface: "+intfc.getName());
|
||||
throw newIllegalArgumentException("not a single-method interface", intfc.getName());
|
||||
final MethodHandle[] vaTargets = new MethodHandle[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Method sm = methods[i];
|
||||
@ -189,7 +190,7 @@ public class MethodHandleProxies {
|
||||
return getArg(method.getName());
|
||||
if (isObjectMethod(method))
|
||||
return callObjectMethod(proxy, method, args);
|
||||
throw new InternalError("bad proxy method: "+method);
|
||||
throw newInternalError("bad proxy method: "+method);
|
||||
}
|
||||
};
|
||||
|
||||
@ -240,7 +241,7 @@ public class MethodHandleProxies {
|
||||
return (WrapperInstance) x;
|
||||
} catch (ClassCastException ex) {
|
||||
}
|
||||
throw new IllegalArgumentException("not a wrapper instance");
|
||||
throw newIllegalArgumentException("not a wrapper instance");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,16 +45,21 @@ import sun.misc.Unsafe;
|
||||
static final boolean DUMP_CLASS_FILES;
|
||||
static final boolean TRACE_INTERPRETER;
|
||||
static final boolean TRACE_METHOD_LINKAGE;
|
||||
static final Integer COMPILE_THRESHOLD;
|
||||
static final boolean USE_LAMBDA_FORM_EDITOR;
|
||||
static final int COMPILE_THRESHOLD;
|
||||
static final int PROFILE_LEVEL;
|
||||
|
||||
static {
|
||||
final Object[] values = { false, false, false, false, null };
|
||||
final Object[] values = { false, false, false, false, false, null, null };
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
|
||||
values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
|
||||
values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
|
||||
values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
|
||||
values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
|
||||
values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.USE_LF_EDITOR");
|
||||
values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30);
|
||||
values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@ -62,7 +67,9 @@ import sun.misc.Unsafe;
|
||||
DUMP_CLASS_FILES = (Boolean) values[1];
|
||||
TRACE_INTERPRETER = (Boolean) values[2];
|
||||
TRACE_METHOD_LINKAGE = (Boolean) values[3];
|
||||
COMPILE_THRESHOLD = (Integer) values[4];
|
||||
USE_LAMBDA_FORM_EDITOR = (Boolean) values[4];
|
||||
COMPILE_THRESHOLD = (Integer) values[5];
|
||||
PROFILE_LEVEL = (Integer) values[6];
|
||||
}
|
||||
|
||||
/** Tell if any of the debugging switches are turned on.
|
||||
@ -127,7 +134,10 @@ import sun.misc.Unsafe;
|
||||
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
|
||||
return new IllegalArgumentException(message(message, obj, obj2));
|
||||
}
|
||||
/** Propagate unchecked exceptions and errors, but wrap anything checked and throw that instead. */
|
||||
/*non-public*/ static Error uncaughtException(Throwable ex) {
|
||||
if (ex instanceof Error) throw (Error) ex;
|
||||
if (ex instanceof RuntimeException) throw (RuntimeException) ex;
|
||||
throw newInternalError("uncaught exception", ex);
|
||||
}
|
||||
static Error NYI() {
|
||||
|
@ -26,8 +26,8 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.invoke.util.ValueConversions;
|
||||
@ -40,6 +40,7 @@ import sun.security.util.SecurityConstants;
|
||||
import java.lang.invoke.LambdaForm.BasicType;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -862,6 +863,8 @@ assertEquals("", (String) MH_newString.invokeExact());
|
||||
return invoker(type);
|
||||
if ("invokeExact".equals(name))
|
||||
return exactInvoker(type);
|
||||
if ("invokeBasic".equals(name))
|
||||
return basicInvoker(type);
|
||||
assert(!MemberName.isMethodHandleInvokeName(name));
|
||||
return null;
|
||||
}
|
||||
@ -1141,7 +1144,7 @@ return mh1;
|
||||
Class<? extends Object> refc = receiver.getClass(); // may get NPE
|
||||
MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
|
||||
MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
|
||||
return mh.bindReceiver(receiver).setVarargs(method);
|
||||
return mh.bindArgumentL(0, receiver).setVarargs(method);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1576,7 +1579,7 @@ return mh1;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
private MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
|
||||
private MethodHandle restrictReceiver(MemberName method, DirectMethodHandle mh, Class<?> caller) throws IllegalAccessException {
|
||||
assert(!method.isStatic());
|
||||
// receiver type of mh is too wide; narrow to caller
|
||||
if (!method.getDeclaringClass().isAssignableFrom(caller)) {
|
||||
@ -1585,7 +1588,9 @@ return mh1;
|
||||
MethodType rawType = mh.type();
|
||||
if (rawType.parameterType(0) == caller) return mh;
|
||||
MethodType narrowType = rawType.changeParameterType(0, caller);
|
||||
return mh.viewAsType(narrowType);
|
||||
assert(!mh.isVarargsCollector()); // viewAsType will lose varargs-ness
|
||||
assert(mh.viewAsTypeChecks(narrowType, true));
|
||||
return mh.copyWith(narrowType, mh.form);
|
||||
}
|
||||
|
||||
/** Check access and get the requested method. */
|
||||
@ -1647,15 +1652,17 @@ return mh1;
|
||||
checkMethod(refKind, refc, method);
|
||||
}
|
||||
|
||||
MethodHandle mh = DirectMethodHandle.make(refKind, refc, method);
|
||||
mh = maybeBindCaller(method, mh, callerClass);
|
||||
mh = mh.setVarargs(method);
|
||||
DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method);
|
||||
MethodHandle mh = dmh;
|
||||
// Optionally narrow the receiver argument to refc using restrictReceiver.
|
||||
if (doRestrict &&
|
||||
(refKind == REF_invokeSpecial ||
|
||||
(MethodHandleNatives.refKindHasReceiver(refKind) &&
|
||||
restrictProtectedReceiver(method))))
|
||||
mh = restrictReceiver(method, mh, lookupClass());
|
||||
restrictProtectedReceiver(method)))) {
|
||||
mh = restrictReceiver(method, dmh, lookupClass());
|
||||
}
|
||||
mh = maybeBindCaller(method, mh, callerClass);
|
||||
mh = mh.setVarargs(method);
|
||||
return mh;
|
||||
}
|
||||
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
|
||||
@ -1687,12 +1694,12 @@ return mh1;
|
||||
// Optionally check with the security manager; this isn't needed for unreflect* calls.
|
||||
if (checkSecurity)
|
||||
checkSecurityManager(refc, field);
|
||||
MethodHandle mh = DirectMethodHandle.make(refc, field);
|
||||
DirectMethodHandle dmh = DirectMethodHandle.make(refc, field);
|
||||
boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) &&
|
||||
restrictProtectedReceiver(field));
|
||||
if (doRestrict)
|
||||
mh = restrictReceiver(field, mh, lookupClass());
|
||||
return mh;
|
||||
return restrictReceiver(field, dmh, lookupClass());
|
||||
return dmh;
|
||||
}
|
||||
/** Check access and get the requested constructor. */
|
||||
private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
|
||||
@ -1879,7 +1886,8 @@ return invoker;
|
||||
static public
|
||||
MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
|
||||
if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
|
||||
throw new IllegalArgumentException("bad argument count "+leadingArgCount);
|
||||
throw newIllegalArgumentException("bad argument count", leadingArgCount);
|
||||
type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount);
|
||||
return type.invokers().spreadInvoker(leadingArgCount);
|
||||
}
|
||||
|
||||
@ -1959,12 +1967,12 @@ return invoker;
|
||||
*/
|
||||
static public
|
||||
MethodHandle invoker(MethodType type) {
|
||||
return type.invokers().generalInvoker();
|
||||
return type.invokers().genericInvoker();
|
||||
}
|
||||
|
||||
static /*non-public*/
|
||||
MethodHandle basicInvoker(MethodType type) {
|
||||
return type.form().basicInvoker();
|
||||
return type.invokers().basicInvoker();
|
||||
}
|
||||
|
||||
/// method handle modification (creation from other method handles)
|
||||
@ -2015,10 +2023,13 @@ return invoker;
|
||||
*/
|
||||
public static
|
||||
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
|
||||
if (!target.type().isCastableTo(newType)) {
|
||||
throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
|
||||
MethodType oldType = target.type();
|
||||
// use the asTypeCache when possible:
|
||||
if (oldType == newType) return target;
|
||||
if (oldType.explicitCastEquivalentToAsType(newType)) {
|
||||
return target.asType(newType);
|
||||
}
|
||||
return MethodHandleImpl.makePairwiseConvert(target, newType, 2);
|
||||
return MethodHandleImpl.makePairwiseConvert(target, newType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2082,12 +2093,165 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
*/
|
||||
public static
|
||||
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
|
||||
reorder = reorder.clone();
|
||||
checkReorder(reorder, newType, target.type());
|
||||
return target.permuteArguments(newType, reorder);
|
||||
reorder = reorder.clone(); // get a private copy
|
||||
MethodType oldType = target.type();
|
||||
permuteArgumentChecks(reorder, newType, oldType);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
// first detect dropped arguments and handle them separately
|
||||
int[] originalReorder = reorder;
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm form = result.form;
|
||||
int newArity = newType.parameterCount();
|
||||
// Normalize the reordering into a real permutation,
|
||||
// by removing duplicates and adding dropped elements.
|
||||
// This somewhat improves lambda form caching, as well
|
||||
// as simplifying the transform by breaking it up into steps.
|
||||
for (int ddIdx; (ddIdx = findFirstDupOrDrop(reorder, newArity)) != 0; ) {
|
||||
if (ddIdx > 0) {
|
||||
// We found a duplicated entry at reorder[ddIdx].
|
||||
// Example: (x,y,z)->asList(x,y,z)
|
||||
// permuted by [1*,0,1] => (a0,a1)=>asList(a1,a0,a1)
|
||||
// permuted by [0,1,0*] => (a0,a1)=>asList(a0,a1,a0)
|
||||
// The starred element corresponds to the argument
|
||||
// deleted by the dupArgumentForm transform.
|
||||
int srcPos = ddIdx, dstPos = srcPos, dupVal = reorder[srcPos];
|
||||
boolean killFirst = false;
|
||||
for (int val; (val = reorder[--dstPos]) != dupVal; ) {
|
||||
// Set killFirst if the dup is larger than an intervening position.
|
||||
// This will remove at least one inversion from the permutation.
|
||||
if (dupVal > val) killFirst = true;
|
||||
}
|
||||
if (!killFirst) {
|
||||
srcPos = dstPos;
|
||||
dstPos = ddIdx;
|
||||
}
|
||||
form = form.editor().dupArgumentForm(1 + srcPos, 1 + dstPos);
|
||||
assert (reorder[srcPos] == reorder[dstPos]);
|
||||
oldType = oldType.dropParameterTypes(dstPos, dstPos + 1);
|
||||
// contract the reordering by removing the element at dstPos
|
||||
int tailPos = dstPos + 1;
|
||||
System.arraycopy(reorder, tailPos, reorder, dstPos, reorder.length - tailPos);
|
||||
reorder = Arrays.copyOf(reorder, reorder.length - 1);
|
||||
} else {
|
||||
int dropVal = ~ddIdx, insPos = 0;
|
||||
while (insPos < reorder.length && reorder[insPos] < dropVal) {
|
||||
// Find first element of reorder larger than dropVal.
|
||||
// This is where we will insert the dropVal.
|
||||
insPos += 1;
|
||||
}
|
||||
Class<?> ptype = newType.parameterType(dropVal);
|
||||
form = form.editor().addArgumentForm(1 + insPos, BasicType.basicType(ptype));
|
||||
oldType = oldType.insertParameterTypes(insPos, ptype);
|
||||
// expand the reordering by inserting an element at insPos
|
||||
int tailPos = insPos + 1;
|
||||
reorder = Arrays.copyOf(reorder, reorder.length + 1);
|
||||
System.arraycopy(reorder, insPos, reorder, tailPos, reorder.length - tailPos);
|
||||
reorder[insPos] = dropVal;
|
||||
}
|
||||
assert (permuteArgumentChecks(reorder, newType, oldType));
|
||||
}
|
||||
assert (reorder.length == newArity); // a perfect permutation
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
form = form.editor().permuteArgumentsForm(1, reorder);
|
||||
if (newType == result.type() && form == result.internalForm())
|
||||
return result;
|
||||
return result.copyWith(newType, form);
|
||||
} else {
|
||||
// first detect dropped arguments and handle them separately
|
||||
MethodHandle originalTarget = target;
|
||||
int newArity = newType.parameterCount();
|
||||
for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) {
|
||||
// dropIdx is missing from reorder; add it in at the end
|
||||
int oldArity = reorder.length;
|
||||
target = dropArguments(target, oldArity, newType.parameterType(dropIdx));
|
||||
reorder = Arrays.copyOf(reorder, oldArity+1);
|
||||
reorder[oldArity] = dropIdx;
|
||||
}
|
||||
assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type()));
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList()));
|
||||
return result.copyWith(newType, form);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
|
||||
/** Return the first value in [0..newArity-1] that is not present in reorder. */
|
||||
private static int findFirstDrop(int[] reorder, int newArity) {
|
||||
final int BIT_LIMIT = 63; // max number of bits in bit mask
|
||||
if (newArity < BIT_LIMIT) {
|
||||
long mask = 0;
|
||||
for (int arg : reorder) {
|
||||
assert(arg < newArity);
|
||||
mask |= (1 << arg);
|
||||
}
|
||||
if (mask == (1 << newArity) - 1) {
|
||||
assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity);
|
||||
return -1;
|
||||
}
|
||||
// find first zero
|
||||
long zeroBit = Long.lowestOneBit(~mask);
|
||||
int zeroPos = Long.numberOfTrailingZeros(zeroBit);
|
||||
assert(zeroPos < newArity);
|
||||
return zeroPos;
|
||||
}
|
||||
BitSet mask = new BitSet(newArity);
|
||||
for (int arg : reorder) {
|
||||
assert(arg < newArity);
|
||||
mask.set(arg);
|
||||
}
|
||||
int zeroPos = mask.nextClearBit(0);
|
||||
if (zeroPos == newArity)
|
||||
return -1;
|
||||
return zeroPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an indication of any duplicate or omission in reorder.
|
||||
* If the reorder contains a duplicate entry, return the index of the second occurrence.
|
||||
* Otherwise, return ~(n), for the first n in [0..newArity-1] that is not present in reorder.
|
||||
* Otherwise, return zero.
|
||||
* If an element not in [0..newArity-1] is encountered, return reorder.length.
|
||||
*/
|
||||
private static int findFirstDupOrDrop(int[] reorder, int newArity) {
|
||||
final int BIT_LIMIT = 63; // max number of bits in bit mask
|
||||
if (newArity < BIT_LIMIT) {
|
||||
long mask = 0;
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
int arg = reorder[i];
|
||||
if (arg >= newArity) return reorder.length;
|
||||
int bit = 1 << arg;
|
||||
if ((mask & bit) != 0)
|
||||
return i; // >0 indicates a dup
|
||||
mask |= bit;
|
||||
}
|
||||
if (mask == (1 << newArity) - 1) {
|
||||
assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity);
|
||||
return 0;
|
||||
}
|
||||
// find first zero
|
||||
long zeroBit = Long.lowestOneBit(~mask);
|
||||
int zeroPos = Long.numberOfTrailingZeros(zeroBit);
|
||||
assert(zeroPos < newArity);
|
||||
return ~zeroPos;
|
||||
} else {
|
||||
// same algorithm, different bit set
|
||||
BitSet mask = new BitSet(newArity);
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
int arg = reorder[i];
|
||||
if (arg >= newArity) return reorder.length;
|
||||
if (mask.get(arg))
|
||||
return i; // >0 indicates a dup
|
||||
mask.set(arg);
|
||||
}
|
||||
int zeroPos = mask.nextClearBit(0);
|
||||
if (zeroPos == newArity) {
|
||||
return 0;
|
||||
}
|
||||
return ~zeroPos;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
|
||||
if (newType.returnType() != oldType.returnType())
|
||||
throw newIllegalArgumentException("return types do not match",
|
||||
oldType, newType);
|
||||
@ -2105,7 +2269,7 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
throw newIllegalArgumentException("parameter types do not match after reorder",
|
||||
oldType, newType);
|
||||
}
|
||||
if (!bad) return;
|
||||
if (!bad) return true;
|
||||
}
|
||||
throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
|
||||
}
|
||||
@ -2131,9 +2295,14 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
if (type == void.class)
|
||||
throw newIllegalArgumentException("void type");
|
||||
Wrapper w = Wrapper.forPrimitiveType(type);
|
||||
return insertArguments(identity(type), 0, w.convert(value, type));
|
||||
value = w.convert(value, type);
|
||||
if (w.zero().equals(value))
|
||||
return zero(w, type);
|
||||
return insertArguments(identity(type), 0, value);
|
||||
} else {
|
||||
return identity(type).bindTo(type.cast(value));
|
||||
if (value == null)
|
||||
return zero(Wrapper.OBJECT, type);
|
||||
return identity(type).bindTo(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2146,14 +2315,48 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
*/
|
||||
public static
|
||||
MethodHandle identity(Class<?> type) {
|
||||
if (type == void.class)
|
||||
throw newIllegalArgumentException("void type");
|
||||
else if (type == Object.class)
|
||||
return ValueConversions.identity();
|
||||
else if (type.isPrimitive())
|
||||
return ValueConversions.identity(Wrapper.forPrimitiveType(type));
|
||||
else
|
||||
return MethodHandleImpl.makeReferenceIdentity(type);
|
||||
Wrapper btw = (type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.OBJECT);
|
||||
int pos = btw.ordinal();
|
||||
MethodHandle ident = IDENTITY_MHS[pos];
|
||||
if (ident == null) {
|
||||
ident = setCachedMethodHandle(IDENTITY_MHS, pos, makeIdentity(btw.primitiveType()));
|
||||
}
|
||||
if (ident.type().returnType() == type)
|
||||
return ident;
|
||||
// something like identity(Foo.class); do not bother to intern these
|
||||
assert(btw == Wrapper.OBJECT);
|
||||
return makeIdentity(type);
|
||||
}
|
||||
private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length];
|
||||
private static MethodHandle makeIdentity(Class<?> ptype) {
|
||||
MethodType mtype = MethodType.methodType(ptype, ptype);
|
||||
LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype));
|
||||
return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY);
|
||||
}
|
||||
|
||||
private static MethodHandle zero(Wrapper btw, Class<?> rtype) {
|
||||
int pos = btw.ordinal();
|
||||
MethodHandle zero = ZERO_MHS[pos];
|
||||
if (zero == null) {
|
||||
zero = setCachedMethodHandle(ZERO_MHS, pos, makeZero(btw.primitiveType()));
|
||||
}
|
||||
if (zero.type().returnType() == rtype)
|
||||
return zero;
|
||||
assert(btw == Wrapper.OBJECT);
|
||||
return makeZero(rtype);
|
||||
}
|
||||
private static final MethodHandle[] ZERO_MHS = new MethodHandle[Wrapper.values().length];
|
||||
private static MethodHandle makeZero(Class<?> rtype) {
|
||||
MethodType mtype = MethodType.methodType(rtype);
|
||||
LambdaForm lform = LambdaForm.zeroForm(BasicType.basicType(rtype));
|
||||
return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.ZERO);
|
||||
}
|
||||
|
||||
synchronized private static MethodHandle setCachedMethodHandle(MethodHandle[] cache, int pos, MethodHandle value) {
|
||||
// Simulate a CAS, to avoid racy duplication of results.
|
||||
MethodHandle prev = cache[pos];
|
||||
if (prev != null) return prev;
|
||||
return cache[pos] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2189,6 +2392,37 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
public static
|
||||
MethodHandle insertArguments(MethodHandle target, int pos, Object... values) {
|
||||
int insCount = values.length;
|
||||
Class<?>[] ptypes = insertArgumentsChecks(target, insCount, pos);
|
||||
if (insCount == 0) return target;
|
||||
BoundMethodHandle result = target.rebind();
|
||||
for (int i = 0; i < insCount; i++) {
|
||||
Object value = values[i];
|
||||
Class<?> ptype = ptypes[pos+i];
|
||||
if (ptype.isPrimitive()) {
|
||||
result = insertArgumentPrimitive(result, pos, ptype, value);
|
||||
} else {
|
||||
value = ptype.cast(value); // throw CCE if needed
|
||||
result = result.bindArgumentL(pos, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static BoundMethodHandle insertArgumentPrimitive(BoundMethodHandle result, int pos,
|
||||
Class<?> ptype, Object value) {
|
||||
Wrapper w = Wrapper.forPrimitiveType(ptype);
|
||||
// perform unboxing and/or primitive conversion
|
||||
value = w.convert(value, ptype);
|
||||
switch (w) {
|
||||
case INT: return result.bindArgumentI(pos, (int)value);
|
||||
case LONG: return result.bindArgumentJ(pos, (long)value);
|
||||
case FLOAT: return result.bindArgumentF(pos, (float)value);
|
||||
case DOUBLE: return result.bindArgumentD(pos, (double)value);
|
||||
default: return result.bindArgumentI(pos, ValueConversions.widenSubword(value));
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?>[] insertArgumentsChecks(MethodHandle target, int insCount, int pos) throws RuntimeException {
|
||||
MethodType oldType = target.type();
|
||||
int outargs = oldType.parameterCount();
|
||||
int inargs = outargs - insCount;
|
||||
@ -2196,31 +2430,7 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
throw newIllegalArgumentException("too many values to insert");
|
||||
if (pos < 0 || pos > inargs)
|
||||
throw newIllegalArgumentException("no argument type to append");
|
||||
MethodHandle result = target;
|
||||
for (int i = 0; i < insCount; i++) {
|
||||
Object value = values[i];
|
||||
Class<?> ptype = oldType.parameterType(pos+i);
|
||||
if (ptype.isPrimitive()) {
|
||||
BasicType btype = I_TYPE;
|
||||
Wrapper w = Wrapper.forPrimitiveType(ptype);
|
||||
switch (w) {
|
||||
case LONG: btype = J_TYPE; break;
|
||||
case FLOAT: btype = F_TYPE; break;
|
||||
case DOUBLE: btype = D_TYPE; break;
|
||||
}
|
||||
// perform unboxing and/or primitive conversion
|
||||
value = w.convert(value, ptype);
|
||||
result = result.bindArgument(pos, btype, value);
|
||||
continue;
|
||||
}
|
||||
value = ptype.cast(value); // throw CCE if needed
|
||||
if (pos == 0) {
|
||||
result = result.bindReceiver(value);
|
||||
} else {
|
||||
result = result.bindArgument(pos, L_TYPE, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return oldType.ptypes();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2268,18 +2478,33 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
|
||||
public static
|
||||
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
|
||||
MethodType oldType = target.type(); // get NPE
|
||||
int dropped = dropArgumentChecks(oldType, pos, valueTypes);
|
||||
if (dropped == 0) return target;
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm lform = result.form;
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
int insertFormArg = 1 + pos;
|
||||
for (Class<?> ptype : valueTypes) {
|
||||
lform = lform.editor().addArgumentForm(insertFormArg++, BasicType.basicType(ptype));
|
||||
}
|
||||
} else {
|
||||
lform = lform.addArguments(pos, valueTypes);
|
||||
}
|
||||
MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
|
||||
result = result.copyWith(newType, lform);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int dropArgumentChecks(MethodType oldType, int pos, List<Class<?>> valueTypes) {
|
||||
int dropped = valueTypes.size();
|
||||
MethodType.checkSlotCount(dropped);
|
||||
if (dropped == 0) return target;
|
||||
int outargs = oldType.parameterCount();
|
||||
int inargs = outargs + dropped;
|
||||
if (pos < 0 || pos >= inargs)
|
||||
throw newIllegalArgumentException("no argument type to remove");
|
||||
ArrayList<Class<?>> ptypes = new ArrayList<>(oldType.parameterList());
|
||||
ptypes.addAll(pos, valueTypes);
|
||||
if (ptypes.size() != inargs) throw newIllegalArgumentException("valueTypes");
|
||||
MethodType newType = MethodType.methodType(oldType.returnType(), ptypes);
|
||||
return target.dropArguments(newType, pos, dropped);
|
||||
if (pos < 0 || pos > outargs)
|
||||
throw newIllegalArgumentException("no argument type to remove"
|
||||
+ Arrays.asList(oldType, pos, valueTypes, inargs, outargs)
|
||||
);
|
||||
return dropped;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2401,32 +2626,47 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
||||
*/
|
||||
public static
|
||||
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
|
||||
MethodType targetType = target.type();
|
||||
filterArgumentsCheckArity(target, pos, filters);
|
||||
MethodHandle adapter = target;
|
||||
MethodType adapterType = null;
|
||||
assert((adapterType = targetType) != null);
|
||||
int maxPos = targetType.parameterCount();
|
||||
if (pos + filters.length > maxPos)
|
||||
throw newIllegalArgumentException("too many filters");
|
||||
int curPos = pos-1; // pre-incremented
|
||||
for (MethodHandle filter : filters) {
|
||||
curPos += 1;
|
||||
if (filter == null) continue; // ignore null elements of filters
|
||||
adapter = filterArgument(adapter, curPos, filter);
|
||||
assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
|
||||
}
|
||||
assert(adapterType.equals(adapter.type()));
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/*non-public*/ static
|
||||
MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
|
||||
filterArgumentChecks(target, pos, filter);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
BoundMethodHandle result = target.rebind();
|
||||
Class<?> newParamType = filterType.parameterType(0);
|
||||
LambdaForm lform = result.editor().filterArgumentForm(1 + pos, BasicType.basicType(newParamType));
|
||||
MethodType newType = targetType.changeParameterType(pos, newParamType);
|
||||
result = result.copyWithExtendL(newType, lform, filter);
|
||||
return result;
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) {
|
||||
MethodType targetType = target.type();
|
||||
int maxPos = targetType.parameterCount();
|
||||
if (pos + filters.length > maxPos)
|
||||
throw newIllegalArgumentException("too many filters");
|
||||
}
|
||||
|
||||
private static void filterArgumentChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
if (filterType.parameterCount() != 1
|
||||
|| filterType.returnType() != targetType.parameterType(pos))
|
||||
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2537,12 +2777,36 @@ assertEquals("[top, [[up, down, strange], charm], bottom]",
|
||||
*/
|
||||
public static
|
||||
MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) {
|
||||
MethodType newType = collectArgumentsChecks(target, pos, filter);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
MethodType collectorType = filter.type();
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm lform;
|
||||
if (collectorType.returnType().isArray() && filter.intrinsicName() == Intrinsic.NEW_ARRAY) {
|
||||
lform = result.editor().collectArgumentArrayForm(1 + pos, filter);
|
||||
if (lform != null) {
|
||||
return result.copyWith(newType, lform);
|
||||
}
|
||||
}
|
||||
lform = result.editor().collectArgumentsForm(1 + pos, collectorType.basicType());
|
||||
return result.copyWithExtendL(newType, lform, filter);
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodType collectArgumentsChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
if (filterType.returnType() != void.class &&
|
||||
filterType.returnType() != targetType.parameterType(pos))
|
||||
Class<?> rtype = filterType.returnType();
|
||||
List<Class<?>> filterArgs = filterType.parameterList();
|
||||
if (rtype == void.class) {
|
||||
return targetType.insertParameterTypes(pos, filterArgs);
|
||||
}
|
||||
if (rtype != targetType.parameterType(pos)) {
|
||||
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
return targetType.dropParameterTypes(pos, pos+1).insertParameterTypes(pos, filterArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2606,15 +2870,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
filterReturnValueChecks(targetType, filterType);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
BoundMethodHandle result = target.rebind();
|
||||
BasicType rtype = BasicType.basicType(filterType.returnType());
|
||||
LambdaForm lform = result.editor().filterReturnForm(rtype, false);
|
||||
MethodType newType = targetType.changeReturnType(filterType.returnType());
|
||||
result = result.copyWithExtendL(newType, lform, filter);
|
||||
return result;
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException {
|
||||
Class<?> rtype = targetType.returnType();
|
||||
int filterValues = filterType.parameterCount();
|
||||
if (filterValues == 0
|
||||
? (rtype != void.class)
|
||||
: (rtype != filterType.parameterType(0)))
|
||||
throw newIllegalArgumentException("target and filter types do not match", target, filter);
|
||||
// result = fold( lambda(retval, arg...) { filter(retval) },
|
||||
// lambda( arg...) { target(arg...) } )
|
||||
return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
|
||||
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2695,24 +2970,40 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
*/
|
||||
public static
|
||||
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
|
||||
int pos = 0;
|
||||
int foldPos = 0;
|
||||
MethodType targetType = target.type();
|
||||
MethodType combinerType = combiner.type();
|
||||
int foldPos = pos;
|
||||
int foldArgs = combinerType.parameterCount();
|
||||
int foldVals = combinerType.returnType() == void.class ? 0 : 1;
|
||||
Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
BoundMethodHandle result = target.rebind();
|
||||
boolean dropResult = (rtype == void.class);
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType());
|
||||
MethodType newType = targetType;
|
||||
if (!dropResult)
|
||||
newType = newType.dropParameterTypes(foldPos, foldPos + 1);
|
||||
result = result.copyWithExtendL(newType, lform, combiner);
|
||||
return result;
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
|
||||
int foldArgs = combinerType.parameterCount();
|
||||
Class<?> rtype = combinerType.returnType();
|
||||
int foldVals = rtype == void.class ? 0 : 1;
|
||||
int afterInsertPos = foldPos + foldVals;
|
||||
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
|
||||
if (ok && !(combinerType.parameterList()
|
||||
.equals(targetType.parameterList().subList(afterInsertPos,
|
||||
afterInsertPos + foldArgs))))
|
||||
ok = false;
|
||||
if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
|
||||
if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
|
||||
ok = false;
|
||||
if (!ok)
|
||||
throw misMatchedTypes("target and combiner types", targetType, combinerType);
|
||||
MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
|
||||
return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
|
||||
return rtype;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -467,6 +467,75 @@ class MethodType implements java.io.Serializable {
|
||||
return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
|
||||
}
|
||||
|
||||
/** Replace the last arrayLength parameter types with the component type of arrayType.
|
||||
* @param arrayType any array type
|
||||
* @param arrayLength the number of parameter types to change
|
||||
* @return the resulting type
|
||||
*/
|
||||
/*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
|
||||
assert(parameterCount() >= arrayLength);
|
||||
int spreadPos = ptypes.length - arrayLength;
|
||||
if (arrayLength == 0) return this; // nothing to change
|
||||
if (arrayType == Object[].class) {
|
||||
if (isGeneric()) return this; // nothing to change
|
||||
if (spreadPos == 0) {
|
||||
// no leading arguments to preserve; go generic
|
||||
MethodType res = genericMethodType(arrayLength);
|
||||
if (rtype != Object.class) {
|
||||
res = res.changeReturnType(rtype);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
assert(elemType != null);
|
||||
for (int i = spreadPos; i < ptypes.length; i++) {
|
||||
if (ptypes[i] != elemType) {
|
||||
Class<?>[] fixedPtypes = ptypes.clone();
|
||||
Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
|
||||
return methodType(rtype, fixedPtypes);
|
||||
}
|
||||
}
|
||||
return this; // arguments check out; no change
|
||||
}
|
||||
|
||||
/** Return the leading parameter type, which must exist and be a reference.
|
||||
* @return the leading parameter type, after error checks
|
||||
*/
|
||||
/*non-public*/ Class<?> leadingReferenceParameter() {
|
||||
Class<?> ptype;
|
||||
if (ptypes.length == 0 ||
|
||||
(ptype = ptypes[0]).isPrimitive())
|
||||
throw newIllegalArgumentException("no leading reference parameter");
|
||||
return ptype;
|
||||
}
|
||||
|
||||
/** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
|
||||
* @param arrayType any array type
|
||||
* @param arrayLength the number of parameter types to insert
|
||||
* @return the resulting type
|
||||
*/
|
||||
/*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
|
||||
assert(parameterCount() >= 1);
|
||||
assert(lastParameterType().isAssignableFrom(arrayType));
|
||||
MethodType res;
|
||||
if (arrayType == Object[].class) {
|
||||
res = genericMethodType(arrayLength);
|
||||
if (rtype != Object.class) {
|
||||
res = res.changeReturnType(rtype);
|
||||
}
|
||||
} else {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
assert(elemType != null);
|
||||
res = methodType(rtype, Collections.nCopies(arrayLength, elemType));
|
||||
}
|
||||
if (ptypes.length == 1) {
|
||||
return res;
|
||||
} else {
|
||||
return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a method type with some parameter types omitted.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
@ -574,6 +643,10 @@ class MethodType implements java.io.Serializable {
|
||||
return genericMethodType(parameterCount());
|
||||
}
|
||||
|
||||
/*non-public*/ boolean isGeneric() {
|
||||
return this == erase() && !hasPrimitives();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all primitive types to their corresponding wrapper types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
@ -726,44 +799,137 @@ class MethodType implements java.io.Serializable {
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
|
||||
/** True if the old return type can always be viewed (w/o casting) under new return type,
|
||||
* and the new parameters can be viewed (w/o casting) under the old parameter types.
|
||||
*/
|
||||
/*non-public*/
|
||||
boolean isViewableAs(MethodType newType) {
|
||||
if (!VerifyType.isNullConversion(returnType(), newType.returnType()))
|
||||
boolean isViewableAs(MethodType newType, boolean keepInterfaces) {
|
||||
if (!VerifyType.isNullConversion(returnType(), newType.returnType(), keepInterfaces))
|
||||
return false;
|
||||
return parametersAreViewableAs(newType, keepInterfaces);
|
||||
}
|
||||
/** True if the new parameters can be viewed (w/o casting) under the old parameter types. */
|
||||
/*non-public*/
|
||||
boolean parametersAreViewableAs(MethodType newType, boolean keepInterfaces) {
|
||||
if (form == newType.form && form.erasedType == this)
|
||||
return true; // my reference parameters are all Object
|
||||
if (ptypes == newType.ptypes)
|
||||
return true;
|
||||
int argc = parameterCount();
|
||||
if (argc != newType.parameterCount())
|
||||
return false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i)))
|
||||
if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i), keepInterfaces))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*non-public*/
|
||||
boolean isCastableTo(MethodType newType) {
|
||||
int argc = parameterCount();
|
||||
if (argc != newType.parameterCount())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/*non-public*/
|
||||
boolean isConvertibleTo(MethodType newType) {
|
||||
MethodTypeForm oldForm = this.form();
|
||||
MethodTypeForm newForm = newType.form();
|
||||
if (oldForm == newForm)
|
||||
// same parameter count, same primitive/object mix
|
||||
return true;
|
||||
if (!canConvert(returnType(), newType.returnType()))
|
||||
return false;
|
||||
int argc = parameterCount();
|
||||
if (argc != newType.parameterCount())
|
||||
Class<?>[] srcTypes = newType.ptypes;
|
||||
Class<?>[] dstTypes = ptypes;
|
||||
if (srcTypes == dstTypes)
|
||||
return true;
|
||||
int argc;
|
||||
if ((argc = srcTypes.length) != dstTypes.length)
|
||||
return false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!canConvert(newType.parameterType(i), parameterType(i)))
|
||||
if (argc <= 1) {
|
||||
if (argc == 1 && !canConvert(srcTypes[0], dstTypes[0]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) ||
|
||||
(newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) {
|
||||
// Somewhat complicated test to avoid a loop of 2 or more trips.
|
||||
// If either type has only Object parameters, we know we can convert.
|
||||
assert(canConvertParameters(srcTypes, dstTypes));
|
||||
return true;
|
||||
}
|
||||
return canConvertParameters(srcTypes, dstTypes);
|
||||
}
|
||||
|
||||
/** Returns true if MHs.explicitCastArguments produces the same result as MH.asType.
|
||||
* If the type conversion is impossible for either, the result should be false.
|
||||
*/
|
||||
/*non-public*/
|
||||
boolean explicitCastEquivalentToAsType(MethodType newType) {
|
||||
if (this == newType) return true;
|
||||
if (!explicitCastEquivalentToAsType(rtype, newType.rtype)) {
|
||||
return false;
|
||||
}
|
||||
Class<?>[] srcTypes = newType.ptypes;
|
||||
Class<?>[] dstTypes = ptypes;
|
||||
if (dstTypes == srcTypes) {
|
||||
return true;
|
||||
}
|
||||
if (dstTypes.length != srcTypes.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < dstTypes.length; i++) {
|
||||
if (!explicitCastEquivalentToAsType(srcTypes[i], dstTypes[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Reports true if the src can be converted to the dst, by both asType and MHs.eCE,
|
||||
* and with the same effect.
|
||||
* MHs.eCA has the following "upgrades" to MH.asType:
|
||||
* 1. interfaces are unchecked (that is, treated as if aliased to Object)
|
||||
* Therefore, {@code Object->CharSequence} is possible in both cases but has different semantics
|
||||
* 2. the full matrix of primitive-to-primitive conversions is supported
|
||||
* Narrowing like {@code long->byte} and basic-typing like {@code boolean->int}
|
||||
* are not supported by asType, but anything supported by asType is equivalent
|
||||
* with MHs.eCE.
|
||||
* 3a. unboxing conversions can be followed by the full matrix of primitive conversions
|
||||
* 3b. unboxing of null is permitted (creates a zero primitive value)
|
||||
* Most unboxing conversions, like {@code Object->int}, has potentially
|
||||
* different behaviors for asType vs. MHs.eCE, because the dynamic value
|
||||
* might be a wrapper of a type that requires narrowing, like {@code (Object)1L->byte}.
|
||||
* The equivalence is only certain if the static src type is a wrapper,
|
||||
* and the conversion will be a widening one.
|
||||
* Other than interfaces, reference-to-reference conversions are the same.
|
||||
* Boxing primitives to references is the same for both operators.
|
||||
*/
|
||||
private static boolean explicitCastEquivalentToAsType(Class<?> src, Class<?> dst) {
|
||||
if (src == dst || dst == Object.class || dst == void.class) return true;
|
||||
if (src.isPrimitive()) {
|
||||
// Could be a prim/prim conversion, where casting is a strict superset.
|
||||
// Or a boxing conversion, which is always to an exact wrapper class.
|
||||
return canConvert(src, dst);
|
||||
} else if (dst.isPrimitive()) {
|
||||
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||
// Watch out: If src is Number or Object, we could get dynamic narrowing conversion.
|
||||
// The conversion is known to be widening only if the wrapper type is statically visible.
|
||||
return (Wrapper.isWrapperType(src) &&
|
||||
dw.isConvertibleFrom(Wrapper.forWrapperType(src)));
|
||||
} else {
|
||||
// R->R always works, but we have to avoid a check-cast to an interface.
|
||||
return !dst.isInterface() || dst.isAssignableFrom(src);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canConvertParameters(Class<?>[] srcTypes, Class<?>[] dstTypes) {
|
||||
for (int i = 0; i < srcTypes.length; i++) {
|
||||
if (!canConvert(srcTypes[i], dstTypes[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
static boolean canConvert(Class<?> src, Class<?> dst) {
|
||||
// short-circuit a few cases:
|
||||
if (src == dst || dst == Object.class) return true;
|
||||
if (src == dst || src == Object.class || dst == Object.class) return true;
|
||||
// the remainder of this logic is documented in MethodHandle.asType
|
||||
if (src.isPrimitive()) {
|
||||
// can force void to an explicit null, a la reflect.Method.invoke
|
||||
@ -905,7 +1071,7 @@ class MethodType implements java.io.Serializable {
|
||||
if (!descriptor.startsWith("(") || // also generates NPE if needed
|
||||
descriptor.indexOf(')') < 0 ||
|
||||
descriptor.indexOf('.') >= 0)
|
||||
throw new IllegalArgumentException("not a method descriptor: "+descriptor);
|
||||
throw newIllegalArgumentException("not a method descriptor: "+descriptor);
|
||||
List<Class<?>> types = BytecodeDescriptor.parseMethod(descriptor, loader);
|
||||
Class<?> rtype = types.remove(types.size() - 1);
|
||||
checkSlotCount(types.size());
|
||||
|
@ -47,15 +47,17 @@ final class MethodTypeForm {
|
||||
final int[] argToSlotTable, slotToArgTable;
|
||||
final long argCounts; // packed slot & value counts
|
||||
final long primCounts; // packed prim & double counts
|
||||
final int vmslots; // total number of parameter slots
|
||||
final MethodType erasedType; // the canonical erasure
|
||||
final MethodType basicType; // the canonical erasure, with primitives simplified
|
||||
|
||||
// Cached adapter information:
|
||||
@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
|
||||
@Stable final MethodHandle[] methodHandles;
|
||||
// Indexes into methodHandles:
|
||||
static final int
|
||||
MH_BASIC_INV = 0, // cached instance of MH.invokeBasic
|
||||
MH_NF_INV = 1, // cached helper for LF.NamedFunction
|
||||
MH_UNINIT_CS = 2, // uninitialized call site
|
||||
MH_LIMIT = 3;
|
||||
|
||||
// Cached lambda form information, for basic types only:
|
||||
final @Stable LambdaForm[] lambdaForms;
|
||||
@ -68,26 +70,55 @@ final class MethodTypeForm {
|
||||
LF_INVINTERFACE = 4,
|
||||
LF_INVSTATIC_INIT = 5, // DMH invokeStatic with <clinit> barrier
|
||||
LF_INTERPRET = 6, // LF interpreter
|
||||
LF_COUNTER = 7, // CMH wrapper
|
||||
LF_REINVOKE = 8, // other wrapper
|
||||
LF_EX_LINKER = 9, // invokeExact_MT
|
||||
LF_EX_INVOKER = 10, // invokeExact MH
|
||||
LF_GEN_LINKER = 11,
|
||||
LF_GEN_INVOKER = 12,
|
||||
LF_REBIND = 7, // BoundMethodHandle
|
||||
LF_DELEGATE = 8, // DelegatingMethodHandle
|
||||
LF_EX_LINKER = 9, // invokeExact_MT (for invokehandle)
|
||||
LF_EX_INVOKER = 10, // MHs.invokeExact
|
||||
LF_GEN_LINKER = 11, // generic invoke_MT (for invokehandle)
|
||||
LF_GEN_INVOKER = 12, // generic MHs.invoke
|
||||
LF_CS_LINKER = 13, // linkToCallSite_CS
|
||||
LF_MH_LINKER = 14, // linkToCallSite_MH
|
||||
LF_GWC = 15,
|
||||
LF_LIMIT = 16;
|
||||
LF_GWC = 15, // guardWithCatch (catchException)
|
||||
LF_GWT = 16, // guardWithTest
|
||||
LF_LIMIT = 17;
|
||||
|
||||
/** Return the type corresponding uniquely (1-1) to this MT-form.
|
||||
* It might have any primitive returns or arguments, but will have no references except Object.
|
||||
*/
|
||||
public MethodType erasedType() {
|
||||
return erasedType;
|
||||
}
|
||||
|
||||
/** Return the basic type derived from the erased type of this MT-form.
|
||||
* A basic type is erased (all references Object) and also has all primitive
|
||||
* types (except int, long, float, double, void) normalized to int.
|
||||
* Such basic types correspond to low-level JVM calling sequences.
|
||||
*/
|
||||
public MethodType basicType() {
|
||||
return basicType;
|
||||
}
|
||||
|
||||
private boolean assertIsBasicType() {
|
||||
// primitives must be flattened also
|
||||
assert(erasedType == basicType)
|
||||
: "erasedType: " + erasedType + " != basicType: " + basicType;
|
||||
return true;
|
||||
}
|
||||
|
||||
public MethodHandle cachedMethodHandle(int which) {
|
||||
assert(assertIsBasicType());
|
||||
return methodHandles[which];
|
||||
}
|
||||
|
||||
synchronized public MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
|
||||
// Simulate a CAS, to avoid racy duplication of results.
|
||||
MethodHandle prev = methodHandles[which];
|
||||
if (prev != null) return prev;
|
||||
return methodHandles[which] = mh;
|
||||
}
|
||||
|
||||
public LambdaForm cachedLambdaForm(int which) {
|
||||
assert(assertIsBasicType());
|
||||
return lambdaForms[which];
|
||||
}
|
||||
|
||||
@ -98,28 +129,6 @@ final class MethodTypeForm {
|
||||
return lambdaForms[which] = form;
|
||||
}
|
||||
|
||||
public MethodHandle basicInvoker() {
|
||||
assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also
|
||||
MethodHandle invoker = basicInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
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.
|
||||
@ -172,6 +181,16 @@ final class MethodTypeForm {
|
||||
this.basicType = erasedType;
|
||||
} else {
|
||||
this.basicType = MethodType.makeImpl(bt, bpts, true);
|
||||
// fill in rest of data from the basic type:
|
||||
MethodTypeForm that = this.basicType.form();
|
||||
assert(this != that);
|
||||
this.primCounts = that.primCounts;
|
||||
this.argCounts = that.argCounts;
|
||||
this.argToSlotTable = that.argToSlotTable;
|
||||
this.slotToArgTable = that.slotToArgTable;
|
||||
this.methodHandles = null;
|
||||
this.lambdaForms = null;
|
||||
return;
|
||||
}
|
||||
if (lac != 0) {
|
||||
int slot = ptypeCount + lac;
|
||||
@ -187,10 +206,14 @@ final class MethodTypeForm {
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
assert(slot == 0); // filled the table
|
||||
}
|
||||
this.primCounts = pack(lrc, prc, lac, pac);
|
||||
this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
|
||||
if (slotToArgTab == null) {
|
||||
} else if (pac != 0) {
|
||||
// have primitives but no long primitives; share slot counts with generic
|
||||
assert(ptypeCount == pslotCount);
|
||||
MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form();
|
||||
assert(this != that);
|
||||
slotToArgTab = that.slotToArgTable;
|
||||
argToSlotTab = that.argToSlotTable;
|
||||
} else {
|
||||
int slot = ptypeCount; // first arg is deepest in stack
|
||||
slotToArgTab = new int[slot+1];
|
||||
argToSlotTab = new int[1+ptypeCount];
|
||||
@ -201,19 +224,17 @@ final class MethodTypeForm {
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
}
|
||||
this.primCounts = pack(lrc, prc, lac, pac);
|
||||
this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
|
||||
this.argToSlotTable = argToSlotTab;
|
||||
this.slotToArgTable = slotToArgTab;
|
||||
|
||||
if (pslotCount >= 256) throw newIllegalArgumentException("too many arguments");
|
||||
|
||||
// send a few bits down to the JVM:
|
||||
this.vmslots = parameterSlotCount();
|
||||
|
||||
if (basicType == erasedType) {
|
||||
lambdaForms = new LambdaForm[LF_LIMIT];
|
||||
} else {
|
||||
lambdaForms = null; // could be basicType.form().lambdaForms;
|
||||
}
|
||||
// Initialize caches, but only for basic types
|
||||
assert(basicType == erasedType);
|
||||
this.lambdaForms = new LambdaForm[LF_LIMIT];
|
||||
this.methodHandles = new MethodHandle[MH_LIMIT];
|
||||
}
|
||||
|
||||
private static long pack(int a, int b, int c, int d) {
|
||||
@ -300,7 +321,7 @@ final class MethodTypeForm {
|
||||
*/
|
||||
public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
|
||||
Class<?>[] ptypes = mt.ptypes();
|
||||
Class<?>[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs);
|
||||
Class<?>[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs);
|
||||
Class<?> rtype = mt.returnType();
|
||||
Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
|
||||
if (ptc == null && rtc == null) {
|
||||
@ -368,7 +389,7 @@ final class MethodTypeForm {
|
||||
/** Canonicalize each param type in the given array.
|
||||
* Return null if all types are already canonicalized.
|
||||
*/
|
||||
static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
|
||||
static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
|
||||
Class<?>[] cs = null;
|
||||
for (int imax = ts.length, i = 0; i < imax; i++) {
|
||||
Class<?> c = canonicalize(ts[i], how);
|
||||
|
@ -25,38 +25,77 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* A method handle whose behavior is determined only by its LambdaForm.
|
||||
* @author jrose
|
||||
*/
|
||||
final class SimpleMethodHandle extends MethodHandle {
|
||||
final class SimpleMethodHandle extends BoundMethodHandle {
|
||||
private SimpleMethodHandle(MethodType type, LambdaForm form) {
|
||||
super(type, form);
|
||||
}
|
||||
|
||||
/*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) {
|
||||
/*non-public*/ static BoundMethodHandle make(MethodType type, LambdaForm form) {
|
||||
return new SimpleMethodHandle(type, form);
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
|
||||
MethodType type2 = type().dropParameterTypes(pos, pos+1);
|
||||
LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
|
||||
return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
|
||||
/*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
|
||||
|
||||
/*non-public*/ public SpeciesData speciesData() {
|
||||
return SPECIES_DATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
|
||||
LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
|
||||
return new SimpleMethodHandle(srcType, newForm);
|
||||
/*non-public*/ BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return make(mt, lf);
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle permuteArguments(MethodType newType, int[] reorder) {
|
||||
LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList()));
|
||||
return new SimpleMethodHandle(newType, form2);
|
||||
String internalProperties() {
|
||||
return "\n& Class="+getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
/*non-public*/ public int fieldCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
return BoundMethodHandle.bindSingle(mt, lf, narg); // Use known fast path.
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,8 +94,19 @@ public final class Constructor<T> extends Executable {
|
||||
// For sharing of ConstructorAccessors. This branching structure
|
||||
// is currently only two levels deep (i.e., one root Constructor
|
||||
// and potentially many Constructor objects pointing to it.)
|
||||
//
|
||||
// If this branching structure would ever contain cycles, deadlocks can
|
||||
// occur in annotation code.
|
||||
private Constructor<T> root;
|
||||
|
||||
/**
|
||||
* Used by Excecutable for annotation sharing.
|
||||
*/
|
||||
@Override
|
||||
Executable getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-private constructor used by ReflectAccess to enable
|
||||
* instantiation of these objects in Java code from the java.lang
|
||||
@ -132,6 +143,9 @@ public final class Constructor<T> extends Executable {
|
||||
// which implicitly requires that new java.lang.reflect
|
||||
// objects be fabricated for each reflective call on Class
|
||||
// objects.)
|
||||
if (this.root != null)
|
||||
throw new IllegalArgumentException("Can not copy a non-root Constructor");
|
||||
|
||||
Constructor<T> res = new Constructor<>(clazz,
|
||||
parameterTypes,
|
||||
exceptionTypes, modifiers, slot,
|
||||
|
@ -52,6 +52,11 @@ public abstract class Executable extends AccessibleObject
|
||||
*/
|
||||
abstract byte[] getAnnotationBytes();
|
||||
|
||||
/**
|
||||
* Accessor method to allow code sharing
|
||||
*/
|
||||
abstract Executable getRoot();
|
||||
|
||||
/**
|
||||
* Does the Executable have generic information.
|
||||
*/
|
||||
@ -540,11 +545,16 @@ public abstract class Executable extends AccessibleObject
|
||||
|
||||
private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
|
||||
if (declaredAnnotations == null) {
|
||||
declaredAnnotations = AnnotationParser.parseAnnotations(
|
||||
getAnnotationBytes(),
|
||||
sun.misc.SharedSecrets.getJavaLangAccess().
|
||||
getConstantPool(getDeclaringClass()),
|
||||
getDeclaringClass());
|
||||
Executable root = getRoot();
|
||||
if (root != null) {
|
||||
declaredAnnotations = root.declaredAnnotations();
|
||||
} else {
|
||||
declaredAnnotations = AnnotationParser.parseAnnotations(
|
||||
getAnnotationBytes(),
|
||||
sun.misc.SharedSecrets.getJavaLangAccess().
|
||||
getConstantPool(getDeclaringClass()),
|
||||
getDeclaringClass());
|
||||
}
|
||||
}
|
||||
return declaredAnnotations;
|
||||
}
|
||||
|
@ -81,6 +81,9 @@ class Field extends AccessibleObject implements Member {
|
||||
// For sharing of FieldAccessors. This branching structure is
|
||||
// currently only two levels deep (i.e., one root Field and
|
||||
// potentially many Field objects pointing to it.)
|
||||
//
|
||||
// If this branching structure would ever contain cycles, deadlocks can
|
||||
// occur in annotation code.
|
||||
private Field root;
|
||||
|
||||
// Generics infrastructure
|
||||
@ -141,6 +144,9 @@ class Field extends AccessibleObject implements Member {
|
||||
// which implicitly requires that new java.lang.reflect
|
||||
// objects be fabricated for each reflective call on Class
|
||||
// objects.)
|
||||
if (this.root != null)
|
||||
throw new IllegalArgumentException("Can not copy a non-root Field");
|
||||
|
||||
Field res = new Field(clazz, name, type, modifiers, slot, signature, annotations);
|
||||
res.root = this;
|
||||
// Might as well eagerly propagate this if already present
|
||||
@ -1137,10 +1143,15 @@ class Field extends AccessibleObject implements Member {
|
||||
|
||||
private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
|
||||
if (declaredAnnotations == null) {
|
||||
declaredAnnotations = AnnotationParser.parseAnnotations(
|
||||
annotations, sun.misc.SharedSecrets.getJavaLangAccess().
|
||||
getConstantPool(getDeclaringClass()),
|
||||
getDeclaringClass());
|
||||
Field root = this.root;
|
||||
if (root != null) {
|
||||
declaredAnnotations = root.declaredAnnotations();
|
||||
} else {
|
||||
declaredAnnotations = AnnotationParser.parseAnnotations(
|
||||
annotations,
|
||||
sun.misc.SharedSecrets.getJavaLangAccess().getConstantPool(getDeclaringClass()),
|
||||
getDeclaringClass());
|
||||
}
|
||||
}
|
||||
return declaredAnnotations;
|
||||
}
|
||||
|
@ -79,6 +79,9 @@ public final class Method extends Executable {
|
||||
// For sharing of MethodAccessors. This branching structure is
|
||||
// currently only two levels deep (i.e., one root Method and
|
||||
// potentially many Method objects pointing to it.)
|
||||
//
|
||||
// If this branching structure would ever contain cycles, deadlocks can
|
||||
// occur in annotation code.
|
||||
private Method root;
|
||||
|
||||
// Generics infrastructure
|
||||
@ -144,6 +147,9 @@ public final class Method extends Executable {
|
||||
// which implicitly requires that new java.lang.reflect
|
||||
// objects be fabricated for each reflective call on Class
|
||||
// objects.)
|
||||
if (this.root != null)
|
||||
throw new IllegalArgumentException("Can not copy a non-root Method");
|
||||
|
||||
Method res = new Method(clazz, name, parameterTypes, returnType,
|
||||
exceptionTypes, modifiers, slot, signature,
|
||||
annotations, parameterAnnotations, annotationDefault);
|
||||
@ -153,6 +159,14 @@ public final class Method extends Executable {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by Excecutable for annotation sharing.
|
||||
*/
|
||||
@Override
|
||||
Executable getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasGenericInformation() {
|
||||
return (getGenericSignature() != null);
|
||||
|
@ -354,10 +354,11 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
|
||||
* @exception NullPointerException if {@code name} is {@code null}.
|
||||
*/
|
||||
protected Class<?> findClass(final String name)
|
||||
throws ClassNotFoundException
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
final Class<?> result;
|
||||
try {
|
||||
return AccessController.doPrivileged(
|
||||
result = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<Class<?>>() {
|
||||
public Class<?> run() throws ClassNotFoundException {
|
||||
String path = name.replace('.', '/').concat(".class");
|
||||
@ -369,13 +370,17 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
|
||||
throw new ClassNotFoundException(name, e);
|
||||
}
|
||||
} else {
|
||||
throw new ClassNotFoundException(name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, acc);
|
||||
} catch (java.security.PrivilegedActionException pae) {
|
||||
throw (ClassNotFoundException) pae.getException();
|
||||
}
|
||||
if (result == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -424,7 +424,7 @@ public final class Duration
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
long val = Long.parseLong(text, 10, start, end);
|
||||
long val = Long.parseLong(text, start, end, 10);
|
||||
return Math.multiplyExact(val, multiplier);
|
||||
} catch (NumberFormatException | ArithmeticException ex) {
|
||||
throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex);
|
||||
@ -437,7 +437,7 @@ public final class Duration
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
int fraction = Integer.parseInt(text, 10, start, end);
|
||||
int fraction = Integer.parseInt(text, start, end, 10);
|
||||
|
||||
// for number strings smaller than 9 digits, interpret as if there
|
||||
// were trailing zeros
|
||||
|
@ -358,7 +358,7 @@ public final class Period
|
||||
if (start < 0 || end < 0) {
|
||||
return 0;
|
||||
}
|
||||
int val = Integer.parseInt(text, 10, start, end);
|
||||
int val = Integer.parseInt(text, start, end, 10);
|
||||
try {
|
||||
return Math.multiplyExact(val, negate);
|
||||
} catch (ArithmeticException ex) {
|
||||
|
@ -3865,7 +3865,7 @@ public class Arrays {
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return indexOf(o) != -1;
|
||||
return indexOf(o) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -314,7 +314,7 @@ public class LinkedList<E>
|
||||
* @return {@code true} if this list contains the specified element
|
||||
*/
|
||||
public boolean contains(Object o) {
|
||||
return indexOf(o) != -1;
|
||||
return indexOf(o) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -406,7 +406,7 @@ public class PriorityQueue<E> extends AbstractQueue<E>
|
||||
* @return {@code true} if this queue contains the specified element
|
||||
*/
|
||||
public boolean contains(Object o) {
|
||||
return indexOf(o) != -1;
|
||||
return indexOf(o) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,7 +194,8 @@ public final class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
*
|
||||
*/
|
||||
public static UUID fromString(String name) {
|
||||
if (name.length() > 36) {
|
||||
int len = name.length();
|
||||
if (len > 36) {
|
||||
throw new IllegalArgumentException("UUID string too large");
|
||||
}
|
||||
|
||||
@ -214,15 +215,14 @@ public final class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
throw new IllegalArgumentException("Invalid UUID string: " + name);
|
||||
}
|
||||
|
||||
long mostSigBits = Long.parseLong(name, 16, 0, dash1) & 0xffffffffL;
|
||||
long mostSigBits = Long.parseLong(name, 0, dash1, 16) & 0xffffffffL;
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.parseLong(name, 16, dash1 + 1, dash2) & 0xffffL;
|
||||
mostSigBits |= Long.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL;
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.parseLong(name, 16, dash2 + 1, dash3) & 0xffffL;
|
||||
|
||||
long leastSigBits = Long.parseLong(name, 16, dash3 + 1, dash4) & 0xffffL;
|
||||
mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL;
|
||||
long leastSigBits = Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL;
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.parseLong(name, 16, dash4 + 1) & 0xffffffffffffL;
|
||||
leastSigBits |= Long.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL;
|
||||
|
||||
return new UUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -297,15 +297,22 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to set SIGNAL status unless already completed. Used by
|
||||
* ForkJoinPool. Other variants are directly incorporated into
|
||||
* externalAwaitDone etc.
|
||||
* If not done, sets SIGNAL status and performs Object.wait(timeout).
|
||||
* This task may or may not be done on exit. Ignores interrupts.
|
||||
*
|
||||
* @return true if successful
|
||||
* @param timeout using Object.wait conventions.
|
||||
*/
|
||||
final boolean trySetSignal() {
|
||||
int s = status;
|
||||
return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL);
|
||||
final void internalWait(long timeout) {
|
||||
int s;
|
||||
if ((s = status) >= 0 && // force completer to issue notify
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
try { wait(timeout); } catch (InterruptedException ie) { }
|
||||
else
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,35 +320,29 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* @return status upon completion
|
||||
*/
|
||||
private int externalAwaitDone() {
|
||||
int s;
|
||||
ForkJoinPool cp = ForkJoinPool.common;
|
||||
if ((s = status) >= 0) {
|
||||
if (cp != null) {
|
||||
if (this instanceof CountedCompleter)
|
||||
s = cp.externalHelpComplete((CountedCompleter<?>)this, Integer.MAX_VALUE);
|
||||
else if (cp.tryExternalUnpush(this))
|
||||
s = doExec();
|
||||
}
|
||||
if (s >= 0 && (s = status) >= 0) {
|
||||
boolean interrupted = false;
|
||||
do {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ie) {
|
||||
interrupted = true;
|
||||
}
|
||||
int s = ((this instanceof CountedCompleter) ? // try helping
|
||||
ForkJoinPool.common.externalHelpComplete(
|
||||
(CountedCompleter<?>)this, 0) :
|
||||
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
|
||||
if (s >= 0 && (s = status) >= 0) {
|
||||
boolean interrupted = false;
|
||||
do {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0) {
|
||||
try {
|
||||
wait(0L);
|
||||
} catch (InterruptedException ie) {
|
||||
interrupted = true;
|
||||
}
|
||||
else
|
||||
notifyAll();
|
||||
}
|
||||
else
|
||||
notifyAll();
|
||||
}
|
||||
} while ((s = status) >= 0);
|
||||
if (interrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
} while ((s = status) >= 0);
|
||||
if (interrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@ -351,22 +352,22 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
*/
|
||||
private int externalInterruptibleAwaitDone() throws InterruptedException {
|
||||
int s;
|
||||
ForkJoinPool cp = ForkJoinPool.common;
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
if ((s = status) >= 0 && cp != null) {
|
||||
if (this instanceof CountedCompleter)
|
||||
cp.externalHelpComplete((CountedCompleter<?>)this, Integer.MAX_VALUE);
|
||||
else if (cp.tryExternalUnpush(this))
|
||||
doExec();
|
||||
}
|
||||
while ((s = status) >= 0) {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait();
|
||||
else
|
||||
notifyAll();
|
||||
if ((s = status) >= 0 &&
|
||||
(s = ((this instanceof CountedCompleter) ?
|
||||
ForkJoinPool.common.externalHelpComplete(
|
||||
(CountedCompleter<?>)this, 0) :
|
||||
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
|
||||
0)) >= 0) {
|
||||
while ((s = status) >= 0) {
|
||||
if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait(0L);
|
||||
else
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -386,7 +387,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
|
||||
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
|
||||
tryUnpush(this) && (s = doExec()) < 0 ? s :
|
||||
wt.pool.awaitJoin(w, this) :
|
||||
wt.pool.awaitJoin(w, this, 0L) :
|
||||
externalAwaitDone();
|
||||
}
|
||||
|
||||
@ -399,7 +400,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
int s; Thread t; ForkJoinWorkerThread wt;
|
||||
return (s = doExec()) < 0 ? s :
|
||||
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
|
||||
(wt = (ForkJoinWorkerThread)t).pool.awaitJoin(wt.workQueue, this) :
|
||||
(wt = (ForkJoinWorkerThread)t).pool.
|
||||
awaitJoin(wt.workQueue, this, 0L) :
|
||||
externalAwaitDone();
|
||||
}
|
||||
|
||||
@ -577,7 +579,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
Throwable ex;
|
||||
if (e == null || (ex = e.ex) == null)
|
||||
return null;
|
||||
if (false && e.thrower != Thread.currentThread().getId()) {
|
||||
if (e.thrower != Thread.currentThread().getId()) {
|
||||
Class<? extends Throwable> ec = ex.getClass();
|
||||
try {
|
||||
Constructor<?> noArgCtor = null;
|
||||
@ -587,13 +589,17 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
Class<?>[] ps = c.getParameterTypes();
|
||||
if (ps.length == 0)
|
||||
noArgCtor = c;
|
||||
else if (ps.length == 1 && ps[0] == Throwable.class)
|
||||
return (Throwable)(c.newInstance(ex));
|
||||
else if (ps.length == 1 && ps[0] == Throwable.class) {
|
||||
Throwable wx = (Throwable)c.newInstance(ex);
|
||||
return (wx == null) ? ex : wx;
|
||||
}
|
||||
}
|
||||
if (noArgCtor != null) {
|
||||
Throwable wx = (Throwable)(noArgCtor.newInstance());
|
||||
wx.initCause(ex);
|
||||
return wx;
|
||||
if (wx != null) {
|
||||
wx.initCause(ex);
|
||||
return wx;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
@ -1017,67 +1023,40 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
*/
|
||||
public final V get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
int s;
|
||||
long nanos = unit.toNanos(timeout);
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
// Messy in part because we measure in nanosecs, but wait in millisecs
|
||||
int s; long ms;
|
||||
long ns = unit.toNanos(timeout);
|
||||
ForkJoinPool cp;
|
||||
if ((s = status) >= 0 && ns > 0L) {
|
||||
long deadline = System.nanoTime() + ns;
|
||||
ForkJoinPool p = null;
|
||||
ForkJoinPool.WorkQueue w = null;
|
||||
if ((s = status) >= 0 && nanos > 0L) {
|
||||
long d = System.nanoTime() + nanos;
|
||||
long deadline = (d == 0L) ? 1L : d; // avoid 0
|
||||
Thread t = Thread.currentThread();
|
||||
if (t instanceof ForkJoinWorkerThread) {
|
||||
ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
|
||||
p = wt.pool;
|
||||
w = wt.workQueue;
|
||||
p.helpJoinOnce(w, this); // no retries on failure
|
||||
s = wt.pool.awaitJoin(wt.workQueue, this, deadline);
|
||||
}
|
||||
else if ((cp = ForkJoinPool.common) != null) {
|
||||
if (this instanceof CountedCompleter)
|
||||
cp.externalHelpComplete((CountedCompleter<?>)this, Integer.MAX_VALUE);
|
||||
else if (cp.tryExternalUnpush(this))
|
||||
doExec();
|
||||
}
|
||||
boolean canBlock = false;
|
||||
boolean interrupted = false;
|
||||
try {
|
||||
while ((s = status) >= 0) {
|
||||
if (w != null && w.qlock < 0)
|
||||
cancelIgnoringExceptions(this);
|
||||
else if (!canBlock) {
|
||||
if (p == null || p.tryCompensate(p.ctl))
|
||||
canBlock = true;
|
||||
}
|
||||
else {
|
||||
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0) {
|
||||
try {
|
||||
wait(ms);
|
||||
} catch (InterruptedException ie) {
|
||||
if (p == null)
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
notifyAll();
|
||||
}
|
||||
else if ((s = ((this instanceof CountedCompleter) ?
|
||||
ForkJoinPool.common.externalHelpComplete(
|
||||
(CountedCompleter<?>)this, 0) :
|
||||
ForkJoinPool.common.tryExternalUnpush(this) ?
|
||||
doExec() : 0)) >= 0) {
|
||||
long ns, ms; // measure in nanosecs, but wait in millisecs
|
||||
while ((s = status) >= 0 &&
|
||||
(ns = deadline - System.nanoTime()) > 0L) {
|
||||
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
|
||||
U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
|
||||
synchronized (this) {
|
||||
if (status >= 0)
|
||||
wait(ms); // OK to throw InterruptedException
|
||||
else
|
||||
notifyAll();
|
||||
}
|
||||
if ((s = status) < 0 || interrupted ||
|
||||
(ns = deadline - System.nanoTime()) <= 0L)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (p != null && canBlock)
|
||||
p.incrementActiveCount();
|
||||
}
|
||||
if (interrupted)
|
||||
throw new InterruptedException();
|
||||
}
|
||||
if (s >= 0)
|
||||
s = status;
|
||||
if ((s &= DONE_MASK) != NORMAL) {
|
||||
Throwable ex;
|
||||
if (s == CANCELLED)
|
||||
|
@ -66,7 +66,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
* owning thread.
|
||||
*
|
||||
* Support for (non-public) subclass InnocuousForkJoinWorkerThread
|
||||
* requires that we break quite a lot of encapulation (via Unsafe)
|
||||
* requires that we break quite a lot of encapsulation (via Unsafe)
|
||||
* both here and in the subclass to access and set Thread fields.
|
||||
*/
|
||||
|
||||
@ -118,7 +118,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
* @return the index number
|
||||
*/
|
||||
public int getPoolIndex() {
|
||||
return workQueue.poolIndex >>> 1; // ignore odd/even tag bit
|
||||
return workQueue.getPoolIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,7 +171,7 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases ThreadLocals by nulling out Thread maps
|
||||
* Erases ThreadLocals by nulling out Thread maps.
|
||||
*/
|
||||
final void eraseThreadLocals() {
|
||||
U.putObject(this, THREADLOCALS, null);
|
||||
@ -246,8 +246,8 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
|
||||
/**
|
||||
* Returns a new group with the system ThreadGroup (the
|
||||
* topmost, parentless group) as parent. Uses Unsafe to
|
||||
* traverse Thread group and ThreadGroup parent fields.
|
||||
* topmost, parent-less group) as parent. Uses Unsafe to
|
||||
* traverse Thread.group and ThreadGroup.parent fields.
|
||||
*/
|
||||
private static ThreadGroup createThreadGroup() {
|
||||
try {
|
||||
@ -274,4 +274,3 @@ public class ForkJoinWorkerThread extends Thread {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -29,38 +29,34 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ValueConversions {
|
||||
private static final Class<?> THIS_CLASS = ValueConversions.class;
|
||||
// Do not adjust this except for special platforms:
|
||||
private static final int MAX_ARITY;
|
||||
static {
|
||||
final Object[] values = { 255 };
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
values[0] = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
MAX_ARITY = (Integer) values[0];
|
||||
}
|
||||
|
||||
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
|
||||
@SuppressWarnings("unchecked") // generic array creation
|
||||
EnumMap<Wrapper, MethodHandle>[] caches
|
||||
= (EnumMap<Wrapper, MethodHandle>[]) new EnumMap<?,?>[n];
|
||||
/** Thread-safe canonicalized mapping from Wrapper to MethodHandle
|
||||
* with unsynchronized reads and synchronized writes.
|
||||
* It's safe to publish MethodHandles by data race because they are immutable. */
|
||||
private static class WrapperCache {
|
||||
/** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */
|
||||
private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class);
|
||||
|
||||
public MethodHandle get(Wrapper w) {
|
||||
return map.get(w);
|
||||
}
|
||||
public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
|
||||
// Simulate CAS to avoid racy duplication
|
||||
MethodHandle prev = map.putIfAbsent(w, mh);
|
||||
if (prev != null) return prev;
|
||||
return mh;
|
||||
}
|
||||
}
|
||||
|
||||
private static WrapperCache[] newWrapperCaches(int n) {
|
||||
WrapperCache[] caches = new WrapperCache[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
caches[i] = new EnumMap<>(Wrapper.class);
|
||||
caches[i] = new WrapperCache();
|
||||
return caches;
|
||||
}
|
||||
|
||||
@ -71,63 +67,92 @@ public class ValueConversions {
|
||||
// implicit conversions sanctioned by JLS 5.1.2, etc.
|
||||
// explicit conversions as allowed by explicitCastArguments
|
||||
|
||||
static int unboxInteger(Integer x) {
|
||||
return x;
|
||||
}
|
||||
static int unboxInteger(Object x, boolean cast) {
|
||||
if (x instanceof Integer)
|
||||
return ((Integer) x).intValue();
|
||||
return (Integer) x;
|
||||
return primitiveConversion(Wrapper.INT, x, cast).intValue();
|
||||
}
|
||||
|
||||
static byte unboxByte(Byte x) {
|
||||
return x;
|
||||
}
|
||||
static byte unboxByte(Object x, boolean cast) {
|
||||
if (x instanceof Byte)
|
||||
return ((Byte) x).byteValue();
|
||||
return (Byte) x;
|
||||
return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
|
||||
}
|
||||
|
||||
static short unboxShort(Short x) {
|
||||
return x;
|
||||
}
|
||||
static short unboxShort(Object x, boolean cast) {
|
||||
if (x instanceof Short)
|
||||
return ((Short) x).shortValue();
|
||||
return (Short) x;
|
||||
return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
|
||||
}
|
||||
|
||||
static boolean unboxBoolean(Boolean x) {
|
||||
return x;
|
||||
}
|
||||
static boolean unboxBoolean(Object x, boolean cast) {
|
||||
if (x instanceof Boolean)
|
||||
return ((Boolean) x).booleanValue();
|
||||
return (Boolean) x;
|
||||
return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
|
||||
}
|
||||
|
||||
static char unboxCharacter(Character x) {
|
||||
return x;
|
||||
}
|
||||
static char unboxCharacter(Object x, boolean cast) {
|
||||
if (x instanceof Character)
|
||||
return ((Character) x).charValue();
|
||||
return (Character) x;
|
||||
return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
|
||||
}
|
||||
|
||||
static long unboxLong(Long x) {
|
||||
return x;
|
||||
}
|
||||
static long unboxLong(Object x, boolean cast) {
|
||||
if (x instanceof Long)
|
||||
return ((Long) x).longValue();
|
||||
return (Long) x;
|
||||
return primitiveConversion(Wrapper.LONG, x, cast).longValue();
|
||||
}
|
||||
|
||||
static float unboxFloat(Float x) {
|
||||
return x;
|
||||
}
|
||||
static float unboxFloat(Object x, boolean cast) {
|
||||
if (x instanceof Float)
|
||||
return ((Float) x).floatValue();
|
||||
return (Float) x;
|
||||
return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
|
||||
}
|
||||
|
||||
static double unboxDouble(Double x) {
|
||||
return x;
|
||||
}
|
||||
static double unboxDouble(Object x, boolean cast) {
|
||||
if (x instanceof Double)
|
||||
return ((Double) x).doubleValue();
|
||||
return (Double) x;
|
||||
return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
|
||||
}
|
||||
|
||||
private static MethodType unboxType(Wrapper wrap) {
|
||||
private static MethodType unboxType(Wrapper wrap, int kind) {
|
||||
if (kind == 0)
|
||||
return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());
|
||||
return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
UNBOX_CONVERSIONS = newWrapperCaches(2);
|
||||
private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);
|
||||
|
||||
private static MethodHandle unbox(Wrapper wrap, boolean cast) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)];
|
||||
private static MethodHandle unbox(Wrapper wrap, int kind) {
|
||||
// kind 0 -> strongly typed with NPE
|
||||
// kind 1 -> strongly typed but zero for null,
|
||||
// kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE
|
||||
// kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null
|
||||
WrapperCache cache = UNBOX_CONVERSIONS[kind];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
@ -135,41 +160,59 @@ public class ValueConversions {
|
||||
// slow path
|
||||
switch (wrap) {
|
||||
case OBJECT:
|
||||
mh = IDENTITY; break;
|
||||
case VOID:
|
||||
mh = IGNORE; break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
throw new IllegalArgumentException("unbox "+wrap);
|
||||
}
|
||||
// look up the method
|
||||
String name = "unbox" + wrap.wrapperSimpleName();
|
||||
MethodType type = unboxType(wrap);
|
||||
MethodType type = unboxType(wrap, kind);
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
if (mh != null) {
|
||||
mh = MethodHandles.insertArguments(mh, 1, cast);
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
if (kind > 0) {
|
||||
boolean cast = (kind != 2);
|
||||
mh = MethodHandles.insertArguments(mh, 1, cast);
|
||||
}
|
||||
if (kind == 1) { // casting but exact (null -> zero)
|
||||
mh = mh.asType(unboxType(wrap, 0));
|
||||
}
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
|
||||
+ (cast ? " (cast)" : ""));
|
||||
+ (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));
|
||||
}
|
||||
|
||||
/** Return an exact unboxer for the given primitive type. */
|
||||
public static MethodHandle unboxExact(Wrapper type) {
|
||||
return unbox(type, 0);
|
||||
}
|
||||
|
||||
/** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
|
||||
* The boolean says whether to throw an NPE on a null value (false means unbox a zero).
|
||||
* The type of the unboxer is of a form like (Integer)int.
|
||||
*/
|
||||
public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {
|
||||
return unbox(type, throwNPE ? 0 : 1);
|
||||
}
|
||||
|
||||
/** Return a widening unboxer for the given primitive type.
|
||||
* Widen narrower primitive boxes to the given type.
|
||||
* Do not narrow any primitive values or convert null to zero.
|
||||
* The type of the unboxer is of a form like (Object)int.
|
||||
*/
|
||||
public static MethodHandle unboxWiden(Wrapper type) {
|
||||
return unbox(type, 2);
|
||||
}
|
||||
|
||||
/** Return a casting unboxer for the given primitive type.
|
||||
* Widen or narrow primitive values to the given type, or convert null to zero, as needed.
|
||||
* The type of the unboxer is of a form like (Object)int.
|
||||
*/
|
||||
public static MethodHandle unboxCast(Wrapper type) {
|
||||
return unbox(type, true);
|
||||
}
|
||||
|
||||
public static MethodHandle unbox(Class<?> type) {
|
||||
return unbox(Wrapper.forPrimitiveType(type), false);
|
||||
}
|
||||
|
||||
public static MethodHandle unboxCast(Class<?> type) {
|
||||
return unbox(Wrapper.forPrimitiveType(type), true);
|
||||
return unbox(type, 3);
|
||||
}
|
||||
|
||||
static private final Integer ZERO_INT = 0, ONE_INT = 1;
|
||||
@ -266,57 +309,26 @@ public class ValueConversions {
|
||||
return MethodType.methodType(boxType, wrap.primitiveType());
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
BOX_CONVERSIONS = newWrapperCaches(2);
|
||||
private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);
|
||||
|
||||
private static MethodHandle box(Wrapper wrap, boolean exact) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)];
|
||||
public static MethodHandle boxExact(Wrapper wrap) {
|
||||
WrapperCache cache = BOX_CONVERSIONS[0];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
switch (wrap) {
|
||||
case OBJECT:
|
||||
mh = IDENTITY; break;
|
||||
case VOID:
|
||||
mh = ZERO_OBJECT;
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
// look up the method
|
||||
String name = "box" + wrap.wrapperSimpleName();
|
||||
MethodType type = boxType(wrap);
|
||||
if (exact) {
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
} else {
|
||||
mh = box(wrap, !exact).asType(type.erase());
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find box adapter for "
|
||||
+ wrap + (exact ? " (exact)" : ""));
|
||||
}
|
||||
|
||||
public static MethodHandle box(Class<?> type) {
|
||||
boolean exact = false;
|
||||
// e.g., boxShort(short)Short if exact,
|
||||
// e.g., boxShort(short)Object if !exact
|
||||
return box(Wrapper.forPrimitiveType(type), exact);
|
||||
}
|
||||
|
||||
public static MethodHandle box(Wrapper type) {
|
||||
boolean exact = false;
|
||||
return box(type, exact);
|
||||
throw new IllegalArgumentException("cannot find box adapter for " + wrap);
|
||||
}
|
||||
|
||||
/// Constant functions
|
||||
@ -348,11 +360,10 @@ public class ValueConversions {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
CONSTANT_FUNCTIONS = newWrapperCaches(2);
|
||||
private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);
|
||||
|
||||
public static MethodHandle zeroConstantFunction(Wrapper wrap) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[0];
|
||||
WrapperCache cache = CONSTANT_FUNCTIONS[0];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
@ -373,184 +384,37 @@ public class ValueConversions {
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
|
||||
// use zeroInt and cast the result
|
||||
if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
|
||||
mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find zero constant for " + wrap);
|
||||
}
|
||||
|
||||
/// Converting references to references.
|
||||
|
||||
/**
|
||||
* Identity function.
|
||||
* @param x an arbitrary reference value
|
||||
* @return the same value x
|
||||
*/
|
||||
static <T> T identity(T x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static <T> T[] identity(T[] x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function on ints.
|
||||
* @param x an arbitrary int value
|
||||
* @return the same value x
|
||||
*/
|
||||
static int identity(int x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static byte identity(byte x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static short identity(short x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static boolean identity(boolean x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static char identity(char x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function on longs.
|
||||
* @param x an arbitrary long value
|
||||
* @return the same value x
|
||||
*/
|
||||
static long identity(long x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static float identity(float x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static double identity(double x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
private static ClassCastException newClassCastException(Class<?> t, Object obj) {
|
||||
return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName());
|
||||
}
|
||||
|
||||
private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY,
|
||||
ARRAY_IDENTITY, FILL_NEW_TYPED_ARRAY, FILL_NEW_ARRAY;
|
||||
private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY;
|
||||
static {
|
||||
try {
|
||||
MethodType idType = MethodType.genericMethodType(1);
|
||||
MethodType ignoreType = idType.changeReturnType(void.class);
|
||||
MethodType zeroObjectType = MethodType.genericMethodType(0);
|
||||
IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
|
||||
CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
|
||||
ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType);
|
||||
IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
|
||||
EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
|
||||
ARRAY_IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(Object[].class, Object[].class));
|
||||
FILL_NEW_ARRAY = IMPL_LOOKUP
|
||||
.findStatic(THIS_CLASS, "fillNewArray",
|
||||
MethodType.methodType(Object[].class, Integer.class, Object[].class));
|
||||
FILL_NEW_TYPED_ARRAY = IMPL_LOOKUP
|
||||
.findStatic(THIS_CLASS, "fillNewTypedArray",
|
||||
MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
|
||||
} catch (NoSuchMethodException | IllegalAccessException ex) {
|
||||
throw newInternalError("uncaught exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Varargs methods need to be in a separately initialized class, to avoid bootstrapping problems.
|
||||
static class LazyStatics {
|
||||
private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
|
||||
static {
|
||||
try {
|
||||
//MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class));
|
||||
COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class));
|
||||
COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class));
|
||||
MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError("uncaught exception", ex);
|
||||
}
|
||||
}
|
||||
public static MethodHandle ignore() {
|
||||
return IGNORE;
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
|
||||
= newWrapperCaches(1);
|
||||
|
||||
/** Return a method that casts its sole argument (an Object) to the given type
|
||||
* and returns it as the given type.
|
||||
*/
|
||||
public static MethodHandle cast(Class<?> type) {
|
||||
return cast(type, CAST_REFERENCE);
|
||||
}
|
||||
public static MethodHandle cast(Class<?> type, MethodHandle castReference) {
|
||||
if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
|
||||
MethodHandle mh;
|
||||
Wrapper wrap = null;
|
||||
EnumMap<Wrapper, MethodHandle> cache = null;
|
||||
if (Wrapper.isWrapperType(type)) {
|
||||
wrap = Wrapper.forWrapperType(type);
|
||||
cache = WRAPPER_CASTS[0];
|
||||
mh = cache.get(wrap);
|
||||
if (mh != null) return mh;
|
||||
}
|
||||
mh = MethodHandles.insertArguments(castReference, 0, type);
|
||||
if (cache != null)
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
|
||||
public static MethodHandle identity() {
|
||||
return IDENTITY;
|
||||
}
|
||||
|
||||
public static MethodHandle identity(Class<?> type) {
|
||||
if (!type.isPrimitive())
|
||||
// Reference identity has been moved into MethodHandles:
|
||||
return MethodHandles.identity(type);
|
||||
return identity(Wrapper.findPrimitiveType(type));
|
||||
}
|
||||
|
||||
public static MethodHandle identity(Wrapper wrap) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[1];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
MethodType type = MethodType.methodType(wrap.primitiveType());
|
||||
if (wrap != Wrapper.VOID)
|
||||
type = type.appendParameterTypes(wrap.primitiveType());
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
if (mh == null && wrap == Wrapper.VOID) {
|
||||
mh = EMPTY; // #(){} : #()void
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find identity for " + wrap);
|
||||
/** Return a method that casts its second argument (an Object) to the given type (a Class). */
|
||||
public static MethodHandle cast() {
|
||||
return CAST_REFERENCE;
|
||||
}
|
||||
|
||||
/// Primitive conversions.
|
||||
@ -759,11 +623,10 @@ public class ValueConversions {
|
||||
return (x ? (byte)1 : (byte)0);
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
|
||||
private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
|
||||
|
||||
public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
|
||||
WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
|
||||
MethodHandle mh = cache.get(wdst);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
@ -771,17 +634,9 @@ public class ValueConversions {
|
||||
// slow path
|
||||
Class<?> src = wsrc.primitiveType();
|
||||
Class<?> dst = wdst.primitiveType();
|
||||
MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src);
|
||||
MethodType type = MethodType.methodType(dst, src);
|
||||
if (wsrc == wdst) {
|
||||
mh = identity(src);
|
||||
} else if (wsrc == Wrapper.VOID) {
|
||||
mh = zeroConstantFunction(wdst);
|
||||
} else if (wdst == Wrapper.VOID) {
|
||||
mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles.
|
||||
} else if (wsrc == Wrapper.OBJECT) {
|
||||
mh = unboxCast(dst);
|
||||
} else if (wdst == Wrapper.OBJECT) {
|
||||
mh = box(src);
|
||||
mh = MethodHandles.identity(src);
|
||||
} else {
|
||||
assert(src.isPrimitive() && dst.isPrimitive());
|
||||
try {
|
||||
@ -792,8 +647,7 @@ public class ValueConversions {
|
||||
}
|
||||
if (mh != null) {
|
||||
assert(mh.type() == type) : mh;
|
||||
cache.put(wdst, mh);
|
||||
return mh;
|
||||
return cache.put(wdst, mh);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("cannot find primitive conversion function for " +
|
||||
@ -808,363 +662,6 @@ public class ValueConversions {
|
||||
return Character.toUpperCase(x.charAt(0))+x.substring(1);
|
||||
}
|
||||
|
||||
/// Collection of multiple arguments.
|
||||
|
||||
public static Object convertArrayElements(Class<?> arrayType, Object array) {
|
||||
Class<?> src = array.getClass().getComponentType();
|
||||
Class<?> dst = arrayType.getComponentType();
|
||||
if (src == null || dst == null) throw new IllegalArgumentException("not array type");
|
||||
Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null);
|
||||
Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null);
|
||||
int length;
|
||||
if (sw == null) {
|
||||
Object[] a = (Object[]) array;
|
||||
length = a.length;
|
||||
if (dw == null)
|
||||
return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class));
|
||||
Object res = dw.makeArray(length);
|
||||
dw.copyArrayUnboxing(a, 0, res, 0, length);
|
||||
return res;
|
||||
}
|
||||
length = java.lang.reflect.Array.getLength(array);
|
||||
Object[] res;
|
||||
if (dw == null) {
|
||||
res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class));
|
||||
} else {
|
||||
res = new Object[length];
|
||||
}
|
||||
sw.copyArrayBoxing(array, 0, res, 0, length);
|
||||
if (dw == null) return res;
|
||||
Object a = dw.makeArray(length);
|
||||
dw.copyArrayUnboxing(res, 0, a, 0, length);
|
||||
return a;
|
||||
}
|
||||
|
||||
private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
|
||||
MethodType type = MethodType.genericMethodType(nargs)
|
||||
.changeReturnType(rtype)
|
||||
.insertParameterTypes(0, ptypes);
|
||||
try {
|
||||
return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object[] NO_ARGS_ARRAY = {};
|
||||
private static Object[] makeArray(Object... args) { return args; }
|
||||
private static Object[] array() { return NO_ARGS_ARRAY; }
|
||||
private static Object[] array(Object a0)
|
||||
{ return makeArray(a0); }
|
||||
private static Object[] array(Object a0, Object a1)
|
||||
{ return makeArray(a0, a1); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2)
|
||||
{ return makeArray(a0, a1, a2); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3)
|
||||
{ return makeArray(a0, a1, a2, a3); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4)
|
||||
{ return makeArray(a0, a1, a2, a3, a4); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8, Object a9)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
||||
private static MethodHandle[] makeArrays() {
|
||||
ArrayList<MethodHandle> mhs = new ArrayList<>();
|
||||
for (;;) {
|
||||
MethodHandle mh = findCollector("array", mhs.size(), Object[].class);
|
||||
if (mh == null) break;
|
||||
mhs.add(mh);
|
||||
}
|
||||
assert(mhs.size() == 11); // current number of methods
|
||||
return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
|
||||
}
|
||||
private static final MethodHandle[] ARRAYS = makeArrays();
|
||||
|
||||
// filling versions of the above:
|
||||
// using Integer len instead of int len and no varargs to avoid bootstrapping problems
|
||||
private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) {
|
||||
Object[] a = new Object[len];
|
||||
fillWithArguments(a, 0, args);
|
||||
return a;
|
||||
}
|
||||
private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) {
|
||||
Object[] a = Arrays.copyOf(example, len);
|
||||
fillWithArguments(a, 0, args);
|
||||
return a;
|
||||
}
|
||||
private static void fillWithArguments(Object[] a, int pos, Object... args) {
|
||||
System.arraycopy(args, 0, a, pos, args.length);
|
||||
}
|
||||
// using Integer pos instead of int pos to avoid bootstrapping problems
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0)
|
||||
{ fillWithArguments(a, pos, a0); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1)
|
||||
{ fillWithArguments(a, pos, a0, a1); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8, Object a9)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
|
||||
private static MethodHandle[] makeFillArrays() {
|
||||
ArrayList<MethodHandle> mhs = new ArrayList<>();
|
||||
mhs.add(null); // there is no empty fill; at least a0 is required
|
||||
for (;;) {
|
||||
MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class);
|
||||
if (mh == null) break;
|
||||
mhs.add(mh);
|
||||
}
|
||||
assert(mhs.size() == 11); // current number of methods
|
||||
return mhs.toArray(new MethodHandle[0]);
|
||||
}
|
||||
private static final MethodHandle[] FILL_ARRAYS = makeFillArrays();
|
||||
|
||||
private static Object[] copyAsReferenceArray(Class<? extends Object[]> arrayType, Object... a) {
|
||||
return Arrays.copyOf(a, a.length, arrayType);
|
||||
}
|
||||
private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
|
||||
Object a = w.makeArray(boxes.length);
|
||||
w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
|
||||
return a;
|
||||
}
|
||||
|
||||
/** Return a method handle that takes the indicated number of Object
|
||||
* arguments and returns an Object array of them, as if for varargs.
|
||||
*/
|
||||
public static MethodHandle varargsArray(int nargs) {
|
||||
MethodHandle mh = ARRAYS[nargs];
|
||||
if (mh != null) return mh;
|
||||
mh = findCollector("array", nargs, Object[].class);
|
||||
if (mh != null) return ARRAYS[nargs] = mh;
|
||||
mh = buildVarargsArray(FILL_NEW_ARRAY, ARRAY_IDENTITY, nargs);
|
||||
assert(assertCorrectArity(mh, nargs));
|
||||
return ARRAYS[nargs] = mh;
|
||||
}
|
||||
|
||||
private static boolean assertCorrectArity(MethodHandle mh, int arity) {
|
||||
assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) {
|
||||
// Build up the result mh as a sequence of fills like this:
|
||||
// finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23))
|
||||
// The various fill(_,10*I,___*[J]) are reusable.
|
||||
int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately
|
||||
int rightLen = nargs - leftLen;
|
||||
MethodHandle leftCollector = newArray.bindTo(nargs);
|
||||
leftCollector = leftCollector.asCollector(Object[].class, leftLen);
|
||||
MethodHandle mh = finisher;
|
||||
if (rightLen > 0) {
|
||||
MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
|
||||
if (mh == ARRAY_IDENTITY)
|
||||
mh = rightFiller;
|
||||
else
|
||||
mh = MethodHandles.collectArguments(mh, 0, rightFiller);
|
||||
}
|
||||
if (mh == ARRAY_IDENTITY)
|
||||
mh = leftCollector;
|
||||
else
|
||||
mh = MethodHandles.collectArguments(mh, 0, leftCollector);
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1);
|
||||
private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
|
||||
/** fill_array_to_right(N).invoke(a, argL..arg[N-1])
|
||||
* fills a[L]..a[N-1] with corresponding arguments,
|
||||
* and then returns a. The value L is a global constant (LEFT_ARGS).
|
||||
*/
|
||||
private static MethodHandle fillToRight(int nargs) {
|
||||
MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs];
|
||||
if (filler != null) return filler;
|
||||
filler = buildFiller(nargs);
|
||||
assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1));
|
||||
return FILL_ARRAY_TO_RIGHT[nargs] = filler;
|
||||
}
|
||||
private static MethodHandle buildFiller(int nargs) {
|
||||
if (nargs <= LEFT_ARGS)
|
||||
return ARRAY_IDENTITY; // no args to fill; return the array unchanged
|
||||
// we need room for both mh and a in mh.invoke(a, arg*[nargs])
|
||||
final int CHUNK = LEFT_ARGS;
|
||||
int rightLen = nargs % CHUNK;
|
||||
int midLen = nargs - rightLen;
|
||||
if (rightLen == 0) {
|
||||
midLen = nargs - (rightLen = CHUNK);
|
||||
if (FILL_ARRAY_TO_RIGHT[midLen] == null) {
|
||||
// build some precursors from left to right
|
||||
for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK)
|
||||
if (j > LEFT_ARGS) fillToRight(j);
|
||||
}
|
||||
}
|
||||
if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
|
||||
assert(rightLen > 0);
|
||||
MethodHandle midFill = fillToRight(midLen); // recursive fill
|
||||
MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1]
|
||||
assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS);
|
||||
assert(rightFill.type().parameterCount() == 1 + rightLen);
|
||||
|
||||
// Combine the two fills:
|
||||
// right(mid(a, x10..x19), x20..x23)
|
||||
// The final product will look like this:
|
||||
// right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23)
|
||||
if (midLen == LEFT_ARGS)
|
||||
return rightFill;
|
||||
else
|
||||
return MethodHandles.collectArguments(rightFill, 0, midFill);
|
||||
}
|
||||
|
||||
// Type-polymorphic version of varargs maker.
|
||||
private static final ClassValue<MethodHandle[]> TYPED_COLLECTORS
|
||||
= new ClassValue<MethodHandle[]>() {
|
||||
@Override
|
||||
protected MethodHandle[] computeValue(Class<?> type) {
|
||||
return new MethodHandle[256];
|
||||
}
|
||||
};
|
||||
|
||||
static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM
|
||||
|
||||
/** Return a method handle that takes the indicated number of
|
||||
* typed arguments and returns an array of them.
|
||||
* The type argument is the array type.
|
||||
*/
|
||||
public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType);
|
||||
// FIXME: Need more special casing and caching here.
|
||||
if (nargs >= MAX_JVM_ARITY/2 - 1) {
|
||||
int slots = nargs;
|
||||
final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH
|
||||
if (arrayType == double[].class || arrayType == long[].class)
|
||||
slots *= 2;
|
||||
if (slots > MAX_ARRAY_SLOTS)
|
||||
throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs);
|
||||
}
|
||||
if (elemType == Object.class)
|
||||
return varargsArray(nargs);
|
||||
// other cases: primitive arrays, subtypes of Object[]
|
||||
MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
|
||||
MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
|
||||
if (mh != null) return mh;
|
||||
if (elemType.isPrimitive()) {
|
||||
MethodHandle builder = FILL_NEW_ARRAY;
|
||||
MethodHandle producer = buildArrayProducer(arrayType);
|
||||
mh = buildVarargsArray(builder, producer, nargs);
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Object[]> objArrayType = (Class<? extends Object[]>) arrayType;
|
||||
Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
|
||||
MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example);
|
||||
MethodHandle producer = ARRAY_IDENTITY;
|
||||
mh = buildVarargsArray(builder, producer, nargs);
|
||||
}
|
||||
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
|
||||
assert(assertCorrectArity(mh, nargs));
|
||||
if (nargs < cache.length)
|
||||
cache[nargs] = mh;
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static MethodHandle buildArrayProducer(Class<?> arrayType) {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
if (elemType.isPrimitive())
|
||||
return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType));
|
||||
else
|
||||
return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType);
|
||||
}
|
||||
|
||||
// List version of varargs maker.
|
||||
|
||||
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
|
||||
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
|
||||
private static List<Object> list() { return NO_ARGS_LIST; }
|
||||
private static List<Object> list(Object a0)
|
||||
{ return makeList(a0); }
|
||||
private static List<Object> list(Object a0, Object a1)
|
||||
{ return makeList(a0, a1); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2)
|
||||
{ return makeList(a0, a1, a2); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3)
|
||||
{ return makeList(a0, a1, a2, a3); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4)
|
||||
{ return makeList(a0, a1, a2, a3, a4); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5)
|
||||
{ return makeList(a0, a1, a2, a3, a4, a5); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6)
|
||||
{ return makeList(a0, a1, a2, a3, a4, a5, a6); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7)
|
||||
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8)
|
||||
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
|
||||
private static List<Object> list(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8, Object a9)
|
||||
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
||||
private static MethodHandle[] makeLists() {
|
||||
ArrayList<MethodHandle> mhs = new ArrayList<>();
|
||||
for (;;) {
|
||||
MethodHandle mh = findCollector("list", mhs.size(), List.class);
|
||||
if (mh == null) break;
|
||||
mhs.add(mh);
|
||||
}
|
||||
assert(mhs.size() == 11); // current number of methods
|
||||
return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
|
||||
}
|
||||
private static final MethodHandle[] LISTS = makeLists();
|
||||
|
||||
/** Return a method handle that takes the indicated number of Object
|
||||
* arguments and returns a List.
|
||||
*/
|
||||
public static MethodHandle varargsList(int nargs) {
|
||||
MethodHandle mh = LISTS[nargs];
|
||||
if (mh != null) return mh;
|
||||
mh = findCollector("list", nargs, List.class);
|
||||
if (mh != null) return LISTS[nargs] = mh;
|
||||
return LISTS[nargs] = buildVarargsList(nargs);
|
||||
}
|
||||
private static MethodHandle buildVarargsList(int nargs) {
|
||||
return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST);
|
||||
}
|
||||
|
||||
// handy shared exception makers (they simplify the common case code)
|
||||
private static InternalError newInternalError(String message, Throwable cause) {
|
||||
return new InternalError(message, cause);
|
||||
|
@ -40,18 +40,38 @@ public class VerifyType {
|
||||
/**
|
||||
* True if a value can be stacked as the source type and unstacked as the
|
||||
* destination type, without violating the JVM's type consistency.
|
||||
* <p>
|
||||
* If both types are references, we apply the verifier's subclass check
|
||||
* (or subtyping, if keepInterfaces).
|
||||
* If the src type is a type guaranteed to be null (Void) it can be converted
|
||||
* to any other reference type.
|
||||
* <p>
|
||||
* If both types are primitives, we apply the verifier's primitive conversions.
|
||||
* These do not include Java conversions such as long to double, since those
|
||||
* require computation and (in general) stack depth changes.
|
||||
* But very simple 32-bit viewing changes, such as byte to int,
|
||||
* are null conversions, because they do not require any computation.
|
||||
* These conversions are from any type to a wider type up to 32 bits,
|
||||
* as long as the conversion is not signed to unsigned (byte to char).
|
||||
* <p>
|
||||
* The primitive type 'void' does not interconvert with any other type,
|
||||
* even though it is legal to drop any type from the stack and "return void".
|
||||
* The stack effects, though are different between void and any other type,
|
||||
* so it is safer to report a non-trivial conversion.
|
||||
*
|
||||
* @param src the type of a stacked value
|
||||
* @param dst the type by which we'd like to treat it
|
||||
* @param keepInterfaces if false, we treat any interface as if it were Object
|
||||
* @return whether the retyping can be done without motion or reformatting
|
||||
*/
|
||||
public static boolean isNullConversion(Class<?> src, Class<?> dst) {
|
||||
public static boolean isNullConversion(Class<?> src, Class<?> dst, boolean keepInterfaces) {
|
||||
if (src == dst) return true;
|
||||
// Verifier allows any interface to be treated as Object:
|
||||
if (dst.isInterface()) dst = Object.class;
|
||||
if (src.isInterface()) src = Object.class;
|
||||
if (src == dst) return true; // check again
|
||||
if (dst == void.class) return true; // drop any return value
|
||||
if (!keepInterfaces) {
|
||||
if (dst.isInterface()) dst = Object.class;
|
||||
if (src.isInterface()) src = Object.class;
|
||||
if (src == dst) return true; // check again
|
||||
}
|
||||
if (isNullType(src)) return !dst.isPrimitive();
|
||||
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
|
||||
if (!dst.isPrimitive()) return false;
|
||||
@ -82,25 +102,13 @@ public class VerifyType {
|
||||
* Is the given type java.lang.Null or an equivalent null-only type?
|
||||
*/
|
||||
public static boolean isNullType(Class<?> type) {
|
||||
if (type == null) return false;
|
||||
return type == NULL_CLASS
|
||||
// This one may also be used as a null type.
|
||||
// TO DO: Decide if we really want to legitimize it here.
|
||||
// Probably we do, unless java.lang.Null really makes it into Java 7
|
||||
//|| type == Void.class
|
||||
// Locally known null-only class:
|
||||
|| type == Empty.class
|
||||
;
|
||||
}
|
||||
private static final Class<?> NULL_CLASS;
|
||||
static {
|
||||
Class<?> nullClass = null;
|
||||
try {
|
||||
nullClass = Class.forName("java.lang.Null");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
// OK, we'll cope
|
||||
}
|
||||
NULL_CLASS = nullClass;
|
||||
// Any reference statically typed as Void is guaranteed to be null.
|
||||
// Therefore, it can be safely treated as a value of any
|
||||
// other type that admits null, i.e., a reference type.
|
||||
if (type == Void.class) return true;
|
||||
// Locally known null-only class:
|
||||
if (type == Empty.class) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,14 +119,14 @@ public class VerifyType {
|
||||
* @param recv the type of the method handle receiving the call
|
||||
* @return whether the retyping can be done without motion or reformatting
|
||||
*/
|
||||
public static boolean isNullConversion(MethodType call, MethodType recv) {
|
||||
public static boolean isNullConversion(MethodType call, MethodType recv, boolean keepInterfaces) {
|
||||
if (call == recv) return true;
|
||||
int len = call.parameterCount();
|
||||
if (len != recv.parameterCount()) return false;
|
||||
for (int i = 0; i < len; i++)
|
||||
if (!isNullConversion(call.parameterType(i), recv.parameterType(i)))
|
||||
if (!isNullConversion(call.parameterType(i), recv.parameterType(i), keepInterfaces))
|
||||
return false;
|
||||
return isNullConversion(recv.returnType(), call.returnType());
|
||||
return isNullConversion(recv.returnType(), call.returnType(), keepInterfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,14 +230,6 @@ public enum Wrapper {
|
||||
*/
|
||||
public <T> T zero(Class<T> type) { return convert(zero, type); }
|
||||
|
||||
// /** Produce a wrapper for the given wrapper or primitive type. */
|
||||
// public static Wrapper valueOf(Class<?> type) {
|
||||
// if (isPrimitiveType(type))
|
||||
// return forPrimitiveType(type);
|
||||
// else
|
||||
// return forWrapperType(type);
|
||||
// }
|
||||
|
||||
/** Return the wrapper that wraps values of the given type.
|
||||
* The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
|
||||
* Otherwise, the type must be a primitive.
|
||||
|
@ -69,33 +69,34 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
protected short codeIdx;
|
||||
protected short exceptionsIdx;
|
||||
// Boxing
|
||||
protected short valueOfIdx;
|
||||
protected short booleanIdx;
|
||||
protected short booleanCtorIdx;
|
||||
protected short booleanBoxIdx;
|
||||
protected short booleanUnboxIdx;
|
||||
protected short byteIdx;
|
||||
protected short byteCtorIdx;
|
||||
protected short byteBoxIdx;
|
||||
protected short byteUnboxIdx;
|
||||
protected short characterIdx;
|
||||
protected short characterCtorIdx;
|
||||
protected short characterBoxIdx;
|
||||
protected short characterUnboxIdx;
|
||||
protected short doubleIdx;
|
||||
protected short doubleCtorIdx;
|
||||
protected short doubleBoxIdx;
|
||||
protected short doubleUnboxIdx;
|
||||
protected short floatIdx;
|
||||
protected short floatCtorIdx;
|
||||
protected short floatBoxIdx;
|
||||
protected short floatUnboxIdx;
|
||||
protected short integerIdx;
|
||||
protected short integerCtorIdx;
|
||||
protected short integerBoxIdx;
|
||||
protected short integerUnboxIdx;
|
||||
protected short longIdx;
|
||||
protected short longCtorIdx;
|
||||
protected short longBoxIdx;
|
||||
protected short longUnboxIdx;
|
||||
protected short shortIdx;
|
||||
protected short shortCtorIdx;
|
||||
protected short shortBoxIdx;
|
||||
protected short shortUnboxIdx;
|
||||
|
||||
protected final short NUM_COMMON_CPOOL_ENTRIES = (short) 30;
|
||||
protected final short NUM_BOXING_CPOOL_ENTRIES = (short) 72;
|
||||
protected final short NUM_BOXING_CPOOL_ENTRIES = (short) 73;
|
||||
|
||||
// Requires that superClass has been set up
|
||||
protected void emitCommonConstantPoolEntries() {
|
||||
@ -181,9 +182,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
/** Constant pool entries required to be able to box/unbox primitive
|
||||
types. Note that we don't emit these if we don't need them. */
|
||||
protected void emitBoxingContantPoolEntries() {
|
||||
// * [UTF-8] "valueOf"
|
||||
// * [UTF-8] "java/lang/Boolean"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(Z)V"
|
||||
// * [UTF-8] "(Z)Ljava/lang/Boolean;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "booleanValue"
|
||||
@ -192,7 +194,7 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Byte"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(B)V"
|
||||
// * [UTF-8] "(B)Ljava/lang/Byte;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "byteValue"
|
||||
@ -201,7 +203,7 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Character"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(C)V"
|
||||
// * [UTF-8] "(C)Ljava/lang/Character;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "charValue"
|
||||
@ -210,7 +212,7 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Double"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(D)V"
|
||||
// * [UTF-8] "(D)Ljava/lang/Double;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "doubleValue"
|
||||
@ -219,7 +221,7 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Float"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(F)V"
|
||||
// * [UTF-8] "(F)Ljava/lang/Float;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "floatValue"
|
||||
@ -228,7 +230,7 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Integer"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(I)V"
|
||||
// * [UTF-8] "(I)Ljava/lang/Integer;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "intValue"
|
||||
@ -237,7 +239,7 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Long"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(J)V"
|
||||
// * [UTF-8] "(J)Ljava/lang/Long;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "longValue"
|
||||
@ -246,21 +248,26 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "java/lang/Short"
|
||||
// * [CONSTANT_Class_info] for above
|
||||
// * [UTF-8] "(S)V"
|
||||
// * [UTF-8] "(S)Ljava/lang/Short;"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
// * [UTF-8] "shortValue"
|
||||
// * [UTF-8] "()S"
|
||||
// * [CONSTANT_NameAndType_info] for above
|
||||
// * [CONSTANT_Methodref_info] for above
|
||||
|
||||
// valueOf-method name
|
||||
asm.emitConstantPoolUTF8("valueOf");
|
||||
valueOfIdx = asm.cpi();
|
||||
|
||||
// Boolean
|
||||
asm.emitConstantPoolUTF8("java/lang/Boolean");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
booleanIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(Z)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(Z)Ljava/lang/Boolean;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
booleanCtorIdx = asm.cpi();
|
||||
booleanBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("booleanValue");
|
||||
asm.emitConstantPoolUTF8("()Z");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -271,10 +278,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Byte");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
byteIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(B)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(B)Ljava/lang/Byte;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
byteCtorIdx = asm.cpi();
|
||||
byteBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("byteValue");
|
||||
asm.emitConstantPoolUTF8("()B");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -285,10 +292,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Character");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
characterIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(C)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(C)Ljava/lang/Character;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
characterCtorIdx = asm.cpi();
|
||||
characterBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("charValue");
|
||||
asm.emitConstantPoolUTF8("()C");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -299,10 +306,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Double");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
doubleIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(D)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(D)Ljava/lang/Double;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
doubleCtorIdx = asm.cpi();
|
||||
doubleBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("doubleValue");
|
||||
asm.emitConstantPoolUTF8("()D");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -313,10 +320,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Float");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
floatIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(F)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(F)Ljava/lang/Float;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
floatCtorIdx = asm.cpi();
|
||||
floatBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("floatValue");
|
||||
asm.emitConstantPoolUTF8("()F");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -327,10 +334,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Integer");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
integerIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(I)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(I)Ljava/lang/Integer;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
integerCtorIdx = asm.cpi();
|
||||
integerBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("intValue");
|
||||
asm.emitConstantPoolUTF8("()I");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -341,10 +348,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Long");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
longIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(J)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(J)Ljava/lang/Long;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
longCtorIdx = asm.cpi();
|
||||
longBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("longValue");
|
||||
asm.emitConstantPoolUTF8("()J");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -355,10 +362,10 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
asm.emitConstantPoolUTF8("java/lang/Short");
|
||||
asm.emitConstantPoolClass(asm.cpi());
|
||||
shortIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("(S)V");
|
||||
asm.emitConstantPoolNameAndType(initIdx, asm.cpi());
|
||||
asm.emitConstantPoolUTF8("(S)Ljava/lang/Short;");
|
||||
asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi());
|
||||
asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi());
|
||||
shortCtorIdx = asm.cpi();
|
||||
shortBoxIdx = asm.cpi();
|
||||
asm.emitConstantPoolUTF8("shortValue");
|
||||
asm.emitConstantPoolUTF8("()S");
|
||||
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
|
||||
@ -515,23 +522,23 @@ class AccessorGenerator implements ClassFileConstants {
|
||||
throw new InternalError("Should have found primitive type");
|
||||
}
|
||||
|
||||
protected short ctorIndexForPrimitiveType(Class<?> type) {
|
||||
protected short boxingMethodForPrimitiveType(Class<?> type) {
|
||||
if (type == Boolean.TYPE) {
|
||||
return booleanCtorIdx;
|
||||
return booleanBoxIdx;
|
||||
} else if (type == Byte.TYPE) {
|
||||
return byteCtorIdx;
|
||||
return byteBoxIdx;
|
||||
} else if (type == Character.TYPE) {
|
||||
return characterCtorIdx;
|
||||
return characterBoxIdx;
|
||||
} else if (type == Double.TYPE) {
|
||||
return doubleCtorIdx;
|
||||
return doubleBoxIdx;
|
||||
} else if (type == Float.TYPE) {
|
||||
return floatCtorIdx;
|
||||
return floatBoxIdx;
|
||||
} else if (type == Integer.TYPE) {
|
||||
return integerCtorIdx;
|
||||
return integerBoxIdx;
|
||||
} else if (type == Long.TYPE) {
|
||||
return longCtorIdx;
|
||||
return longBoxIdx;
|
||||
} else if (type == Short.TYPE) {
|
||||
return shortCtorIdx;
|
||||
return shortBoxIdx;
|
||||
}
|
||||
throw new InternalError("Should have found primitive type");
|
||||
}
|
||||
|
@ -660,9 +660,9 @@ class MethodAccessorGenerator extends AccessorGenerator {
|
||||
if (!isConstructor) {
|
||||
// Box return value if necessary
|
||||
if (isPrimitive(returnType)) {
|
||||
cb.opc_invokespecial(ctorIndexForPrimitiveType(returnType),
|
||||
typeSizeInStackSlots(returnType),
|
||||
0);
|
||||
cb.opc_invokestatic(boxingMethodForPrimitiveType(returnType),
|
||||
typeSizeInStackSlots(returnType),
|
||||
0);
|
||||
} else if (returnType == Void.TYPE) {
|
||||
cb.opc_aconst_null();
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class UnsafeBooleanFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Boolean(getBoolean(obj));
|
||||
return Boolean.valueOf(getBoolean(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeByteFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Byte(getByte(obj));
|
||||
return Byte.valueOf(getByte(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeCharacterFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Character(getChar(obj));
|
||||
return Character.valueOf(getChar(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeDoubleFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Double(getDouble(obj));
|
||||
return Double.valueOf(getDouble(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeFloatFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Float(getFloat(obj));
|
||||
return Float.valueOf(getFloat(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeIntegerFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Integer(getInt(obj));
|
||||
return Integer.valueOf(getInt(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeLongFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Long(getLong(obj));
|
||||
return Long.valueOf(getLong(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedBooleanFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Boolean(getBoolean(obj));
|
||||
return Boolean.valueOf(getBoolean(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedByteFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Byte(getByte(obj));
|
||||
return Byte.valueOf(getByte(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedCharacterFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Character(getChar(obj));
|
||||
return Character.valueOf(getChar(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedDoubleFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Double(getDouble(obj));
|
||||
return Double.valueOf(getDouble(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedFloatFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Float(getFloat(obj));
|
||||
return Float.valueOf(getFloat(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedIntegerFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Integer(getInt(obj));
|
||||
return Integer.valueOf(getInt(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedLongFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Long(getLong(obj));
|
||||
return Long.valueOf(getLong(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedShortFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Short(getShort(obj));
|
||||
return Short.valueOf(getShort(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticBooleanFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Boolean(getBoolean(obj));
|
||||
return Boolean.valueOf(getBoolean(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticByteFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Byte(getByte(obj));
|
||||
return Byte.valueOf(getByte(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticCharacterFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Character(getChar(obj));
|
||||
return Character.valueOf(getChar(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticDoubleFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Double(getDouble(obj));
|
||||
return Double.valueOf(getDouble(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticFloatFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Float(getFloat(obj));
|
||||
return Float.valueOf(getFloat(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticIntegerFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Integer(getInt(obj));
|
||||
return Integer.valueOf(getInt(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticLongFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Long(getLong(obj));
|
||||
return Long.valueOf(getLong(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -35,7 +35,7 @@ class UnsafeQualifiedStaticShortFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Short(getShort(obj));
|
||||
return Short.valueOf(getShort(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeShortFieldAccessorImpl extends UnsafeFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Short(getShort(obj));
|
||||
return Short.valueOf(getShort(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticBooleanFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Boolean(getBoolean(obj));
|
||||
return Boolean.valueOf(getBoolean(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticByteFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Byte(getByte(obj));
|
||||
return Byte.valueOf(getByte(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticCharacterFieldAccessorImpl extends UnsafeStaticFieldAccessorIm
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Character(getChar(obj));
|
||||
return Character.valueOf(getChar(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticDoubleFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Double(getDouble(obj));
|
||||
return Double.valueOf(getDouble(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticFloatFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Float(getFloat(obj));
|
||||
return Float.valueOf(getFloat(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticIntegerFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Integer(getInt(obj));
|
||||
return Integer.valueOf(getInt(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticLongFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Long(getLong(obj));
|
||||
return Long.valueOf(getLong(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -33,7 +33,7 @@ class UnsafeStaticShortFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl {
|
||||
}
|
||||
|
||||
public Object get(Object obj) throws IllegalArgumentException {
|
||||
return new Short(getShort(obj));
|
||||
return Short.valueOf(getShort(obj));
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object obj) throws IllegalArgumentException {
|
||||
|
@ -615,7 +615,9 @@ public final class ZoneInfoFile {
|
||||
// startTime=86400000 <= 24 hours
|
||||
// This: startDayOfWeek=6
|
||||
// startTime=0
|
||||
// Below is the workaround, it probably slows down everyone a little
|
||||
// Similar workaround needs to be applied to Africa/Cairo and
|
||||
// its endDayOfWeek and endTime
|
||||
// Below is the workarounds, it probably slows down everyone a little
|
||||
if (params[2] == 6 && params[3] == 0 &&
|
||||
(zoneId.equals("Asia/Amman") ||
|
||||
zoneId.equals("Asia/Gaza") ||
|
||||
@ -623,6 +625,13 @@ public final class ZoneInfoFile {
|
||||
params[2] = 5;
|
||||
params[3] = 86400000;
|
||||
}
|
||||
//endDayOfWeek and endTime workaround
|
||||
if (params[7] == 6 && params[8] == 0 &&
|
||||
(zoneId.equals("Africa/Cairo"))) {
|
||||
params[7] = 5;
|
||||
params[8] = 86400000;
|
||||
}
|
||||
|
||||
} else if (nTrans > 0) { // only do this if there is something in table already
|
||||
if (lastyear < LASTYEAR) {
|
||||
// ZoneInfo has an ending entry for 2037
|
||||
|
@ -47,9 +47,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String ACT[] = new String[] {"Acre Time", "ACT",
|
||||
"Acre Summer Time", "ACST",
|
||||
"Acre Time", "ACT"};
|
||||
String ADELAIDE[] = new String[] {"Central Standard Time (South Australia)", "CST",
|
||||
"Central Summer Time (South Australia)", "CST",
|
||||
"Central Time (South Australia)", "CT"};
|
||||
String ADELAIDE[] = new String[] {"Australian Central Standard Time (South Australia)", "ACST",
|
||||
"Australian Central Daylight Time (South Australia)", "ACDT",
|
||||
"Australian Central Time (South Australia)", "ACT"};
|
||||
String AGT[] = new String[] {"Argentine Time", "ART",
|
||||
"Argentine Summer Time", "ARST",
|
||||
"Argentine Time", "ART"};
|
||||
@ -71,12 +71,12 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String BDT[] = new String[] {"Bangladesh Time", "BDT",
|
||||
"Bangladesh Summer Time", "BDST",
|
||||
"Bangladesh Time", "BDT"};
|
||||
String BRISBANE[] = new String[] {"Eastern Standard Time (Queensland)", "EST",
|
||||
"Eastern Summer Time (Queensland)", "EST",
|
||||
"Eastern Time (Queensland)", "ET"};
|
||||
String BROKEN_HILL[] = new String[] {"Central Standard Time (South Australia/New South Wales)", "CST",
|
||||
"Central Summer Time (South Australia/New South Wales)", "CST",
|
||||
"Central Time (South Australia/New South Wales)", "CT"};
|
||||
String BRISBANE[] = new String[] {"Australian Eastern Standard Time (Queensland)", "AEST",
|
||||
"Australian Eastern Daylight Time (Queensland)", "AEDT",
|
||||
"Australian Eastern Time (Queensland)", "AET"};
|
||||
String BROKEN_HILL[] = new String[] {"Australian Central Standard Time (South Australia/New South Wales)", "ACST",
|
||||
"Australian Central Daylight Time (South Australia/New South Wales)", "ACDT",
|
||||
"Australian Central Time (South Australia/New South Wales)", "ACT"};
|
||||
String BRT[] = new String[] {"Brasilia Time", "BRT",
|
||||
"Brasilia Summer Time", "BRST",
|
||||
"Brasilia Time", "BRT"};
|
||||
@ -110,9 +110,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String CUBA[] = new String[] {"Cuba Standard Time", "CST",
|
||||
"Cuba Daylight Time", "CDT",
|
||||
"Cuba Time", "CT"};
|
||||
String DARWIN[] = new String[] {"Central Standard Time (Northern Territory)", "CST",
|
||||
"Central Summer Time (Northern Territory)", "CST",
|
||||
"Central Time (Northern Territory)", "CT"};
|
||||
String DARWIN[] = new String[] {"Australian Central Standard Time (Northern Territory)", "ACST",
|
||||
"Australian Central Daylight Time (Northern Territory)", "ACDT",
|
||||
"Australian Central Time (Northern Territory)", "ACT"};
|
||||
String DUBLIN[] = new String[] {"Greenwich Mean Time", "GMT",
|
||||
"Irish Summer Time", "IST",
|
||||
"Irish Time", "IT"};
|
||||
@ -131,9 +131,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String EST[] = new String[] {"Eastern Standard Time", "EST",
|
||||
"Eastern Daylight Time", "EDT",
|
||||
"Eastern Time", "ET"};
|
||||
String EST_NSW[] = new String[] {"Eastern Standard Time (New South Wales)", "EST",
|
||||
"Eastern Summer Time (New South Wales)", "EST",
|
||||
"Eastern Time (New South Wales)", "ET"};
|
||||
String EST_NSW[] = new String[] {"Australian Eastern Standard Time (New South Wales)", "AEST",
|
||||
"Australian Eastern Daylight Time (New South Wales)", "AEDT",
|
||||
"Australian Eastern Time (New South Wales)", "AET"};
|
||||
String FET[] = new String[] {"Further-eastern European Time", "FET",
|
||||
"Further-eastern European Summer Time", "FEST",
|
||||
"Further-eastern European Time", "FET"};
|
||||
@ -167,6 +167,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String IRT[] = new String[] {"Iran Standard Time", "IRST",
|
||||
"Iran Daylight Time", "IRDT",
|
||||
"Iran Time", "IRT"};
|
||||
String IRKT[] = new String[] {"Irkutsk Time", "IRKT",
|
||||
"Irkutsk Summer Time", "IRKST",
|
||||
"Irkutsk Time", "IRKT"};
|
||||
String ISRAEL[] = new String[] {"Israel Standard Time", "IST",
|
||||
"Israel Daylight Time", "IDT",
|
||||
"Israel Time", "IT"};
|
||||
@ -176,11 +179,14 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String JST[] = new String[] {"Japan Standard Time", "JST",
|
||||
"Japan Daylight Time", "JDT",
|
||||
"Japan Time", "JT"};
|
||||
String KRAT[] = new String[] {"Krasnoyarsk Time", "KRAT",
|
||||
"Krasnoyarsk Summer Time", "KRAST",
|
||||
"Krasnoyarsk Time", "KRAT"};
|
||||
String KST[] = new String[] {"Korea Standard Time", "KST",
|
||||
"Korea Daylight Time", "KDT",
|
||||
"Korea Time", "KT"};
|
||||
String LORD_HOWE[] = new String[] {"Lord Howe Standard Time", "LHST",
|
||||
"Lord Howe Summer Time", "LHST",
|
||||
"Lord Howe Daylight Time", "LHDT",
|
||||
"Lord Howe Time", "LHT"};
|
||||
String MHT[] = new String[] {"Marshall Islands Time", "MHT",
|
||||
"Marshall Islands Summer Time", "MHST",
|
||||
@ -230,21 +236,15 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String SGT[] = new String[] {"Singapore Time", "SGT",
|
||||
"Singapore Summer Time", "SGST",
|
||||
"Singapore Time", "SGT"};
|
||||
String SLST[] = new String[] {"Greenwich Mean Time", "GMT",
|
||||
"Sierra Leone Summer Time", "SLST",
|
||||
"Sierra Leone Time", "SLT"};
|
||||
String TASMANIA[] = new String[] {"Eastern Standard Time (Tasmania)", "EST",
|
||||
"Eastern Summer Time (Tasmania)", "EST",
|
||||
"Eastern Time (Tasmania)", "ET"};
|
||||
String TASMANIA[] = new String[] {"Australian Eastern Standard Time (Tasmania)", "AEST",
|
||||
"Australian Eastern Daylight Time (Tasmania)", "AEDT",
|
||||
"Australian Eastern Time (Tasmania)", "AET"};
|
||||
String TMT[] = new String[] {"Turkmenistan Time", "TMT",
|
||||
"Turkmenistan Summer Time", "TMST",
|
||||
"Turkmenistan Time", "TMT"};
|
||||
String ULAT[]= new String[] {"Ulaanbaatar Time", "ULAT",
|
||||
"Ulaanbaatar Summer Time", "ULAST",
|
||||
"Ulaanbaatar Time", "ULAT"};
|
||||
String WART[] = new String[] {"Western Argentine Time", "WART",
|
||||
"Western Argentine Summer Time", "WARST",
|
||||
"Western Argentine Time", "WART"};
|
||||
String WAT[] = new String[] {"Western African Time", "WAT",
|
||||
"Western African Summer Time", "WAST",
|
||||
"Western African Time", "WAT"};
|
||||
@ -254,27 +254,30 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
String WIT[] = new String[] {"West Indonesia Time", "WIB",
|
||||
"West Indonesia Summer Time", "WIST",
|
||||
"West Indonesia Time", "WIB"};
|
||||
String WST_AUS[] = new String[] {"Western Standard Time (Australia)", "WST",
|
||||
"Western Summer Time (Australia)", "WST",
|
||||
"Western Time (Australia)", "WT"};
|
||||
String WST_AUS[] = new String[] {"Australian Western Standard Time", "AWST",
|
||||
"Australian Western Daylight Time", "AWDT",
|
||||
"Australian Western Time", "AWT"};
|
||||
String SAMOA[] = new String[] {"Samoa Standard Time", "SST",
|
||||
"Samoa Daylight Time", "SDT",
|
||||
"Samoa Time", "ST"};
|
||||
String WST_SAMOA[] = new String[] {"West Samoa Time", "WST",
|
||||
String WST_SAMOA[] = new String[] {"West Samoa Standard Time", "WSST",
|
||||
"West Samoa Daylight Time", "WSDT",
|
||||
"West Samoa Time", "WST"};
|
||||
String ChST[] = new String[] {"Chamorro Standard Time", "ChST",
|
||||
"Chamorro Daylight Time", "ChDT",
|
||||
"Chamorro Time", "ChT"};
|
||||
String VICTORIA[] = new String[] {"Eastern Standard Time (Victoria)", "EST",
|
||||
"Eastern Summer Time (Victoria)", "EST",
|
||||
"Eastern Time (Victoria)", "ET"};
|
||||
String VICTORIA[] = new String[] {"Australian Eastern Standard Time (Victoria)", "AEST",
|
||||
"Australian Eastern Daylight Time (Victoria)", "AEDT",
|
||||
"Australian Eastern Time (Victoria)", "AET"};
|
||||
String UTC[] = new String[] {"Coordinated Universal Time", "UTC",
|
||||
"Coordinated Universal Time", "UTC",
|
||||
"Coordinated Universal Time", "UTC"};
|
||||
String UZT[] = new String[] {"Uzbekistan Time", "UZT",
|
||||
"Uzbekistan Summer Time", "UZST",
|
||||
"Uzbekistan Time", "UZT"};
|
||||
String XJT[] = new String[] {"Xinjiang Standard Time", "XJT",
|
||||
"Xinjiang Daylight Time", "XJDT",
|
||||
"Xinjiang Time", "XJT"};
|
||||
|
||||
return new Object[][] {
|
||||
{"America/Los_Angeles", PST},
|
||||
@ -336,7 +339,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Africa/Djibouti", EAT},
|
||||
{"Africa/Douala", WAT},
|
||||
{"Africa/El_Aaiun", WET},
|
||||
{"Africa/Freetown", SLST},
|
||||
{"Africa/Freetown", GMT},
|
||||
{"Africa/Gaborone", CAT},
|
||||
{"Africa/Harare", CAT},
|
||||
{"Africa/Johannesburg", SAST},
|
||||
@ -437,7 +440,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
"Western Greenland Summer Time", "WGST",
|
||||
"Western Greenland Time", "WGT"}},
|
||||
{"America/Goose_Bay", AST},
|
||||
{"America/Grand_Turk", EST},
|
||||
{"America/Grand_Turk", AST},
|
||||
{"America/Grenada", AST},
|
||||
{"America/Guadeloupe", AST},
|
||||
{"America/Guatemala", CST},
|
||||
@ -484,9 +487,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"America/Mendoza", AGT},
|
||||
{"America/Menominee", CST},
|
||||
{"America/Merida", CST},
|
||||
{"America/Metlakatla", new String[] {"Metlakatla Standard Time", "MeST",
|
||||
"Metlakatla Daylight Time", "MeDT",
|
||||
"Metlakatla Time", "MeT"}},
|
||||
{"America/Metlakatla", PST},
|
||||
{"America/Mexico_City", CST},
|
||||
{"America/Miquelon", new String[] {"Pierre & Miquelon Standard Time", "PMST",
|
||||
"Pierre & Miquelon Daylight Time", "PMDT",
|
||||
@ -555,8 +556,8 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Antarctica/DumontDUrville", new String[] {"Dumont-d'Urville Time", "DDUT",
|
||||
"Dumont-d'Urville Summer Time", "DDUST",
|
||||
"Dumont-d'Urville Time", "DDUT"}},
|
||||
{"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
|
||||
"Macquarie Island Summer Time", "MIST",
|
||||
{"Antarctica/Macquarie", new String[] {"Macquarie Island Standard Time", "MIST",
|
||||
"Macquarie Island Daylight Time", "MIDT",
|
||||
"Macquarie Island Time", "MIST"}},
|
||||
{"Antarctica/Mawson", new String[] {"Mawson Time", "MAWT",
|
||||
"Mawson Summer Time", "MAWST",
|
||||
@ -607,6 +608,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
"Brunei Summer Time", "BNST",
|
||||
"Brunei Time", "BNT"}},
|
||||
{"Asia/Calcutta", IST},
|
||||
{"Asia/Chita", IRKT},
|
||||
{"Asia/Choibalsan", new String[] {"Choibalsan Time", "CHOT",
|
||||
"Choibalsan Summer Time", "CHOST",
|
||||
"Choibalsan Time", "CHOT"}},
|
||||
@ -631,9 +633,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Asia/Hovd", new String[] {"Hovd Time", "HOVT",
|
||||
"Hovd Summer Time", "HOVST",
|
||||
"Hovd Time", "HOVT"}},
|
||||
{"Asia/Irkutsk", new String[] {"Irkutsk Time", "IRKT",
|
||||
"Irkutsk Summer Time", "IRKST",
|
||||
"Irkutsk Time", "IRKT"}},
|
||||
{"Asia/Irkutsk", IRKT},
|
||||
{"Asia/Istanbul", EET},
|
||||
{"Asia/Jakarta", WIT},
|
||||
{"Asia/Jayapura", new String[] {"East Indonesia Time", "WIT",
|
||||
@ -646,7 +646,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
"Petropavlovsk-Kamchatski Summer Time", "PETST",
|
||||
"Petropavlovsk-Kamchatski Time", "PETT"}},
|
||||
{"Asia/Karachi", PKT},
|
||||
{"Asia/Kashgar", CTT},
|
||||
{"Asia/Kashgar", XJT},
|
||||
{"Asia/Kathmandu", NPT},
|
||||
{"Asia/Katmandu", NPT},
|
||||
{"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT",
|
||||
@ -654,9 +654,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
"Khandyga Time", "YAKT"}},
|
||||
|
||||
{"Asia/Kolkata", IST},
|
||||
{"Asia/Krasnoyarsk", new String[] {"Krasnoyarsk Time", "KRAT",
|
||||
"Krasnoyarsk Summer Time", "KRAST",
|
||||
"Krasnoyarsk Time", "KRAT"}},
|
||||
{"Asia/Krasnoyarsk", KRAT},
|
||||
{"Asia/Kuala_Lumpur", MYT},
|
||||
{"Asia/Kuching", MYT},
|
||||
{"Asia/Kuwait", ARAST},
|
||||
@ -671,7 +669,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
"Philippines Time", "PHT"}},
|
||||
{"Asia/Muscat", GST},
|
||||
{"Asia/Nicosia", EET},
|
||||
{"Asia/Novokuznetsk", NOVT},
|
||||
{"Asia/Novokuznetsk", KRAT},
|
||||
{"Asia/Novosibirsk", NOVT},
|
||||
{"Asia/Oral", new String[] {"Oral Time", "ORAT",
|
||||
"Oral Summer Time", "ORAST",
|
||||
@ -697,6 +695,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Asia/Samarkand", UZT},
|
||||
{"Asia/Seoul", KST},
|
||||
{"Asia/Singapore", SGT},
|
||||
{"Asia/Srednekolymsk", new String[] {"Srednekolymsk Time", "SRET",
|
||||
"Srednekolymsk Daylight Time", "SREDT",
|
||||
"Srednekolymsk Time", "SRET"}},
|
||||
{"Asia/Taipei", CTT},
|
||||
{"Asia/Tel_Aviv", ISRAEL},
|
||||
{"Asia/Tashkent", UZT},
|
||||
@ -709,7 +710,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Asia/Ujung_Pandang", CIT},
|
||||
{"Asia/Ulaanbaatar", ULAT},
|
||||
{"Asia/Ulan_Bator", ULAT},
|
||||
{"Asia/Urumqi", CTT},
|
||||
{"Asia/Urumqi", XJT},
|
||||
{"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT",
|
||||
"Ust-Nera Summer Time", "VLAST",
|
||||
"Ust-Nera Time", "VLAT"}},
|
||||
@ -751,9 +752,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Australia/Canberra", EST_NSW},
|
||||
{"Australia/Currie", EST_NSW},
|
||||
{"Australia/Darwin", DARWIN},
|
||||
{"Australia/Eucla", new String[] {"Central Western Standard Time (Australia)", "CWST",
|
||||
"Central Western Summer Time (Australia)", "CWST",
|
||||
"Central Western Time (Australia)", "CWT"}},
|
||||
{"Australia/Eucla", new String[] {"Australian Central Western Standard Time", "ACWST",
|
||||
"Australian Central Western Daylight Time", "ACWDT",
|
||||
"Australian Central Western Time", "ACWT"}},
|
||||
{"Australia/Hobart", TASMANIA},
|
||||
{"Australia/LHI", LORD_HOWE},
|
||||
{"Australia/Lindeman", BRISBANE},
|
||||
@ -819,7 +820,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Europe/Isle_of_Man", GMTBST},
|
||||
{"Europe/Istanbul", EET},
|
||||
{"Europe/Jersey", GMTBST},
|
||||
{"Europe/Kaliningrad", FET},
|
||||
{"Europe/Kaliningrad", EET},
|
||||
{"Europe/Kiev", EET},
|
||||
{"Europe/Lisbon", WET},
|
||||
{"Europe/Ljubljana", CET},
|
||||
@ -854,9 +855,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle {
|
||||
{"Europe/Vatican", CET},
|
||||
{"Europe/Vienna", CET},
|
||||
{"Europe/Vilnius", EET},
|
||||
{"Europe/Volgograd", new String[] {"Volgograd Time", "VOLT",
|
||||
"Volgograd Summer Time", "VOLST",
|
||||
"Volgograd Time", "VOLT"}},
|
||||
{"Europe/Volgograd", MSK},
|
||||
{"Europe/Warsaw", CET},
|
||||
{"Europe/Zagreb", CET},
|
||||
{"Europe/Zaporozhye", EET},
|
||||
|
@ -36,6 +36,17 @@ import sun.misc.Unsafe;
|
||||
class WindowsNativeDispatcher {
|
||||
private WindowsNativeDispatcher() { }
|
||||
|
||||
/**
|
||||
* HANDLE CreateEvent(
|
||||
* LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
* BOOL bManualReset,
|
||||
* BOOL bInitialState,
|
||||
* PCTSTR lpName
|
||||
* );
|
||||
*/
|
||||
static native long CreateEvent(boolean bManualReset, boolean bInitialState)
|
||||
throws WindowsException;
|
||||
|
||||
/**
|
||||
* HANDLE CreateFile(
|
||||
* LPCTSTR lpFileName,
|
||||
@ -1041,6 +1052,25 @@ class WindowsNativeDispatcher {
|
||||
long pOverlapped)
|
||||
throws WindowsException;
|
||||
|
||||
|
||||
/**
|
||||
* CancelIo(
|
||||
* HANDLE hFile
|
||||
* )
|
||||
*/
|
||||
static native void CancelIo(long hFile) throws WindowsException;
|
||||
|
||||
/**
|
||||
* GetOverlappedResult(
|
||||
* HANDLE hFile,
|
||||
* LPOVERLAPPED lpOverlapped,
|
||||
* LPDWORD lpNumberOfBytesTransferred,
|
||||
* BOOL bWait
|
||||
* );
|
||||
*/
|
||||
static native int GetOverlappedResult(long hFile, long lpOverlapped)
|
||||
throws WindowsException;
|
||||
|
||||
/**
|
||||
* BackupRead(
|
||||
* HANDLE hFile,
|
||||
|
@ -25,9 +25,16 @@
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sun.nio.file.ExtendedWatchEventModifier;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
@ -42,7 +49,6 @@ class WindowsWatchService
|
||||
extends AbstractWatchService
|
||||
{
|
||||
private final static int WAKEUP_COMPLETION_KEY = 0;
|
||||
private final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
// background thread to service I/O completion port
|
||||
private final Poller poller;
|
||||
@ -82,7 +88,7 @@ class WindowsWatchService
|
||||
/**
|
||||
* Windows implementation of WatchKey.
|
||||
*/
|
||||
private class WindowsWatchKey extends AbstractWatchKey {
|
||||
private static class WindowsWatchKey extends AbstractWatchKey {
|
||||
// file key (used to detect existing registrations)
|
||||
private final FileKey fileKey;
|
||||
|
||||
@ -169,15 +175,9 @@ class WindowsWatchService
|
||||
return completionKey;
|
||||
}
|
||||
|
||||
// close directory and release buffer
|
||||
void releaseResources() {
|
||||
CloseHandle(handle);
|
||||
buffer.cleaner().clean();
|
||||
}
|
||||
|
||||
// Invalidate key by closing directory and releasing buffer
|
||||
// Invalidate the key, assumes that resources have been released
|
||||
void invalidate() {
|
||||
releaseResources();
|
||||
((WindowsWatchService)watcher()).poller.releaseResources(this);
|
||||
handle = INVALID_HANDLE_VALUE;
|
||||
buffer = null;
|
||||
countAddress = 0;
|
||||
@ -193,7 +193,7 @@ class WindowsWatchService
|
||||
public void cancel() {
|
||||
if (isValid()) {
|
||||
// delegate to poller
|
||||
poller.cancel(this);
|
||||
((WindowsWatchService)watcher()).poller.cancel(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -241,18 +241,25 @@ class WindowsWatchService
|
||||
/**
|
||||
* Background thread to service I/O completion port.
|
||||
*/
|
||||
private class Poller extends AbstractPoller {
|
||||
private static class Poller extends AbstractPoller {
|
||||
private final static Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
/*
|
||||
* typedef struct _OVERLAPPED {
|
||||
* DWORD Internal;
|
||||
* DWORD InternalHigh;
|
||||
* DWORD Offset;
|
||||
* DWORD OffsetHigh;
|
||||
* HANDLE hEvent;
|
||||
* ULONG_PTR Internal;
|
||||
* ULONG_PTR InternalHigh;
|
||||
* union {
|
||||
* struct { DWORD Offset; DWORD OffsetHigh; };
|
||||
* PVOID Pointer;
|
||||
* };
|
||||
* HANDLE hEvent;
|
||||
* } OVERLAPPED;
|
||||
*/
|
||||
private static final short SIZEOF_DWORD = 4;
|
||||
private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit
|
||||
private static final short OFFSETOF_HEVENT =
|
||||
(UNSAFE.addressSize() == 4) ? (short) 16 : 24;
|
||||
|
||||
|
||||
/*
|
||||
* typedef struct _FILE_NOTIFY_INFORMATION {
|
||||
@ -276,10 +283,10 @@ class WindowsWatchService
|
||||
private final long port;
|
||||
|
||||
// maps completion key to WatchKey
|
||||
private final Map<Integer,WindowsWatchKey> ck2key;
|
||||
private final Map<Integer, WindowsWatchKey> ck2key;
|
||||
|
||||
// maps file key to WatchKey
|
||||
private final Map<FileKey,WindowsWatchKey> fk2key;
|
||||
private final Map<FileKey, WindowsWatchKey> fk2key;
|
||||
|
||||
// unique completion key for each directory
|
||||
// native completion key capacity is 64 bits on Win64.
|
||||
@ -393,8 +400,13 @@ class WindowsWatchService
|
||||
long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED;
|
||||
long countAddress = overlappedAddress - SIZEOF_DWORD;
|
||||
|
||||
// zero the overlapped structure
|
||||
UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0);
|
||||
|
||||
// start async read of changes to directory
|
||||
try {
|
||||
createAndAttachEvent(overlappedAddress);
|
||||
|
||||
ReadDirectoryChangesW(handle,
|
||||
bufferAddress,
|
||||
CHANGES_BUFFER_SIZE,
|
||||
@ -403,6 +415,7 @@ class WindowsWatchService
|
||||
countAddress,
|
||||
overlappedAddress);
|
||||
} catch (WindowsException x) {
|
||||
closeAttachedEvent(overlappedAddress);
|
||||
buffer.release();
|
||||
return new IOException(x.getMessage());
|
||||
}
|
||||
@ -421,7 +434,7 @@ class WindowsWatchService
|
||||
// 2. release existing key's resources (handle/buffer)
|
||||
// 3. re-initialize key with new handle/buffer
|
||||
ck2key.remove(existing.completionKey());
|
||||
existing.releaseResources();
|
||||
releaseResources(existing);
|
||||
watchKey = existing.init(handle, events, watchSubtree, buffer,
|
||||
countAddress, overlappedAddress, completionKey);
|
||||
}
|
||||
@ -436,6 +449,42 @@ class WindowsWatchService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the outstanding I/O operation on the directory
|
||||
* associated with the given key and releases the associated
|
||||
* resources.
|
||||
*/
|
||||
private void releaseResources(WindowsWatchKey key) {
|
||||
try {
|
||||
CancelIo(key.handle());
|
||||
GetOverlappedResult(key.handle(), key.overlappedAddress());
|
||||
} catch (WindowsException expected) {
|
||||
// expected as I/O operation has been cancelled
|
||||
}
|
||||
CloseHandle(key.handle());
|
||||
closeAttachedEvent(key.overlappedAddress());
|
||||
key.buffer().cleaner().clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unnamed event and set it as the hEvent field
|
||||
* in the given OVERLAPPED structure
|
||||
*/
|
||||
private void createAndAttachEvent(long ov) throws WindowsException {
|
||||
long hEvent = CreateEvent(false, false);
|
||||
UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the event attached to the given OVERLAPPED structure. A
|
||||
* no-op if there isn't an event attached.
|
||||
*/
|
||||
private void closeAttachedEvent(long ov) {
|
||||
long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT);
|
||||
if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hEvent);
|
||||
}
|
||||
|
||||
// cancel single key
|
||||
@Override
|
||||
void implCancelKey(WatchKey obj) {
|
||||
@ -451,9 +500,8 @@ class WindowsWatchService
|
||||
@Override
|
||||
void implCloseAll() {
|
||||
// cancel all keys
|
||||
for (Map.Entry<Integer, WindowsWatchKey> entry: ck2key.entrySet()) {
|
||||
entry.getValue().invalidate();
|
||||
}
|
||||
ck2key.values().forEach(WindowsWatchKey::invalidate);
|
||||
|
||||
fk2key.clear();
|
||||
ck2key.clear();
|
||||
|
||||
@ -462,8 +510,7 @@ class WindowsWatchService
|
||||
}
|
||||
|
||||
// Translate file change action into watch event
|
||||
private WatchEvent.Kind<?> translateActionToEvent(int action)
|
||||
{
|
||||
private WatchEvent.Kind<?> translateActionToEvent(int action) {
|
||||
switch (action) {
|
||||
case FILE_ACTION_MODIFIED :
|
||||
return StandardWatchEventKinds.ENTRY_MODIFY;
|
||||
@ -487,18 +534,18 @@ class WindowsWatchService
|
||||
|
||||
int nextOffset;
|
||||
do {
|
||||
int action = unsafe.getInt(address + OFFSETOF_ACTION);
|
||||
int action = UNSAFE.getInt(address + OFFSETOF_ACTION);
|
||||
|
||||
// map action to event
|
||||
WatchEvent.Kind<?> kind = translateActionToEvent(action);
|
||||
if (key.events().contains(kind)) {
|
||||
// copy the name
|
||||
int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FILENAMELENGTH);
|
||||
int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH);
|
||||
if ((nameLengthInBytes % 2) != 0) {
|
||||
throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2");
|
||||
throw new AssertionError("FileNameLength is not a multiple of 2");
|
||||
}
|
||||
char[] nameAsArray = new char[nameLengthInBytes/2];
|
||||
unsafe.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,
|
||||
UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,
|
||||
Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
|
||||
|
||||
// create FileName and queue event
|
||||
@ -508,7 +555,7 @@ class WindowsWatchService
|
||||
}
|
||||
|
||||
// next event
|
||||
nextOffset = unsafe.getInt(address + OFFSETOF_NEXTENTRYOFFSET);
|
||||
nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET);
|
||||
address += (long)nextOffset;
|
||||
} while (nextOffset != 0);
|
||||
}
|
||||
|
@ -493,6 +493,9 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* NET_BindV6() closes both sockets upon a failure */
|
||||
(*env)->SetObjectField(env, this, pdsi_fdID, NULL);
|
||||
(*env)->SetObjectField(env, this, pdsi_fd1ID, NULL);
|
||||
NET_ThrowCurrent (env, "Cannot bind");
|
||||
return;
|
||||
}
|
||||
|
@ -467,6 +467,10 @@ Java_java_net_TwoStacksPlainSocketImpl_socketBind(JNIEnv *env, jobject this,
|
||||
(*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* NET_BindV6() closes both sockets upon a failure */
|
||||
(*env)->SetObjectField(env, this, psi_fdID, NULL);
|
||||
(*env)->SetObjectField(env, this, psi_fd1ID, NULL);
|
||||
}
|
||||
} else {
|
||||
rv = NET_WinBind(fd, (struct sockaddr *)&him, len, exclBind);
|
||||
|
@ -627,7 +627,7 @@ void dumpAddr (char *str, void *addr) {
|
||||
* and returns SOCKET_ERROR. Used in NET_BindV6 only.
|
||||
*/
|
||||
|
||||
#define CLOSE_SOCKETS_AND_RETURN { \
|
||||
#define CLOSE_SOCKETS_AND_RETURN do { \
|
||||
if (fd != -1) { \
|
||||
closesocket (fd); \
|
||||
fd = -1; \
|
||||
@ -646,7 +646,7 @@ void dumpAddr (char *str, void *addr) {
|
||||
} \
|
||||
b->ipv4_fd = b->ipv6_fd = -1; \
|
||||
return SOCKET_ERROR; \
|
||||
}
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* if ipv6 is available, call NET_BindV6 to bind to the required address/port.
|
||||
|
@ -195,6 +195,17 @@ Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_nio_fs_WindowsNativeDispatcher_CreateEvent(JNIEnv* env, jclass this,
|
||||
jboolean bManualReset, jboolean bInitialState)
|
||||
{
|
||||
HANDLE hEvent = CreateEventW(NULL, bManualReset, bInitialState, NULL);
|
||||
if (hEvent == NULL) {
|
||||
throwWindowsException(env, GetLastError());
|
||||
}
|
||||
return ptr_to_jlong(hEvent);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_sun_nio_fs_WindowsNativeDispatcher_FormatMessage(JNIEnv* env, jclass this, jint errorCode) {
|
||||
WCHAR message[255];
|
||||
@ -1229,6 +1240,31 @@ Java_sun_nio_fs_WindowsNativeDispatcher_PostQueuedCompletionStatus(JNIEnv* env,
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_WindowsNativeDispatcher_CancelIo(JNIEnv* env, jclass this, jlong hFile) {
|
||||
if (CancelIo((HANDLE)jlong_to_ptr(hFile)) == 0) {
|
||||
throwWindowsException(env, GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_WindowsNativeDispatcher_GetOverlappedResult(JNIEnv *env, jclass this,
|
||||
jlong hFile, jlong lpOverlapped)
|
||||
{
|
||||
BOOL res;
|
||||
DWORD bytesTransferred = -1;
|
||||
|
||||
res = GetOverlappedResult((HANDLE)jlong_to_ptr(hFile),
|
||||
(LPOVERLAPPED)jlong_to_ptr(lpOverlapped),
|
||||
&bytesTransferred,
|
||||
TRUE);
|
||||
if (res == 0) {
|
||||
throwWindowsException(env, GetLastError());
|
||||
}
|
||||
|
||||
return (jint)bytesTransferred;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_WindowsNativeDispatcher_ReadDirectoryChangesW(JNIEnv* env, jclass this,
|
||||
jlong hDirectory, jlong bufferAddress, jint bufferLength, jboolean watchSubTree, jint filter,
|
||||
@ -1236,17 +1272,7 @@ Java_sun_nio_fs_WindowsNativeDispatcher_ReadDirectoryChangesW(JNIEnv* env, jclas
|
||||
{
|
||||
BOOL res;
|
||||
BOOL subtree = (watchSubTree == JNI_TRUE) ? TRUE : FALSE;
|
||||
|
||||
/* Any unused members of [OVERLAPPED] structure should always be initialized to zero
|
||||
before the structure is used in a function call.
|
||||
Otherwise, the function may fail and return ERROR_INVALID_PARAMETER.
|
||||
http://msdn.microsoft.com/en-us/library/windows/desktop/ms684342%28v=vs.85%29.aspx
|
||||
|
||||
The [Offset] and [OffsetHigh] members of this structure are not used.
|
||||
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v=vs.85%29.aspx
|
||||
|
||||
[hEvent] should be zero, other fields are the return values. */
|
||||
ZeroMemory((LPOVERLAPPED)jlong_to_ptr(pOverlapped), sizeof(OVERLAPPED));
|
||||
LPOVERLAPPED ov = (LPOVERLAPPED)jlong_to_ptr(pOverlapped);
|
||||
|
||||
res = ReadDirectoryChangesW((HANDLE)jlong_to_ptr(hDirectory),
|
||||
(LPVOID)jlong_to_ptr(bufferAddress),
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2014, 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
|
||||
@ -1313,11 +1313,11 @@ public class MLet extends java.net.URLClassLoader
|
||||
if (type.compareTo("java.lang.Long") == 0)
|
||||
return Long.valueOf(param);
|
||||
if (type.compareTo("java.lang.Integer") == 0)
|
||||
return param;
|
||||
return Integer.valueOf(param);
|
||||
if (type.compareTo("java.lang.Float") == 0)
|
||||
return new Float(param);
|
||||
return Float.valueOf(param);
|
||||
if (type.compareTo("java.lang.Double") == 0)
|
||||
return new Double(param);
|
||||
return Double.valueOf(param);
|
||||
if (type.compareTo("java.lang.String") == 0)
|
||||
return param;
|
||||
|
||||
|
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