This commit is contained in:
Lana Steuck 2014-09-11 14:29:21 -07:00
commit cda19943f0
161 changed files with 13378 additions and 10570 deletions
jdk
.hgignore
make
src
java.base
share/classes
java
sun
windows
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)

@ -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 "&lt;init&gt;"}.
* 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>

@ -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