--- old/./.hgignore 2019-03-22 01:07:54.332897528 +0100 +++ new/./.hgignore 2019-03-22 01:07:54.176897884 +0100 @@ -11,6 +11,6 @@ test/nashorn/script/external test/nashorn/lib NashornProfile.txt -.*/JTreport/.* -.*/JTwork/.* +JTreport/ +JTwork/ .*/.git/.* --- old/./.hgtags 2019-03-22 01:07:54.520897096 +0100 +++ new/./.hgtags 2019-03-22 01:07:54.372897434 +0100 @@ -547,5 +547,3 @@ c081f3ea6b9300265a4a34e38f970b1e3ddaae9f jdk-13+9 b67884871b5fff79c5ef3eb8ac74dd48d71ea9b1 jdk-12+33 8e069f7b4fabfe05d9f500783e6d56cb0196d25c jdk-13+10 -21ea4076a275a0f498afa517e9ee1b94a9cf0255 jdk-13+11 -1d7aec80147a6d92b101a76aef92f3ddc88bedf4 jdk-13+12 --- old/doc/building.html 2019-03-22 01:07:54.732896615 +0100 +++ new/doc/building.html 2019-03-22 01:07:54.564896998 +0100 @@ -237,7 +237,7 @@

For rpm-based distributions (Fedora, Red Hat, etc), try this:

sudo yum groupinstall "Development Tools"

AIX

-

Please consult the AIX section of the Supported Build Platforms OpenJDK Build Wiki page for details about which versions of AIX are supported.

+

The regular builds by SAP is using AIX version 7.1, but AIX 5.3 is also supported. See the OpenJDK PowerPC Port Status Page for details.

Native Compiler (Toolchain) Requirements

Large portions of the JDK consists of native code, that needs to be compiled to be able to run on the target platform. In theory, toolchain and operating system should be independent factors, but in practice there's more or less a one-to-one correlation between target operating system and toolchain.

@@ -373,7 +373,8 @@

If you have multiple versions of Visual Studio installed, configure will by default pick the latest. You can request a specific version to be used by setting --with-toolchain-version, e.g. --with-toolchain-version=2015.

If you get LINK: fatal error LNK1123: failure during conversion to COFF: file invalid when building using Visual Studio 2010, you have encountered KB2757355, a bug triggered by a specific installation order. However, the solution suggested by the KB article does not always resolve the problem. See this stackoverflow discussion for other suggestions.

IBM XL C/C++

-

Please consult the AIX section of the Supported Build Platforms OpenJDK Build Wiki page for details about which versions of XLC are supported.

+

The regular builds by SAP is using version 12.1, described as IBM XL C/C++ for AIX, V12.1 (5765-J02, 5725-C72) Version: 12.01.0000.0017.

+

See the OpenJDK PowerPC Port Status Page for details.

Boot JDK Requirements

Paradoxically, building the JDK requires a pre-existing JDK. This is called the "boot JDK". The boot JDK does not, however, have to be a JDK built directly from the source code available in the OpenJDK Community. If you are porting the JDK to a new platform, chances are that there already exists another JDK for that platform that is usable as boot JDK.

The rule of thumb is that the boot JDK for building JDK major version N should be a JDK of major version N-1, so for building JDK 9 a JDK 8 would be suitable as boot JDK. However, the JDK should be able to "build itself", so an up-to-date build of the current JDK source is an acceptable alternative. If you are following the N-1 rule, make sure you've got the latest update version, since JDK 8 GA might not be able to build JDK 9 on all platforms.

--- old/doc/building.md 2019-03-22 01:07:54.936896148 +0100 +++ new/doc/building.md 2019-03-22 01:07:54.788896486 +0100 @@ -295,9 +295,9 @@ ### AIX -Please consult the AIX section of the [Supported Build Platforms]( -https://wiki.openjdk.java.net/display/Build/Supported+Build+Platforms) OpenJDK -Build Wiki page for details about which versions of AIX are supported. +The regular builds by SAP is using AIX version 7.1, but AIX 5.3 is also +supported. See the [OpenJDK PowerPC Port Status Page]( +http://cr.openjdk.java.net/~simonis/ppc-aix-port) for details. ## Native Compiler (Toolchain) Requirements @@ -419,10 +419,11 @@ ### IBM XL C/C++ -Please consult the AIX section of the [Supported Build Platforms]( -https://wiki.openjdk.java.net/display/Build/Supported+Build+Platforms) OpenJDK -Build Wiki page for details about which versions of XLC are supported. +The regular builds by SAP is using version 12.1, described as `IBM XL C/C++ for +AIX, V12.1 (5765-J02, 5725-C72) Version: 12.01.0000.0017`. +See the [OpenJDK PowerPC Port Status Page]( +http://cr.openjdk.java.net/~simonis/ppc-aix-port) for details. ## Boot JDK Requirements --- old/doc/testing.html 2019-03-22 01:07:55.140895686 +0100 +++ new/doc/testing.html 2019-03-22 01:07:54.996896014 +0100 @@ -1,19 +1,24 @@ - + - - - + + + Testing the JDK - - + + -
+

Testing the JDK

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Permission Target NameWhat the Permission AllowsRisks of Allowing this Permission
setHostnameVerifierThe ability to set a callback which can decide whether to + * allow a mismatch between the host being connected to by + * an HttpsURLConnection and the common name field in + * server certificate. + * Malicious + * code can set a verifier that monitors host names visited by + * HttpsURLConnection requests or that allows server certificates + * with invalid common names. + *
getSSLSessionContextThe ability to get the SSLSessionContext of an SSLSession. + * Malicious code may monitor sessions which have been established + * with SSL peers or might invalidate sessions to slow down performance. + *
+ * + * @see java.security.BasicPermission + * @see java.security.Permission + * @see java.security.Permissions + * @see java.security.PermissionCollection + * @see java.lang.SecurityManager + * + * + * @author Marianne Mueller + * @author Roland Schemers + * + * @deprecated As of JDK 1.4, this implementation-specific class was + * replaced by {@link javax.net.ssl.SSLPermission}. + */ +@Deprecated(since="1.4") +public final class SSLPermission extends BasicPermission { + + private static final long serialVersionUID = -2583684302506167542L; + + /** + * Creates a new SSLPermission with the specified name. + * The name is the symbolic name of the SSLPermission, such as + * "setDefaultAuthenticator", etc. An asterisk + * may appear at the end of the name, following a ".", or by itself, to + * signify a wildcard match. + * + * @param name the name of the SSLPermission. + */ + + public SSLPermission(String name) + { + super(name); + } + + /** + * Creates a new SSLPermission object with the specified name. + * The name is the symbolic name of the SSLPermission, and the + * actions String is currently unused and should be null. This + * constructor exists for use by the Policy object + * to instantiate new Permission objects. + * + * @param name the name of the SSLPermission. + * @param actions should be null. + */ + + public SSLPermission(String name, String actions) + { + super(name, actions); + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/SSLSecurity.java 2019-03-22 01:09:18.648705529 +0100 @@ -0,0 +1,699 @@ +/* + * Copyright (c) 2000, 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. + */ + +/* + * NOTE: this file was copied from javax.net.ssl.SSLSecurity, + * but was heavily modified to allow com.sun.* users to + * access providers written using the javax.sun.* APIs. + */ + +package com.sun.net.ssl; + +import java.util.*; +import java.io.*; +import java.security.*; +import java.security.Provider.Service; +import java.net.Socket; + +import sun.security.jca.*; + +/** + * This class instantiates implementations of JSSE engine classes from + * providers registered with the java.security.Security object. + * + * @author Jan Luehe + * @author Jeff Nisewanger + * @author Brad Wetmore + */ + +final class SSLSecurity { + + /* + * Don't let anyone instantiate this. + */ + private SSLSecurity() { + } + + + // ProviderList.getService() is not accessible now, implement our own loop + private static Service getService(String type, String alg) { + ProviderList list = Providers.getProviderList(); + for (Provider p : list.providers()) { + Service s = p.getService(type, alg); + if (s != null) { + return s; + } + } + return null; + } + + /** + * The body of the driver for the getImpl method. + */ + private static Object[] getImpl1(String algName, String engineType, + Service service) throws NoSuchAlgorithmException + { + Provider provider = service.getProvider(); + String className = service.getClassName(); + Class implClass; + try { + ClassLoader cl = provider.getClass().getClassLoader(); + if (cl == null) { + // system class + implClass = Class.forName(className); + } else { + implClass = cl.loadClass(className); + } + } catch (ClassNotFoundException e) { + throw new NoSuchAlgorithmException("Class " + className + + " configured for " + + engineType + + " not found: " + + e.getMessage()); + } catch (SecurityException e) { + throw new NoSuchAlgorithmException("Class " + className + + " configured for " + + engineType + + " cannot be accessed: " + + e.getMessage()); + } + + /* + * JSSE 1.0, 1.0.1, and 1.0.2 used the com.sun.net.ssl API as the + * API was being developed. As JSSE was folded into the main + * release, it was decided to promote the com.sun.net.ssl API to + * be javax.net.ssl. It is desired to keep binary compatibility + * with vendors of JSSE implementation written using the + * com.sun.net.sll API, so we do this magic to handle everything. + * + * API used Implementation used Supported? + * ======== =================== ========== + * com.sun javax Yes + * com.sun com.sun Yes + * javax javax Yes + * javax com.sun Not Currently + * + * Make sure the implementation class is a subclass of the + * corresponding engine class. + * + * In wrapping these classes, there's no way to know how to + * wrap all possible classes that extend the TrustManager/KeyManager. + * We only wrap the x509 variants. + */ + + try { // catch instantiation errors + + /* + * (The following Class.forName()s should alway work, because + * this class and all the SPI classes in javax.crypto are + * loaded by the same class loader.) That is, unless they + * give us a SPI class that doesn't exist, say SSLFoo, + * or someone has removed classes from the java.base module. + */ + + Class typeClassJavax; + Class typeClassCom; + Object obj = null; + + /* + * Odds are more likely that we have a javax variant, try this + * first. + */ + if (((typeClassJavax = Class.forName("javax.net.ssl." + + engineType + "Spi")) != null) && + (checkSuperclass(implClass, typeClassJavax))) { + + if (engineType.equals("SSLContext")) { + obj = new SSLContextSpiWrapper(algName, provider); + } else if (engineType.equals("TrustManagerFactory")) { + obj = new TrustManagerFactorySpiWrapper(algName, provider); + } else if (engineType.equals("KeyManagerFactory")) { + obj = new KeyManagerFactorySpiWrapper(algName, provider); + } else { + /* + * We should throw an error if we get + * something totally unexpected. Don't ever + * expect to see this one... + */ + throw new IllegalStateException( + "Class " + implClass.getName() + + " unknown engineType wrapper:" + engineType); + } + + } else if (((typeClassCom = Class.forName("com.sun.net.ssl." + + engineType + "Spi")) != null) && + (checkSuperclass(implClass, typeClassCom))) { + obj = service.newInstance(null); + } + + if (obj != null) { + return new Object[] { obj, provider }; + } else { + throw new NoSuchAlgorithmException( + "Couldn't locate correct object or wrapper: " + + engineType + " " + algName); + } + + } catch (ClassNotFoundException e) { + IllegalStateException exc = new IllegalStateException( + "Engine Class Not Found for " + engineType); + exc.initCause(e); + throw exc; + } + } + + /** + * Returns an array of objects: the first object in the array is + * an instance of an implementation of the requested algorithm + * and type, and the second object in the array identifies the provider + * of that implementation. + * The provName argument can be null, in which case all + * configured providers will be searched in order of preference. + */ + static Object[] getImpl(String algName, String engineType, String provName) + throws NoSuchAlgorithmException, NoSuchProviderException + { + Service service; + if (provName != null) { + ProviderList list = Providers.getProviderList(); + Provider prov = list.getProvider(provName); + if (prov == null) { + throw new NoSuchProviderException("No such provider: " + + provName); + } + service = prov.getService(engineType, algName); + } else { + service = getService(engineType, algName); + } + if (service == null) { + throw new NoSuchAlgorithmException("Algorithm " + algName + + " not available"); + } + return getImpl1(algName, engineType, service); + } + + + /** + * Returns an array of objects: the first object in the array is + * an instance of an implementation of the requested algorithm + * and type, and the second object in the array identifies the provider + * of that implementation. + * The prov argument can be null, in which case all + * configured providers will be searched in order of preference. + */ + static Object[] getImpl(String algName, String engineType, Provider prov) + throws NoSuchAlgorithmException + { + Service service = prov.getService(engineType, algName); + if (service == null) { + throw new NoSuchAlgorithmException("No such algorithm: " + + algName); + } + return getImpl1(algName, engineType, service); + } + + /* + * Checks whether one class is the superclass of another + */ + private static boolean checkSuperclass(Class subclass, Class superclass) { + if ((subclass == null) || (superclass == null)) + return false; + + while (!subclass.equals(superclass)) { + subclass = subclass.getSuperclass(); + if (subclass == null) { + return false; + } + } + return true; + } + + /* + * Return at most the first "resize" elements of an array. + * + * Didn't want to use java.util.Arrays, as PJava may not have it. + */ + static Object[] truncateArray(Object[] oldArray, Object[] newArray) { + + for (int i = 0; i < newArray.length; i++) { + newArray[i] = oldArray[i]; + } + + return newArray; + } + +} + + +/* + * ================================================================= + * The remainder of this file is for the wrapper and wrapper-support + * classes. When SSLSecurity finds something which extends the + * javax.net.ssl.*Spi, we need to go grab a real instance of the + * thing that the Spi supports, and wrap into a com.sun.net.ssl.*Spi + * object. This also mean that anything going down into the SPI + * needs to be wrapped, as well as anything coming back up. + */ +@SuppressWarnings("deprecation") +final class SSLContextSpiWrapper extends SSLContextSpi { + + private javax.net.ssl.SSLContext theSSLContext; + + SSLContextSpiWrapper(String algName, Provider prov) throws + NoSuchAlgorithmException { + theSSLContext = javax.net.ssl.SSLContext.getInstance(algName, prov); + } + + @SuppressWarnings("deprecation") + protected void engineInit(KeyManager[] kma, TrustManager[] tma, + SecureRandom sr) throws KeyManagementException { + + // Keep track of the actual number of array elements copied + int dst; + int src; + javax.net.ssl.KeyManager[] kmaw; + javax.net.ssl.TrustManager[] tmaw; + + // Convert com.sun.net.ssl.kma to a javax.net.ssl.kma + // wrapper if need be. + if (kma != null) { + kmaw = new javax.net.ssl.KeyManager[kma.length]; + for (src = 0, dst = 0; src < kma.length; ) { + /* + * These key managers may implement both javax + * and com.sun interfaces, so if they do + * javax, there's no need to wrap them. + */ + if (!(kma[src] instanceof javax.net.ssl.KeyManager)) { + /* + * Do we know how to convert them? If not, oh well... + * We'll have to drop them on the floor in this + * case, cause we don't know how to handle them. + * This will be pretty rare, but put here for + * completeness. + */ + if (kma[src] instanceof X509KeyManager) { + kmaw[dst] = (javax.net.ssl.KeyManager) + new X509KeyManagerJavaxWrapper( + (X509KeyManager)kma[src]); + dst++; + } + } else { + // We can convert directly, since they implement. + kmaw[dst] = (javax.net.ssl.KeyManager)kma[src]; + dst++; + } + src++; + } + + /* + * If dst != src, there were more items in the original array + * than in the new array. Compress the new elements to avoid + * any problems down the road. + */ + if (dst != src) { + kmaw = (javax.net.ssl.KeyManager []) + SSLSecurity.truncateArray(kmaw, + new javax.net.ssl.KeyManager [dst]); + } + } else { + kmaw = null; + } + + // Now do the same thing with the TrustManagers. + if (tma != null) { + tmaw = new javax.net.ssl.TrustManager[tma.length]; + + for (src = 0, dst = 0; src < tma.length; ) { + /* + * These key managers may implement both...see above... + */ + if (!(tma[src] instanceof javax.net.ssl.TrustManager)) { + // Do we know how to convert them? + if (tma[src] instanceof X509TrustManager) { + tmaw[dst] = (javax.net.ssl.TrustManager) + new X509TrustManagerJavaxWrapper( + (X509TrustManager)tma[src]); + dst++; + } + } else { + tmaw[dst] = (javax.net.ssl.TrustManager)tma[src]; + dst++; + } + src++; + } + + if (dst != src) { + tmaw = (javax.net.ssl.TrustManager []) + SSLSecurity.truncateArray(tmaw, + new javax.net.ssl.TrustManager [dst]); + } + } else { + tmaw = null; + } + + theSSLContext.init(kmaw, tmaw, sr); + } + + protected javax.net.ssl.SSLSocketFactory + engineGetSocketFactory() { + return theSSLContext.getSocketFactory(); + } + + protected javax.net.ssl.SSLServerSocketFactory + engineGetServerSocketFactory() { + return theSSLContext.getServerSocketFactory(); + } + +} + +@SuppressWarnings("deprecation") +final class TrustManagerFactorySpiWrapper extends TrustManagerFactorySpi { + + private javax.net.ssl.TrustManagerFactory theTrustManagerFactory; + + TrustManagerFactorySpiWrapper(String algName, Provider prov) throws + NoSuchAlgorithmException { + theTrustManagerFactory = + javax.net.ssl.TrustManagerFactory.getInstance(algName, prov); + } + + protected void engineInit(KeyStore ks) throws KeyStoreException { + theTrustManagerFactory.init(ks); + } + + protected TrustManager[] engineGetTrustManagers() { + + int dst; + int src; + + javax.net.ssl.TrustManager[] tma = + theTrustManagerFactory.getTrustManagers(); + + TrustManager[] tmaw = new TrustManager[tma.length]; + + for (src = 0, dst = 0; src < tma.length; ) { + if (!(tma[src] instanceof com.sun.net.ssl.TrustManager)) { + // We only know how to wrap X509TrustManagers, as + // TrustManagers don't have any methods to wrap. + if (tma[src] instanceof javax.net.ssl.X509TrustManager) { + tmaw[dst] = (TrustManager) + new X509TrustManagerComSunWrapper( + (javax.net.ssl.X509TrustManager)tma[src]); + dst++; + } + } else { + tmaw[dst] = (TrustManager)tma[src]; + dst++; + } + src++; + } + + if (dst != src) { + tmaw = (TrustManager []) + SSLSecurity.truncateArray(tmaw, new TrustManager [dst]); + } + + return tmaw; + } + +} + +@SuppressWarnings("deprecation") +final class KeyManagerFactorySpiWrapper extends KeyManagerFactorySpi { + + private javax.net.ssl.KeyManagerFactory theKeyManagerFactory; + + KeyManagerFactorySpiWrapper(String algName, Provider prov) throws + NoSuchAlgorithmException { + theKeyManagerFactory = + javax.net.ssl.KeyManagerFactory.getInstance(algName, prov); + } + + protected void engineInit(KeyStore ks, char[] password) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException { + theKeyManagerFactory.init(ks, password); + } + + protected KeyManager[] engineGetKeyManagers() { + + int dst; + int src; + + javax.net.ssl.KeyManager[] kma = + theKeyManagerFactory.getKeyManagers(); + + KeyManager[] kmaw = new KeyManager[kma.length]; + + for (src = 0, dst = 0; src < kma.length; ) { + if (!(kma[src] instanceof com.sun.net.ssl.KeyManager)) { + // We only know how to wrap X509KeyManagers, as + // KeyManagers don't have any methods to wrap. + if (kma[src] instanceof javax.net.ssl.X509KeyManager) { + kmaw[dst] = (KeyManager) + new X509KeyManagerComSunWrapper( + (javax.net.ssl.X509KeyManager)kma[src]); + dst++; + } + } else { + kmaw[dst] = (KeyManager)kma[src]; + dst++; + } + src++; + } + + if (dst != src) { + kmaw = (KeyManager []) + SSLSecurity.truncateArray(kmaw, new KeyManager [dst]); + } + + return kmaw; + } + +} + +// ================================= + +@SuppressWarnings("deprecation") +final class X509KeyManagerJavaxWrapper implements + javax.net.ssl.X509KeyManager { + + private X509KeyManager theX509KeyManager; + + X509KeyManagerJavaxWrapper(X509KeyManager obj) { + theX509KeyManager = obj; + } + + public String[] getClientAliases(String keyType, Principal[] issuers) { + return theX509KeyManager.getClientAliases(keyType, issuers); + } + + public String chooseClientAlias(String[] keyTypes, Principal[] issuers, + Socket socket) { + String retval; + + if (keyTypes == null) { + return null; + } + + /* + * Scan the list, look for something we can pass back. + */ + for (int i = 0; i < keyTypes.length; i++) { + if ((retval = theX509KeyManager.chooseClientAlias(keyTypes[i], + issuers)) != null) + return retval; + } + return null; + + } + + /* + * JSSE 1.0.x was only socket based, but it's possible someone might + * want to install a really old provider. We should at least + * try to be nice. + */ + public String chooseEngineClientAlias( + String[] keyTypes, Principal[] issuers, + javax.net.ssl.SSLEngine engine) { + String retval; + + if (keyTypes == null) { + return null; + } + + /* + * Scan the list, look for something we can pass back. + */ + for (int i = 0; i < keyTypes.length; i++) { + if ((retval = theX509KeyManager.chooseClientAlias(keyTypes[i], + issuers)) != null) + return retval; + } + + return null; + } + + public String[] getServerAliases(String keyType, Principal[] issuers) { + return theX509KeyManager.getServerAliases(keyType, issuers); + } + + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + + if (keyType == null) { + return null; + } + return theX509KeyManager.chooseServerAlias(keyType, issuers); + } + + /* + * JSSE 1.0.x was only socket based, but it's possible someone might + * want to install a really old provider. We should at least + * try to be nice. + */ + public String chooseEngineServerAlias( + String keyType, Principal[] issuers, + javax.net.ssl.SSLEngine engine) { + + if (keyType == null) { + return null; + } + return theX509KeyManager.chooseServerAlias(keyType, issuers); + } + + public java.security.cert.X509Certificate[] + getCertificateChain(String alias) { + return theX509KeyManager.getCertificateChain(alias); + } + + public PrivateKey getPrivateKey(String alias) { + return theX509KeyManager.getPrivateKey(alias); + } +} + +@SuppressWarnings("deprecation") +final class X509TrustManagerJavaxWrapper implements + javax.net.ssl.X509TrustManager { + + private X509TrustManager theX509TrustManager; + + X509TrustManagerJavaxWrapper(X509TrustManager obj) { + theX509TrustManager = obj; + } + + public void checkClientTrusted( + java.security.cert.X509Certificate[] chain, String authType) + throws java.security.cert.CertificateException { + if (!theX509TrustManager.isClientTrusted(chain)) { + throw new java.security.cert.CertificateException( + "Untrusted Client Certificate Chain"); + } + } + + public void checkServerTrusted( + java.security.cert.X509Certificate[] chain, String authType) + throws java.security.cert.CertificateException { + if (!theX509TrustManager.isServerTrusted(chain)) { + throw new java.security.cert.CertificateException( + "Untrusted Server Certificate Chain"); + } + } + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return theX509TrustManager.getAcceptedIssuers(); + } +} + +@SuppressWarnings("deprecation") +final class X509KeyManagerComSunWrapper implements X509KeyManager { + + private javax.net.ssl.X509KeyManager theX509KeyManager; + + X509KeyManagerComSunWrapper(javax.net.ssl.X509KeyManager obj) { + theX509KeyManager = obj; + } + + public String[] getClientAliases(String keyType, Principal[] issuers) { + return theX509KeyManager.getClientAliases(keyType, issuers); + } + + public String chooseClientAlias(String keyType, Principal[] issuers) { + String [] keyTypes = new String [] { keyType }; + return theX509KeyManager.chooseClientAlias(keyTypes, issuers, null); + } + + public String[] getServerAliases(String keyType, Principal[] issuers) { + return theX509KeyManager.getServerAliases(keyType, issuers); + } + + public String chooseServerAlias(String keyType, Principal[] issuers) { + return theX509KeyManager.chooseServerAlias(keyType, issuers, null); + } + + public java.security.cert.X509Certificate[] + getCertificateChain(String alias) { + return theX509KeyManager.getCertificateChain(alias); + } + + public PrivateKey getPrivateKey(String alias) { + return theX509KeyManager.getPrivateKey(alias); + } +} + +@SuppressWarnings("deprecation") +final class X509TrustManagerComSunWrapper implements X509TrustManager { + + private javax.net.ssl.X509TrustManager theX509TrustManager; + + X509TrustManagerComSunWrapper(javax.net.ssl.X509TrustManager obj) { + theX509TrustManager = obj; + } + + public boolean isClientTrusted( + java.security.cert.X509Certificate[] chain) { + try { + theX509TrustManager.checkClientTrusted(chain, "UNKNOWN"); + return true; + } catch (java.security.cert.CertificateException e) { + return false; + } + } + + public boolean isServerTrusted( + java.security.cert.X509Certificate[] chain) { + try { + theX509TrustManager.checkServerTrusted(chain, "UNKNOWN"); + return true; + } catch (java.security.cert.CertificateException e) { + return false; + } + } + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return theX509TrustManager.getAcceptedIssuers(); + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/TrustManager.java 2019-03-22 01:09:18.892704986 +0100 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2000, 2017, 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. + */ + +/* + * NOTE: this file was copied from javax.net.ssl.TrustManager + */ + +package com.sun.net.ssl; + +/** + * Base interface for JSSE trust managers which manage + * authentication trust decisions for different types of + * authentication material. + * + * @deprecated As of JDK 1.4, this implementation-specific class was + * replaced by {@link javax.net.ssl.TrustManager}. + */ +@Deprecated(since="1.4") +public interface TrustManager { +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/TrustManagerFactory.java 2019-03-22 01:09:19.132704451 +0100 @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2000, 2017, 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. + */ + +/* + * NOTE: this file was copied from javax.net.ssl.TrustManagerFactory + */ + +package com.sun.net.ssl; + +import java.security.*; + +/** + * This class acts as a factory for trust managers based on a + * source of trust material. Each trust manager manages a specific + * type of trust material for use by secure sockets. The trust + * material is based on a KeyStore and/or provider specific sources. + * + * @deprecated As of JDK 1.4, this implementation-specific class was + * replaced by {@link javax.net.ssl.TrustManagerFactory}. + */ +@Deprecated(since="1.4") +public class TrustManagerFactory { + // The provider + private Provider provider; + + // The provider implementation (delegate) + private TrustManagerFactorySpi factorySpi; + + // The name of the trust management algorithm. + private String algorithm; + + /** + *

The default TrustManager can be changed by setting the value of the + * {@code sun.ssl.trustmanager.type} security property to the desired name. + * + * @return the default type as specified by the + * {@code sun.ssl.trustmanager.type} security property, or an + * implementation-specific default if no such property exists. + * + * @see java.security.Security security properties + */ + public static final String getDefaultAlgorithm() { + String type; + type = AccessController.doPrivileged(new PrivilegedAction<>() { + public String run() { + return Security.getProperty("sun.ssl.trustmanager.type"); + } + }); + if (type == null) { + type = "SunX509"; + } + return type; + + } + + /** + * Creates a TrustManagerFactory object. + * + * @param factorySpi the delegate + * @param provider the provider + * @param algorithm the algorithm + */ + protected TrustManagerFactory(TrustManagerFactorySpi factorySpi, + Provider provider, String algorithm) { + this.factorySpi = factorySpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** + * Returns the algorithm name of this TrustManagerFactory + * object. + * + *

This is the same name that was specified in one of the + * getInstance calls that created this + * TrustManagerFactory object. + * + * @return the algorithm name of this TrustManagerFactory + * object. + */ + public final String getAlgorithm() { + return this.algorithm; + } + + /** + * Generates a TrustManagerFactory object that implements the + * specified trust management algorithm. + * If the default provider package provides an implementation of the + * requested trust management algorithm, an instance of + * TrustManagerFactory containing that implementation is + * returned. If the algorithm is not available in the default provider + * package, other provider packages are searched. + * + * @param algorithm the standard name of the requested trust management + * algorithm. + * + * @return the new TrustManagerFactory object + * + * @exception NoSuchAlgorithmException if the specified algorithm is not + * available in the default provider package or any of the other provider + * packages that were searched. + */ + public static final TrustManagerFactory getInstance(String algorithm) + throws NoSuchAlgorithmException + { + try { + Object[] objs = SSLSecurity.getImpl(algorithm, + "TrustManagerFactory", (String) null); + return new TrustManagerFactory((TrustManagerFactorySpi)objs[0], + (Provider)objs[1], + algorithm); + } catch (NoSuchProviderException e) { + throw new NoSuchAlgorithmException(algorithm + " not found"); + } + } + + /** + * Generates a TrustManagerFactory object for the specified + * trust management algorithm from the specified provider. + * + * @param algorithm the standard name of the requested trust management + * algorithm. + * @param provider the name of the provider + * + * @return the new TrustManagerFactory object + * + * @exception NoSuchAlgorithmException if the specified algorithm is not + * available from the specified provider. + * @exception NoSuchProviderException if the specified provider has not + * been configured. + */ + public static final TrustManagerFactory getInstance(String algorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.isEmpty()) + throw new IllegalArgumentException("missing provider"); + Object[] objs = SSLSecurity.getImpl(algorithm, "TrustManagerFactory", + provider); + return new TrustManagerFactory((TrustManagerFactorySpi)objs[0], + (Provider)objs[1], algorithm); + } + + /** + * Generates a TrustManagerFactory object for the specified + * trust management algorithm from the specified provider. + * + * @param algorithm the standard name of the requested trust management + * algorithm. + * @param provider an instance of the provider + * + * @return the new TrustManagerFactory object + * + * @exception NoSuchAlgorithmException if the specified algorithm is not + * available from the specified provider. + */ + public static final TrustManagerFactory getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("missing provider"); + Object[] objs = SSLSecurity.getImpl(algorithm, "TrustManagerFactory", + provider); + return new TrustManagerFactory((TrustManagerFactorySpi)objs[0], + (Provider)objs[1], algorithm); + } + + /** + * Returns the provider of this TrustManagerFactory object. + * + * @return the provider of this TrustManagerFactory object + */ + public final Provider getProvider() { + return this.provider; + } + + + /** + * Initializes this factory with a source of certificate + * authorities and related trust material. The + * provider may also include a provider-specific source + * of key material. + * + * @param ks the key store or null + */ + public void init(KeyStore ks) throws KeyStoreException { + factorySpi.engineInit(ks); + } + + /** + * Returns one trust manager for each type of trust material. + * @return the trust managers + */ + public TrustManager[] getTrustManagers() { + return factorySpi.engineGetTrustManagers(); + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/TrustManagerFactorySpi.java 2019-03-22 01:09:19.400703852 +0100 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2000, 2017, 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. + */ + +/* + * NOTE: this file was copied from javax.net.ssl.TrustManagerFactorySpi + */ + +package com.sun.net.ssl; + +import java.security.*; + +/** + * This class defines the Service Provider Interface (SPI) + * for the TrustManagerFactory class. + * + *

All the abstract methods in this class must be implemented by each + * cryptographic service provider who wishes to supply the implementation + * of a particular trust manager factory. + * + * @deprecated As of JDK 1.4, this implementation-specific class was + * replaced by {@link javax.net.ssl.TrustManagerFactorySpi}. + */ +@Deprecated(since="1.4") +public abstract class TrustManagerFactorySpi { + /** + * Initializes this factory with a source of certificate + * authorities and related trust material. The + * provider may also include a provider-specific source + * of key material. + * + * @param ks the key store or null + */ + protected abstract void engineInit(KeyStore ks) throws KeyStoreException; + + /** + * Returns one trust manager for each type of trust material. + * @return the trust managers + */ + protected abstract TrustManager[] engineGetTrustManagers(); +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/X509KeyManager.java 2019-03-22 01:09:19.640703320 +0100 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2000, 2017, 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. + */ + +/* + * NOTE: this file was copied from javax.net.ssl.X509KeyManager + */ + +package com.sun.net.ssl; + +import java.security.KeyManagementException; +import java.security.PrivateKey; +import java.security.Principal; +import java.security.cert.X509Certificate; + +/** + * Instances of this interface manage which X509 certificate-based + * key pairs are used to authenticate the local side of a secure + * socket. The individual entries are identified by unique alias names. + * + * @deprecated As of JDK 1.4, this implementation-specific class was + * replaced by {@link javax.net.ssl.X509KeyManager}. + */ +@Deprecated(since="1.4") +public interface X509KeyManager extends KeyManager { + /** + * Get the matching aliases for authenticating the client side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + * + * @param keyType the key algorithm type name + * @param issuers the list of acceptable CA issuer subject names + * @return the matching alias names + */ + public String[] getClientAliases(String keyType, Principal[] issuers); + + /** + * Choose an alias to authenticate the client side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + * + * @param keyType the key algorithm type name + * @param issuers the list of acceptable CA issuer subject names + * @return the alias name for the desired key + */ + public String chooseClientAlias(String keyType, Principal[] issuers); + + /** + * Get the matching aliases for authenticating the server side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + * + * @param keyType the key algorithm type name + * @param issuers the list of acceptable CA issuer subject names + * @return the matching alias names + */ + public String[] getServerAliases(String keyType, Principal[] issuers); + + /** + * Choose an alias to authenticate the server side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + * + * @param keyType the key algorithm type name + * @param issuers the list of acceptable CA issuer subject names + * @return the alias name for the desired key + */ + public String chooseServerAlias(String keyType, Principal[] issuers); + + /** + * Returns the certificate chain associated with the given alias. + * + * @param alias the alias name + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last) + */ + public X509Certificate[] getCertificateChain(String alias); + + /* + * Returns the key associated with the given alias. + * + * @param alias the alias name + * + * @return the requested key + */ + public PrivateKey getPrivateKey(String alias); +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/X509TrustManager.java 2019-03-22 01:09:19.868702810 +0100 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000, 2017, 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. + */ + +/* + * NOTE: this file was copied from javax.net.ssl.X509TrustManager + */ + +package com.sun.net.ssl; + +import java.security.cert.X509Certificate; + +/** + * Instance of this interface manage which X509 certificates + * may be used to authenticate the remote side of a secure + * socket. Decisions may be based on trusted certificate + * authorities, certificate revocation lists, online + * status checking or other means. + * + * @deprecated As of JDK 1.4, this implementation-specific class was + * replaced by {@link javax.net.ssl.X509TrustManager}. + */ +@Deprecated(since="1.4") +public interface X509TrustManager extends TrustManager { + /** + * Given the partial or complete certificate chain + * provided by the peer, build a certificate path + * to a trusted root and return true if it can be + * validated and is trusted for client SSL authentication. + * + * @param chain the peer certificate chain + */ + public boolean isClientTrusted(X509Certificate[] chain); + + /** + * Given the partial or complete certificate chain + * provided by the peer, build a certificate path + * to a trusted root and return true if it can be + * validated and is trusted for server SSL authentication. + * + * @param chain the peer certificate chain + */ + public boolean isServerTrusted(X509Certificate[] chain); + + /** + * Return an array of certificate authority certificates + * which are trusted for authenticating peers. + * + * @return the acceptable CA issuer certificates + */ + public X509Certificate[] getAcceptedIssuers(); +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/internal/ssl/Provider.java 2019-03-22 01:09:20.112702269 +0100 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007, 2019, 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 com.sun.net.ssl.internal.ssl; + +import sun.security.ssl.SunJSSE; + +/** + * Main class for the SunJSSE provider. The actual code was moved to the + * class sun.security.ssl.SunJSSE, but for backward compatibility we + * continue to use this class as the main Provider class. + */ +@Deprecated(since="9") +public final class Provider extends SunJSSE { + + private static final long serialVersionUID = 3231825739635378733L; + + // standard constructor + public Provider() { + super(); + } + + /** + * Installs the JSSE provider. + */ + public static synchronized void install() { + /* nop. Remove this method in the future. */ + } + +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/internal/ssl/X509ExtendedTrustManager.java 2019-03-22 01:09:20.432701556 +0100 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2005, 2017, 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 com.sun.net.ssl.internal.ssl; + +import javax.net.ssl.X509TrustManager; + +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; + +/** + * Instance of this class is an extension of X509TrustManager. + *

+ * Note that this class is referenced by the Deploy workspace. Any updates + * must make sure that they do not cause any breakage there. + *

+ * It takes the responsiblity of checking the peer identity with its + * principal declared in the cerificate. + *

+ * The class provides an alternative to HostnameVerifer. + * If application customizes its HostnameVerifer for + * HttpsURLConnection, the peer identity will be checked + * by the customized HostnameVerifer; otherwise, it will + * be checked by the extended trust manager. + *

+ * RFC2830 defines the server identification specification for "LDAP" + * algorithm. RFC2818 defines both the server identification and the + * client identification specification for "HTTPS" algorithm. + * + * @see X509TrustManager + * @see HostnameVerifier + * + * @since 1.6 + * @author Xuelei Fan + */ +@Deprecated(since="9") +public abstract class X509ExtendedTrustManager implements X509TrustManager { + /** + * Constructor used by subclasses only. + */ + protected X509ExtendedTrustManager() { + } + + /** + * Given the partial or complete certificate chain provided by the + * peer, check its identity and build a certificate path to a trusted + * root, return if it can be validated and is trusted for client SSL + * authentication based on the authentication type. + *

+ * The authentication type is determined by the actual certificate + * used. For instance, if RSAPublicKey is used, the authType + * should be "RSA". Checking is case-sensitive. + *

+ * The algorithm parameter specifies the client identification protocol + * to use. If the algorithm and the peer hostname are available, the + * peer hostname is checked against the peer's identity presented in + * the X509 certificate, in order to prevent masquerade attacks. + * + * @param chain the peer certificate chain + * @param authType the authentication type based on the client certificate + * @param hostname the peer hostname + * @param algorithm the identification algorithm + * @throws IllegalArgumentException if null or zero-length chain + * is passed in for the chain parameter or if null or zero-length + * string is passed in for the authType parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager. + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, String hostname, String algorithm) + throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, check its identity and build a certificate path to a trusted + * root, return if it can be validated and is trusted for server SSL + * authentication based on the authentication type. + *

+ * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Checking is case-sensitive. + *

+ * The algorithm parameter specifies the server identification protocol + * to use. If the algorithm and the peer hostname are available, the + * peer hostname is checked against the peer's identity presented in + * the X509 certificate, in order to prevent masquerade attacks. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param hostname the peer hostname + * @param algorithm the identification algorithm + * @throws IllegalArgumentException if null or zero-length chain + * is passed in for the chain parameter or if null or zero-length + * string is passed in for the authType parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager. + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, String hostname, String algorithm) + throws CertificateException; +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java 2019-03-22 01:09:20.692700978 +0100 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2001, 2017, 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 com.sun.net.ssl.internal.www.protocol.https; + +import java.net.URL; +import java.net.Proxy; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Iterator; + +import java.security.Principal; +import java.security.cert.*; + +import javax.security.auth.x500.X500Principal; + +import sun.security.util.HostnameChecker; +import sun.security.util.DerValue; +import sun.security.x509.X500Name; + +import sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection; + +/** + * This class was introduced to provide an additional level of + * abstraction between javax.net.ssl.HttpURLConnection and + * com.sun.net.ssl.HttpURLConnection objects.

+ * + * javax.net.ssl.HttpURLConnection is used in the new sun.net version + * of protocol implementation (this one) + * com.sun.net.ssl.HttpURLConnection is used in the com.sun version. + * + */ +@Deprecated(since="9") +@SuppressWarnings("deprecation") // HttpsURLConnection is deprecated +public class DelegateHttpsURLConnection extends AbstractDelegateHttpsURLConnection { + + // we need a reference to the HttpsURLConnection to get + // the properties set there + // we also need it to be public so that it can be referenced + // from sun.net.www.protocol.http.HttpURLConnection + // this is for ResponseCache.put(URI, URLConnection) + // second parameter needs to be cast to javax.net.ssl.HttpsURLConnection + // instead of AbstractDelegateHttpsURLConnection + + public com.sun.net.ssl.HttpsURLConnection httpsURLConnection; + + DelegateHttpsURLConnection(URL url, + sun.net.www.protocol.http.Handler handler, + com.sun.net.ssl.HttpsURLConnection httpsURLConnection) + throws IOException { + this(url, null, handler, httpsURLConnection); + } + + DelegateHttpsURLConnection(URL url, Proxy p, + sun.net.www.protocol.http.Handler handler, + com.sun.net.ssl.HttpsURLConnection httpsURLConnection) + throws IOException { + super(url, p, handler); + this.httpsURLConnection = httpsURLConnection; + } + + protected javax.net.ssl.SSLSocketFactory getSSLSocketFactory() { + return httpsURLConnection.getSSLSocketFactory(); + } + + protected javax.net.ssl.HostnameVerifier getHostnameVerifier() { + // note: getHostnameVerifier() never returns null + return new VerifierWrapper(httpsURLConnection.getHostnameVerifier()); + } + + /* + * Called by layered delegator's finalize() method to handle closing + * the underlying object. + */ + protected void dispose() throws Throwable { + super.finalize(); + } +} + +class VerifierWrapper implements javax.net.ssl.HostnameVerifier { + @SuppressWarnings("deprecation") + private com.sun.net.ssl.HostnameVerifier verifier; + + @SuppressWarnings("deprecation") + VerifierWrapper(com.sun.net.ssl.HostnameVerifier verifier) { + this.verifier = verifier; + } + + /* + * In com.sun.net.ssl.HostnameVerifier the method is defined + * as verify(String urlHostname, String certHostname). + * This means we need to extract the hostname from the X.509 certificate + * in this wrapper. + */ + public boolean verify(String hostname, javax.net.ssl.SSLSession session) { + try { + Certificate[] serverChain = session.getPeerCertificates(); + if ((serverChain == null) || (serverChain.length == 0)) { + return false; + } + if (serverChain[0] instanceof X509Certificate == false) { + return false; + } + X509Certificate serverCert = (X509Certificate)serverChain[0]; + String serverName = getServername(serverCert); + if (serverName == null) { + return false; + } + return verifier.verify(hostname, serverName); + } catch (javax.net.ssl.SSLPeerUnverifiedException e) { + return false; + } + } + + /* + * Extract the name of the SSL server from the certificate. + * + * Note this code is essentially a subset of the hostname extraction + * code in HostnameChecker. + */ + private static String getServername(X509Certificate peerCert) { + try { + // compare to subjectAltNames if dnsName is present + Collection> subjAltNames = peerCert.getSubjectAlternativeNames(); + if (subjAltNames != null) { + for (Iterator> itr = subjAltNames.iterator(); itr.hasNext(); ) { + List next = itr.next(); + if (((Integer)next.get(0)).intValue() == 2) { + // compare dNSName with host in url + String dnsName = ((String)next.get(1)); + return dnsName; + } + } + } + + // else check against common name in the subject field + X500Name subject = HostnameChecker.getSubjectX500Name(peerCert); + + DerValue derValue = subject.findMostSpecificAttribute + (X500Name.commonName_oid); + if (derValue != null) { + try { + String name = derValue.getAsString(); + return name; + } catch (IOException e) { + // ignore + } + } + } catch (java.security.cert.CertificateException e) { + // ignore + } + return null; + } + +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/Handler.java 2019-03-22 01:09:20.920700469 +0100 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1996, 2017, 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 com.sun.net.ssl.internal.www.protocol.https; + +import java.io.IOException; +import java.net.URL; +import java.net.Proxy; + +/** + * This class exists for compatibility with previous JSSE releases + * only. The HTTPS implementation can now be found in + * sun.net.www.protocol.https. + * + */ +@Deprecated(since="9") +public class Handler extends sun.net.www.protocol.https.Handler { + + public Handler() { + super(); + } + + public Handler(String proxy, int port) { + super(proxy, port); + } + + protected java.net.URLConnection openConnection(URL u) throws IOException { + return openConnection(u, (Proxy)null); + } + + protected java.net.URLConnection openConnection(URL u, Proxy p) throws IOException { + return new HttpsURLConnectionOldImpl(u, p, this); + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/HttpsURLConnectionOldImpl.java 2019-03-22 01:09:21.136699988 +0100 @@ -0,0 +1,506 @@ +/* + * Copyright (c) 1996, 2017, 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. + */ + +/* + * NOTE: This class lives in the package sun.net.www.protocol.https. + * There is a copy in com.sun.net.ssl.internal.www.protocol.https for JSSE + * 1.0.2 compatibility. It is 100% identical except the package and extends + * lines. Any changes should be made to be class in sun.net.* and then copied + * to com.sun.net.*. + */ + +// For both copies of the file, uncomment one line and comment the other +// package sun.net.www.protocol.https; +package com.sun.net.ssl.internal.www.protocol.https; + +import java.net.URL; +import java.net.Proxy; +import java.net.ProtocolException; +import java.net.MalformedURLException; +import java.io.*; +import java.net.Authenticator; +import javax.net.ssl.*; +import java.security.Permission; +import java.util.Map; +import java.util.List; +import sun.net.www.http.HttpClient; + +/** + * A class to represent an HTTP connection to a remote object. + * + * Ideally, this class should subclass and inherit the http handler + * implementation, but it can't do so because that class have the + * wrong Java Type. Thus it uses the delegate (aka, the + * Adapter/Wrapper design pattern) to reuse code from the http + * handler. + * + * Since it would use a delegate to access + * sun.net.www.protocol.http.HttpURLConnection functionalities, it + * needs to implement all public methods in it's super class and all + * the way to Object. + * + */ + +// For both copies of the file, uncomment one line and comment the other +// public class HttpsURLConnectionImpl +// extends javax.net.ssl.HttpsURLConnection { +@Deprecated(since="9") +@SuppressWarnings("deprecation") // HttpsURLConnection is deprecated +public class HttpsURLConnectionOldImpl + extends com.sun.net.ssl.HttpsURLConnection { + + private DelegateHttpsURLConnection delegate; + +// For both copies of the file, uncomment one line and comment the other +// HttpsURLConnectionImpl(URL u, Handler handler) throws IOException { + HttpsURLConnectionOldImpl(URL u, Handler handler) throws IOException { + this(u, null, handler); + } + + static URL checkURL(URL u) throws IOException { + if (u != null) { + if (u.toExternalForm().indexOf('\n') > -1) { + throw new MalformedURLException("Illegal character in URL"); + } + } + return u; + } +// For both copies of the file, uncomment one line and comment the other +// HttpsURLConnectionImpl(URL u, Handler handler) throws IOException { + HttpsURLConnectionOldImpl(URL u, Proxy p, Handler handler) throws IOException { + super(checkURL(u)); + delegate = new DelegateHttpsURLConnection(url, p, handler, this); + } + + /** + * Create a new HttpClient object, bypassing the cache of + * HTTP client objects/connections. + * + * @param url the URL being accessed + */ + protected void setNewClient(URL url) throws IOException { + delegate.setNewClient(url, false); + } + + /** + * Obtain a HttpClient object. Use the cached copy if specified. + * + * @param url the URL being accessed + * @param useCache whether the cached connection should be used + * if present + */ + protected void setNewClient(URL url, boolean useCache) + throws IOException { + delegate.setNewClient(url, useCache); + } + + /** + * Create a new HttpClient object, set up so that it uses + * per-instance proxying to the given HTTP proxy. This + * bypasses the cache of HTTP client objects/connections. + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + */ + protected void setProxiedClient(URL url, String proxyHost, int proxyPort) + throws IOException { + delegate.setProxiedClient(url, proxyHost, proxyPort); + } + + /** + * Obtain a HttpClient object, set up so that it uses per-instance + * proxying to the given HTTP proxy. Use the cached copy of HTTP + * client objects/connections if specified. + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + * @param useCache whether the cached connection should be used + * if present + */ + protected void setProxiedClient(URL url, String proxyHost, int proxyPort, + boolean useCache) throws IOException { + delegate.setProxiedClient(url, proxyHost, proxyPort, useCache); + } + + /** + * Implements the HTTP protocol handler's "connect" method, + * establishing an SSL connection to the server as necessary. + */ + public void connect() throws IOException { + delegate.connect(); + } + + /** + * Used by subclass to access "connected" variable. Since we are + * delegating the actual implementation to "delegate", we need to + * delegate the access of "connected" as well. + */ + protected boolean isConnected() { + return delegate.isConnected(); + } + + /** + * Used by subclass to access "connected" variable. Since we are + * delegating the actual implementation to "delegate", we need to + * delegate the access of "connected" as well. + */ + protected void setConnected(boolean conn) { + delegate.setConnected(conn); + } + + /** + * Returns the cipher suite in use on this connection. + */ + public String getCipherSuite() { + return delegate.getCipherSuite(); + } + + /** + * Returns the certificate chain the client sent to the + * server, or null if the client did not authenticate. + */ + public java.security.cert.Certificate [] + getLocalCertificates() { + return delegate.getLocalCertificates(); + } + + /** + * Returns the server's certificate chain, or throws + * SSLPeerUnverified Exception if + * the server did not authenticate. + */ + public java.security.cert.Certificate [] + getServerCertificates() throws SSLPeerUnverifiedException { + return delegate.getServerCertificates(); + } + + /* + * Allowable input/output sequences: + * [interpreted as POST/PUT] + * - get output, [write output,] get input, [read input] + * - get output, [write output] + * [interpreted as GET] + * - get input, [read input] + * Disallowed: + * - get input, [read input,] get output, [write output] + */ + + public synchronized OutputStream getOutputStream() throws IOException { + return delegate.getOutputStream(); + } + + public synchronized InputStream getInputStream() throws IOException { + return delegate.getInputStream(); + } + + public InputStream getErrorStream() { + return delegate.getErrorStream(); + } + + /** + * Disconnect from the server. + */ + public void disconnect() { + delegate.disconnect(); + } + + public boolean usingProxy() { + return delegate.usingProxy(); + } + + /** + * Returns an unmodifiable Map of the header fields. + * The Map keys are Strings that represent the + * response-header field names. Each Map value is an + * unmodifiable List of Strings that represents + * the corresponding field values. + * + * @return a Map of header fields + * @since 1.4 + */ + public Map> getHeaderFields() { + return delegate.getHeaderFields(); + } + + /** + * Gets a header field by name. Returns null if not known. + * @param name the name of the header field + */ + public String getHeaderField(String name) { + return delegate.getHeaderField(name); + } + + /** + * Gets a header field by index. Returns null if not known. + * @param n the index of the header field + */ + public String getHeaderField(int n) { + return delegate.getHeaderField(n); + } + + /** + * Gets a header field by index. Returns null if not known. + * @param n the index of the header field + */ + public String getHeaderFieldKey(int n) { + return delegate.getHeaderFieldKey(n); + } + + /** + * Sets request property. If a property with the key already + * exists, overwrite its value with the new value. + * @param value the value to be set + */ + public void setRequestProperty(String key, String value) { + delegate.setRequestProperty(key, value); + } + + /** + * Adds a general request property specified by a + * key-value pair. This method will not overwrite + * existing values associated with the same key. + * + * @param key the keyword by which the request is known + * (e.g., "accept"). + * @param value the value associated with it. + * @see #getRequestProperties(java.lang.String) + * @since 1.4 + */ + public void addRequestProperty(String key, String value) { + delegate.addRequestProperty(key, value); + } + + /** + * Overwrite super class method + */ + public int getResponseCode() throws IOException { + return delegate.getResponseCode(); + } + + public String getRequestProperty(String key) { + return delegate.getRequestProperty(key); + } + + /** + * Returns an unmodifiable Map of general request + * properties for this connection. The Map keys + * are Strings that represent the request-header + * field names. Each Map value is a unmodifiable List + * of Strings that represents the corresponding + * field values. + * + * @return a Map of the general request properties for this connection. + * @throws IllegalStateException if already connected + * @since 1.4 + */ + public Map> getRequestProperties() { + return delegate.getRequestProperties(); + } + + /* + * We support JDK 1.2.x so we can't count on these from JDK 1.3. + * We override and supply our own version. + */ + public void setInstanceFollowRedirects(boolean shouldFollow) { + delegate.setInstanceFollowRedirects(shouldFollow); + } + + public boolean getInstanceFollowRedirects() { + return delegate.getInstanceFollowRedirects(); + } + + public void setRequestMethod(String method) throws ProtocolException { + delegate.setRequestMethod(method); + } + + public String getRequestMethod() { + return delegate.getRequestMethod(); + } + + public String getResponseMessage() throws IOException { + return delegate.getResponseMessage(); + } + + public long getHeaderFieldDate(String name, long Default) { + return delegate.getHeaderFieldDate(name, Default); + } + + public Permission getPermission() throws IOException { + return delegate.getPermission(); + } + + public URL getURL() { + return delegate.getURL(); + } + + public int getContentLength() { + return delegate.getContentLength(); + } + + public long getContentLengthLong() { + return delegate.getContentLengthLong(); + } + + public String getContentType() { + return delegate.getContentType(); + } + + public String getContentEncoding() { + return delegate.getContentEncoding(); + } + + public long getExpiration() { + return delegate.getExpiration(); + } + + public long getDate() { + return delegate.getDate(); + } + + public long getLastModified() { + return delegate.getLastModified(); + } + + public int getHeaderFieldInt(String name, int Default) { + return delegate.getHeaderFieldInt(name, Default); + } + + public long getHeaderFieldLong(String name, long Default) { + return delegate.getHeaderFieldLong(name, Default); + } + + public Object getContent() throws IOException { + return delegate.getContent(); + } + + @SuppressWarnings("rawtypes") + public Object getContent(Class[] classes) throws IOException { + return delegate.getContent(classes); + } + + public String toString() { + return delegate.toString(); + } + + public void setDoInput(boolean doinput) { + delegate.setDoInput(doinput); + } + + public boolean getDoInput() { + return delegate.getDoInput(); + } + + public void setDoOutput(boolean dooutput) { + delegate.setDoOutput(dooutput); + } + + public boolean getDoOutput() { + return delegate.getDoOutput(); + } + + public void setAllowUserInteraction(boolean allowuserinteraction) { + delegate.setAllowUserInteraction(allowuserinteraction); + } + + public boolean getAllowUserInteraction() { + return delegate.getAllowUserInteraction(); + } + + public void setUseCaches(boolean usecaches) { + delegate.setUseCaches(usecaches); + } + + public boolean getUseCaches() { + return delegate.getUseCaches(); + } + + public void setIfModifiedSince(long ifmodifiedsince) { + delegate.setIfModifiedSince(ifmodifiedsince); + } + + public long getIfModifiedSince() { + return delegate.getIfModifiedSince(); + } + + public boolean getDefaultUseCaches() { + return delegate.getDefaultUseCaches(); + } + + public void setDefaultUseCaches(boolean defaultusecaches) { + delegate.setDefaultUseCaches(defaultusecaches); + } + + /* + * finalize (dispose) the delegated object. Otherwise + * sun.net.www.protocol.http.HttpURLConnection's finalize() + * would have to be made public. + */ + protected void finalize() throws Throwable { + delegate.dispose(); + } + + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + public int hashCode() { + return delegate.hashCode(); + } + + public void setConnectTimeout(int timeout) { + delegate.setConnectTimeout(timeout); + } + + public int getConnectTimeout() { + return delegate.getConnectTimeout(); + } + + public void setReadTimeout(int timeout) { + delegate.setReadTimeout(timeout); + } + + public int getReadTimeout() { + return delegate.getReadTimeout(); + } + + public void setFixedLengthStreamingMode (int contentLength) { + delegate.setFixedLengthStreamingMode(contentLength); + } + + public void setFixedLengthStreamingMode(long contentLength) { + delegate.setFixedLengthStreamingMode(contentLength); + } + + public void setChunkedStreamingMode (int chunklen) { + delegate.setChunkedStreamingMode(chunklen); + } + + @Override + public void setAuthenticator(Authenticator auth) { + delegate.setAuthenticator(auth); + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/com/sun/net/ssl/package-info.java 2019-03-22 01:09:21.352699508 +0100 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1999, 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. + */ + +/** + * Provides classes related to creating and configuring secure socket + * factories. These classes are used with the Sun reference + * implementation of the Java Secure Socket Extension (JSSE). + */ +package com.sun.net.ssl; --- old/src/java.base/share/classes/com/sun/security/cert/internal/x509/X509V1CertImpl.java 2019-03-22 01:09:21.768698581 +0100 +++ new/src/java.base/share/classes/com/sun/security/cert/internal/x509/X509V1CertImpl.java 2019-03-22 01:09:21.576699008 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -51,8 +51,7 @@ * * @author Jeff Nisewanger */ -@SuppressWarnings("removal") -@Deprecated(since="9", forRemoval=true) +@Deprecated public class X509V1CertImpl extends X509Certificate implements Serializable { static final long serialVersionUID = -2048442350420423405L; private java.security.cert.X509Certificate wrappedCert; --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/AbstractRecord.java 2019-03-22 01:09:21.816698473 +0100 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017, 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; + +import java.lang.annotation.Data; + +/** + * AbstractRecord + * + * @author Brian Goetz + */ +@Data +public abstract class AbstractRecord { + + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract String toString(); +} + --- old/src/java.base/share/classes/java/lang/AbstractStringBuilder.java 2019-03-22 01:09:22.176697672 +0100 +++ new/src/java.base/share/classes/java/lang/AbstractStringBuilder.java 2019-03-22 01:09:22.028698002 +0100 @@ -93,16 +93,13 @@ /** * Creates an AbstractStringBuilder with the specified coder and with - * the initial capacity equal to the smaller of (length + addition) + * the initial capacity equal to the smaller of (capacity + addition) * and Integer.MAX_VALUE. */ - AbstractStringBuilder(byte coder, int length, int addition) { - if (length < 0) { - throw new NegativeArraySizeException("Negative length: " + length); - } + AbstractStringBuilder(byte coder, int capacity, int addition) { this.coder = coder; - int capacity = (length < Integer.MAX_VALUE - addition) - ? length + addition : Integer.MAX_VALUE; + capacity = (capacity < Integer.MAX_VALUE - addition) + ? capacity + addition : Integer.MAX_VALUE; value = (coder == LATIN1) ? new byte[capacity] : StringUTF16.newBytesFor(capacity); } --- old/src/java.base/share/classes/java/lang/Class.java 2019-03-22 01:09:22.380697218 +0100 +++ new/src/java.base/share/classes/java/lang/Class.java 2019-03-22 01:09:22.232697544 +0100 @@ -2255,6 +2255,29 @@ return copyFields(privateGetDeclaredFields(false)); } + /** + * TBD + * @return TBD + * @throws SecurityException TBD + * @since 1.12 + */ + @CallerSensitive + public Field[] getRecordParameters() throws SecurityException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); + } + return isPrimitive() || isArray() ? new Field[0] : copyFields(privateGetRecordParameters()); + } + + /** + * Returns the number of record parameters if this class is a record, 0 if not + * @return the number of record parameters + * @since 1.12 + */ + public int getRecordParametersCount() { + return isPrimitive() || isArray() ? 0 : getRecordParametersCount0(); + } /** * Returns an array containing {@code Method} objects reflecting all the @@ -2956,6 +2979,8 @@ volatile Field[] declaredPublicFields; volatile Method[] declaredPublicMethods; volatile Class[] interfaces; + // record parameters + volatile Field[] recordParameters; // Cached names String simpleName; @@ -3076,6 +3101,21 @@ return res; } + private Field[] privateGetRecordParameters() { + Field[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = rd.recordParameters; + if (res != null) return res; + } + // No cached value available; request value from VM + res = Reflection.filterFields(this, getRecordParameters0()); + if (rd != null) { + rd.recordParameters = res; + } + return res; + } + // Returns an array of "root" fields. These Field objects must NOT // be propagated to the outside world, but must instead be copied // via ReflectionFactory.copyField. @@ -3413,6 +3453,8 @@ private native Method[] getDeclaredMethods0(boolean publicOnly); private native Constructor[] getDeclaredConstructors0(boolean publicOnly); private native Class[] getDeclaredClasses0(); + private native Field[] getRecordParameters0(); + private native int getRecordParametersCount0(); /** * Helper method to get the method name from arguments. @@ -3512,6 +3554,61 @@ this.getSuperclass() == java.lang.Enum.class; } + /** + * Returns true if and only if this class was declared as a record in the + * source code. + * + * @return true if and only if this class was declared as a record in the + * source code + * @since 1.12 + */ + public boolean isRecord() { + // A record must directly extend java.lang.AbstractRecord + return AbstractRecord.class.isAssignableFrom(this); + } + + /** + * Returns an array with the names of the components + * + * @return an array with the names of the components + * @since 1.12 + */ + public String[] getRecordParameterNames() { + if (isRecord()) { + Field[] recordParameters = getRecordParameters(); + String[] names = new String[recordParameters.length]; + int i = 0; + for (Field field : recordParameters) { + names[i] = field.getName(); + i++; + } + return names; + } else { + return new String[0]; + } + } + + /** + * Returns an array with the types of the record parameters + * + * @return an array with the types of the record parameters + * @since 1.12 + */ + public Class[] getRecordParameterTypes() { + if (isRecord()) { + Field[] recordParameters = getRecordParameters(); + Class[] types = new Class[recordParameters.length]; + int i = 0; + for (Field field : recordParameters) { + types[i] = field.getType(); + i++; + } + return types; + } else { + return new Class[0]; + } + } + // Fetches the factory for reflective objects private static ReflectionFactory getReflectionFactory() { if (reflectionFactory == null) { --- old/src/java.base/share/classes/java/lang/ProcessBuilder.java 2019-03-22 01:09:22.604696719 +0100 +++ new/src/java.base/share/classes/java/lang/ProcessBuilder.java 2019-03-22 01:09:22.452697054 +0100 @@ -89,7 +89,7 @@ *

  • a destination for standard output * and standard error. By default, the subprocess writes standard * output and standard error to pipes. Java code can access these pipes - * via the input streams returned by {@link Process#getInputStream()} and + * via the input streams returned by {@link Process#getOutputStream()} and * {@link Process#getErrorStream()}. However, standard output and * standard error may be redirected to other destinations using * {@link #redirectOutput(Redirect) redirectOutput} and --- old/src/java.base/share/classes/java/lang/StringBuffer.java 2019-03-22 01:09:22.812696255 +0100 +++ new/src/java.base/share/classes/java/lang/StringBuffer.java 2019-03-22 01:09:22.656696600 +0100 @@ -157,6 +157,10 @@ * as the specified {@code CharSequence}. The initial capacity of * the string buffer is {@code 16} plus the length of the * {@code CharSequence} argument. + *

    + * If the length of the specified {@code CharSequence} is + * less than or equal to zero, then an empty buffer of capacity + * {@code 16} is returned. * * @param seq the sequence to copy. * @since 1.5 --- old/src/java.base/share/classes/java/lang/ThreadGroup.java 2019-03-22 01:09:23.012695810 +0100 +++ new/src/java.base/share/classes/java/lang/ThreadGroup.java 2019-03-22 01:09:22.860696146 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.io.PrintStream; import java.util.Arrays; +import jdk.internal.misc.VM; /** * A thread group represents a set of threads. In addition, a thread @@ -424,7 +425,7 @@ ThreadGroup[] groupsSnapshot = null; synchronized (this) { if (destroyed) { - return n; + return 0; } int nt = nthreads; if (nt > list.length - n) { @@ -558,7 +559,7 @@ ThreadGroup[] groupsSnapshot = null; synchronized (this) { if (destroyed) { - return n; + return 0; } int ng = ngroups; if (ng > list.length - n) { --- old/src/java.base/share/classes/java/lang/Throwable.java 2019-03-22 01:09:23.220695347 +0100 +++ new/src/java.base/share/classes/java/lang/Throwable.java 2019-03-22 01:09:23.068695682 +0100 @@ -914,7 +914,8 @@ for (Throwable t : suppressedExceptions) { // Enforce constraints on suppressed exceptions in // case of corrupt or malicious stream. - Objects.requireNonNull(t, NULL_CAUSE_MESSAGE); + if (t == null) + throw new NullPointerException(NULL_CAUSE_MESSAGE); if (t == this) throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); suppressed.add(t); @@ -941,7 +942,8 @@ stackTrace = null; } else { // Verify stack trace elements are non-null. for(StackTraceElement ste : stackTrace) { - Objects.requireNonNull(ste, "null StackTraceElement in serial stream."); + if (ste == null) + throw new NullPointerException("null StackTraceElement in serial stream. "); } } } else { @@ -1032,7 +1034,8 @@ if (exception == this) throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE, exception); - Objects.requireNonNull(exception, NULL_CAUSE_MESSAGE); + if (exception == null) + throw new NullPointerException(NULL_CAUSE_MESSAGE); if (suppressedExceptions == null) // Suppressed exceptions not recorded return; --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/annotation/Data.java 2019-03-22 01:09:23.272695228 +0100 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017, 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.annotation; + +/** + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Data {} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/compiler/Extractor.java 2019-03-22 01:09:23.500694723 +0100 @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.compiler; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import sun.invoke.util.BytecodeName; + +import static java.lang.invoke.MethodHandleInfo.REF_invokeInterface; +import static java.lang.invoke.MethodHandleInfo.REF_invokeStatic; +import static java.lang.invoke.MethodHandleInfo.REF_invokeVirtual; +import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial; +import static java.util.Objects.requireNonNull; + +/** + * Supporting type for implementation of pattern matching. An {@linkplain Extractor} + * is a constant bundle of method handles that describe a particular pattern, and + * suitable for storing in the constant pool. + * + *

    An {@linkplain Extractor} is describe by a {@code descriptor} in the form + * of a {@link MethodType}. The return value of the descriptor is ignored; the + * argument types of the descriptor indicate the types of the output binding + * variables. + * + * Notes: + * - totality is erased; + * - compilers expected to optimize away total type patterns; + * - adaptation done in nest() and switch combinators + * + * @author Brian Goetz + */ +public interface Extractor { + /** + * A method handle that attempts to perform a match. It will have type + * {@code (Object)Object}. It accepts the target to be matched, and returns + * an opaque carrier if the match succeeds, or {@code null} if it fails. + * + * @return the {@code tryMatch} method handle + */ + MethodHandle tryMatch(); + + /** + * A method handle that extracts a component from the match carrier. It + * will take the match carrier and return the corresponding match binding. + * + * @param i the index of the component + * @return the {@code component} method handle + */ + MethodHandle component(int i); + + /** + * Returns the component method handles, as an array + * @return the component method handles + */ + MethodHandle[] components(); + + /** + * The descriptor of the {@linkplain Extractor}. The parameter types of + * the descriptor are the types of the binding variables. The return type + * is ignored. + * + * @return the descriptor + */ + MethodType descriptor(); + + /** + * Compose an extractor with a method handle that receives the bindings + * + * @param target method handle to receive the bindings + * @param sentinel value to return when the extractor does not match + * @return the composed method handle + */ + default MethodHandle compose(MethodHandle target, Object sentinel) { + int count = descriptor().parameterCount(); + MethodHandle[] components = components(); + Class carrierType = tryMatch().type().returnType(); + Class resultType = target.type().returnType(); + + MethodHandle mh = MethodHandles.filterArguments(target, 0, components); + mh = MethodHandles.permuteArguments(mh, MethodType.methodType(resultType, carrierType), new int[count]); + mh = MethodHandles.guardWithTest(ExtractorImpl.MH_OBJECTS_NONNULL.asType(MethodType.methodType(boolean.class, carrierType)), + mh, + MethodHandles.dropArguments(MethodHandles.constant(resultType, sentinel), 0, carrierType)); + mh = MethodHandles.filterArguments(mh, 0, tryMatch()); + return mh; + } + + private static MethodType descriptor(Class targetType, MethodHandle[] components) { + Class[] paramTypes = Stream.of(components) + .map(mh -> mh.type().returnType()) + .toArray(Class[]::new); + return MethodType.methodType(targetType, paramTypes); + } + + private static MethodHandle carrierTryExtract(MethodType descriptor, MethodHandle[] components) { + MethodHandle carrierFactory = ExtractorCarriers.carrierFactory(descriptor); + int[] reorder = new int[descriptor.parameterCount()]; // default value is what we want already + + return MethodHandles.permuteArguments(MethodHandles.filterArguments(carrierFactory, 0, components), + MethodType.methodType(carrierFactory.type().returnType(), descriptor.returnType()), + reorder); + } + + /** + * Construct a partial method handle that uses the predicate as guardWithTest, + * which applies the target if the test succeeds, and returns null if the + * test fails. The resulting method handle is of the same type as the + * {@code target} method handle. + * @param target + * @param predicate + * @return + */ + private static MethodHandle partialize(MethodHandle target, MethodHandle predicate) { + Class targetType = target.type().parameterType(0); + Class carrierType = target.type().returnType(); + return MethodHandles.guardWithTest(predicate, + target, + MethodHandles.dropArguments(MethodHandles.constant(carrierType, null), + 0, targetType)); + } + + /** + * Construct a method handle that delegates to target, unless the nth argument + * is null, in which case it returns null + */ + private static MethodHandle bailIfNthNull(MethodHandle target, int n) { + MethodHandle test = ExtractorImpl.MH_OBJECTS_ISNULL.asType(ExtractorImpl.MH_OBJECTS_ISNULL.type().changeParameterType(0, target.type().parameterType(n))); + test = MethodHandles.permuteArguments(test, target.type().changeReturnType(boolean.class), n); + MethodHandle nullh = MethodHandles.dropArguments(MethodHandles.constant(target.type().returnType(), null), 0, target.type().parameterArray()); + return MethodHandles.guardWithTest(test, nullh, target); + } + + /** + * Create a total {@linkplain Extractor} with the given descriptor, which + * operates by feeding results into a factory method handle and returning + * the result. + * + * @param descriptor the descriptor + * @param digester the digester method handle + * @return the extractor + */ + public static Extractor of(MethodType descriptor, + MethodHandle digester) { + return new ExtractorImpl(descriptor, + MethodHandles.insertArguments(digester, + 1, ExtractorCarriers.carrierFactory(descriptor)), + ExtractorCarriers.carrierComponents(descriptor)); + } + + /** + * Create a total {@linkplain Extractor} for a target of type {@code targetType} + * and a given set of component method handles. + * + * @param targetType The type of the match target + * @param components The component method handles + * @return the extractor + */ + public static Extractor ofTotal(Class targetType, MethodHandle... components) { + MethodType descriptor = descriptor(targetType, components); + return new ExtractorImpl(descriptor, + carrierTryExtract(descriptor, components), + ExtractorCarriers.carrierComponents(descriptor)); + } + + /** + * Create a total {@linkplain Extractor} for a target of type {@code targetType} + * and a given set of component method handles, using itself as a carrier. + * + * @param targetType The type of the match target + * @param components The component method handles + * @return the extractor + */ + public static Extractor ofSelfTotal(Class targetType, MethodHandle... components) { + return new ExtractorImpl(descriptor(targetType, components), + MethodHandles.identity(targetType), components); + } + + /** + * Create a partial {@linkplain Extractor} for a given set of component + * method handles. + * + * @param targetType the target type + * @param predicate The match predicate + * @param components The component method handles + * @return the extractor + */ + public static Extractor ofPartial(Class targetType, MethodHandle predicate, MethodHandle... components) { + MethodType descriptor = descriptor(targetType, components); + MethodHandle carrierTryExtract = carrierTryExtract(descriptor, components); + return new ExtractorImpl(descriptor, + partialize(carrierTryExtract, predicate), + ExtractorCarriers.carrierComponents(descriptor)); + } + + /** + * Create a partial {@linkplain Extractor} for a given set of component + * method handles, using itself as a carrier. + * + * @param targetType the target type + * @param predicate The match predicate + * @param components The component method handles + * @return the extractor + */ + public static Extractor ofSelfPartial(Class targetType, MethodHandle predicate, MethodHandle... components) { + return new ExtractorImpl(descriptor(targetType, components), + partialize(MethodHandles.identity(targetType), predicate), + components); + } + + /** + * Create an {@linkplain Extractor} for a type pattern, with a single binding + * variable, whose target type is {@code Object} + * + * @param type the type to match against + * @return the {@linkplain Extractor} + */ + public static Extractor ofType(Class type) { + requireNonNull(type); + if (type.isPrimitive()) + throw new IllegalArgumentException("Reference type expected, found: " + type); + return new ExtractorImpl(MethodType.methodType(type, type), + ExtractorImpl.MH_OF_TYPE_HELPER.bindTo(type).asType(MethodType.methodType(type, type)), + MethodHandles.identity(type)); + } + + /** + * Create an {@linkplain Extractor} for a constant pattern + * + * @param o the constant + * @return the extractor + */ + public static Extractor ofConstant(Object o) { + Class type = o == null ? Object.class : o.getClass(); + MethodHandle match = partialize(MethodHandles.dropArguments(MethodHandles.constant(Object.class, Boolean.TRUE), 0, type), + MethodHandles.insertArguments(ExtractorImpl.MH_OBJECTS_EQUAL, 0, o) + .asType(MethodType.methodType(boolean.class, type))); + return new ExtractorImpl(MethodType.methodType(type), match); + } + + /** + * Create an {@linkplain Extractor} for a nullable type pattern, with a + * single binding variable, whose target type is {@code Object} + * + * @param type the type to match against + * @return the {@linkplain Extractor} + */ + public static Extractor ofTypeNullable(Class type) { + requireNonNull(type); + if (type.isPrimitive()) + throw new IllegalArgumentException("Reference type expected, found: " + type); + return new ExtractorImpl(MethodType.methodType(type, type), + ExtractorImpl.MH_OF_TYPE_NULLABLE_HELPER.bindTo(type).asType(MethodType.methodType(type, type)), + MethodHandles.identity(type)); + } + + /** + * Create an {@linkplain Extractor} that is identical to another {@linkplain Extractor}, + * but without the specified binding variables + * @param etor the original extractor + * @param positions which binding variables to drop + * @return the extractor + */ + public static Extractor dropBindings(Extractor etor, int... positions) { + MethodHandle[] mhs = etor.components(); + for (int position : positions) + mhs[position] = null; + mhs = Stream.of(mhs).filter(Objects::nonNull).toArray(MethodHandle[]::new); + return new ExtractorImpl(descriptor(etor.descriptor().returnType(), mhs), etor.tryMatch(), mhs); + } + + /** + * Adapt an extractor to a new target type + * + * @param e the extractor + * @param newTarget the new target type + * @return the new extractor + */ + public static Extractor adapt(Extractor e, Class newTarget) { + if (e.descriptor().returnType().isAssignableFrom(newTarget)) + return e; + MethodHandle tryMatch = partialize(e.tryMatch().asType(e.tryMatch().type().changeParameterType(0, newTarget)), + ExtractorImpl.MH_ADAPT_HELPER.bindTo(e.descriptor().returnType()) + .asType(MethodType.methodType(boolean.class, newTarget))); + return new ExtractorImpl(e.descriptor().changeReturnType(newTarget), + tryMatch, e.components()); + } + + /** + * Construct a nested extractor, which first matches the target to the + * outer extractor, and then matches the resulting bindings to the inner + * extractors (if not null). The resulting extractor is partial if any + * of the input extractors are; its target type is the target type of the + * outer extractor; and its bindings are the concatenation of the bindings + * of the outer extractor followed by the bindings of the non-null inner + * extractors. + * + * @param outer The outer extractor + * @param extractors The inner extractors, or null if no nested extraction + * for this outer binding is desired + * @return the nested extractor + */ + public static Extractor ofNested(Extractor outer, Extractor... extractors) { + int outerCount = outer.descriptor().parameterCount(); + Class outerCarrierType = outer.tryMatch().type().returnType(); + + // Adapt inners to types of outer bindings + for (int i = 0; i < extractors.length; i++) { + Extractor extractor = extractors[i]; + if (extractor.descriptor().returnType() != outer.descriptor().parameterType(i)) + extractors[i] = adapt(extractor, outer.descriptor().parameterType(i)); + } + + int[] innerPositions = IntStream.range(0, extractors.length) + .filter(i -> extractors[i] != null) + .toArray(); + MethodHandle[] innerComponents = Stream.of(extractors) + .filter(Objects::nonNull) + .map(Extractor::components) + .flatMap(Stream::of) + .toArray(MethodHandle[]::new); + MethodHandle[] innerTryMatches = Stream.of(extractors) + .filter(Objects::nonNull) + .map(e -> e.tryMatch()) + .toArray(MethodHandle[]::new); + Class[] innerCarriers = Stream.of(extractors) + .filter(Objects::nonNull) + .map(e -> e.tryMatch().type().returnType()) + .toArray(Class[]::new); + Class[] innerTypes = Stream.of(innerComponents) + .map(mh -> mh.type().returnType()) + .toArray(Class[]::new); + + MethodType descriptor = outer.descriptor().appendParameterTypes(innerTypes); + + MethodHandle mh = ExtractorCarriers.carrierFactory(descriptor); + mh = MethodHandles.filterArguments(mh, outerCount, innerComponents); + int[] spreadInnerCarriers = new int[outerCount + innerComponents.length]; + for (int i=0; i constantType, + MethodType descriptor, MethodHandle... components) throws Throwable { + return ofSelfTotal(descriptor.returnType(), components); + } + + /** + * Condy bootstrap for creating nested extractors + * + * @param lookup ignored + * @param invocationName ignored + * @param invocationType must be {@code Class} + * @param outer the outer extractor + * @param inners the inner extractors, null if no nesting is needed for this binding + * @return the nested extractor + */ + public static Extractor ofNested(MethodHandles.Lookup lookup, String invocationName, Class invocationType, + Extractor outer, Extractor... inners) { + return ofNested(outer, inners); + } + + /** + * Condy bootstrap for creating non-nullable type extractor + * + * @param lookup ignored + * @param invocationName ignored + * @param invocationType must be {@code Class} + * @param type the type + * @return the extractor + */ + public static Extractor ofType(MethodHandles.Lookup lookup, String invocationName, Class invocationType, + Class type) { + return ofType(type); + } + + /** + * Condy bootstrap for creating nullable type extractor + * + * @param lookup ignored + * @param invocationName ignored + * @param invocationType must be {@code Class} + * @param type the type + * @return the extractor + */ + public static Extractor ofTypeNullable(MethodHandles.Lookup lookup, String invocationName, Class invocationType, + Class type) { + return ofTypeNullable(type); + } + + /** + * Condy bootstrap for creating constant extractor + * + * @param lookup ignored + * @param invocationName ignored + * @param invocationType must be {@code Class} + * @param constant the constant + * @return the extractor + */ + public static Extractor ofConstant(MethodHandles.Lookup lookup, String invocationName, Class invocationType, + Object constant) { + return ofConstant(constant); + } + + /** + * Condy bootstrap for finding extractors + * + * @param lookup the lookup context + * @param constantName ignored + * @param constantType ignored + * @param owner the class containing the extractor + * @param descriptor the extractor descriptor + * @param name the extractor name + * @param refKind the kind of method + * @return the extractor + * @throws Throwable if something went wrong + */ + public static Extractor findExtractor(MethodHandles.Lookup lookup, String constantName, Class constantType, + Class owner, MethodType descriptor, String name, int refKind) throws Throwable { + String dd = descriptor.toMethodDescriptorString(); + String patternMethodName + = BytecodeName.toBytecodeName(String.format("$pattern$%s$%s", + (refKind == REF_newInvokeSpecial ? owner.getSimpleName() : name), + dd.substring(0, dd.indexOf(')') + 1))); + MethodType factoryDesc = MethodType.methodType(Extractor.class); + MethodHandle mh; + switch (refKind) { + case REF_invokeStatic: + case REF_newInvokeSpecial: + mh = lookup.findStatic(owner, patternMethodName, factoryDesc); + break; + case REF_invokeVirtual: + case REF_invokeInterface: + mh = lookup.findVirtual(owner, patternMethodName, factoryDesc); + break; + default: + throw new IllegalAccessException(Integer.toString(refKind)); + } + + return (Extractor) mh.invoke(); + } + + /** + * Bootstrap for extracting the {@code tryMatch} method handle from a {@linkplain Extractor} + * + * @param lookup ignored + * @param constantName ignored + * @param constantType Must be {@code MethodHandle.class} + * @param extractor the {@linkplain Extractor} + * @return the {@code tryMatch} method handle + */ + + + public static MethodHandle extractorTryMatch(MethodHandles.Lookup lookup, String constantName, Class constantType, + Extractor extractor) { + return extractor.tryMatch(); + } + + /** + * Bootstrap for extracting a {@code component} method handle from a {@linkplain Extractor} + * + * @param lookup ignored + * @param constantName ignored + * @param constantType Must be {@code MethodHandle.class} + * @param extractor the {@linkplain Extractor} + * @param i the component index + * @return the {@code component} method handle + */ + public static MethodHandle extractorComponent(MethodHandles.Lookup lookup, String constantName, Class constantType, + Extractor extractor, int i) { + return extractor.component(i); + } + +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/compiler/ExtractorCarriers.java 2019-03-22 01:09:23.712694248 +0100 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.compiler; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; + +/** + * PatternCarriers + */ +public class ExtractorCarriers { + + private static final CarrierFactory factory = CarrierFactories.DUMB; + + interface CarrierFactory { + MethodHandle constructor(MethodType methodType); + MethodHandle component(MethodType methodType, int component); + } + + static class DumbCarrier { + private final Object[] args; + + DumbCarrier(Object... args) { + this.args = args.clone(); + } + + Object get(int i) { + return args[i]; + } + } + + enum CarrierFactories implements CarrierFactory { + DUMB { + private final MethodHandle CARRIER_CTOR; + private final MethodHandle CARRIER_GET; + + { + try { + CARRIER_CTOR = MethodHandles.lookup().findConstructor(DumbCarrier.class, MethodType.methodType(void.class, Object[].class)); + CARRIER_GET = MethodHandles.lookup().findVirtual(DumbCarrier.class, "get", MethodType.methodType(Object.class, int.class)); + } + catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + @Override + public MethodHandle constructor(MethodType methodType) { + return CARRIER_CTOR.asType(methodType.changeReturnType(Object.class)); + } + + @Override + public MethodHandle component(MethodType methodType, int component) { + return MethodHandles.insertArguments(CARRIER_GET, 1, component) + .asType(MethodType.methodType(methodType.parameterType(component), Object.class)); + } + }, + DUMB_SINGLE { + // An optimization of DUMB, where we use the value itself as carrier when there is only one value + + @Override + public MethodHandle constructor(MethodType methodType) { + return methodType.parameterCount() == 1 ? MethodHandles.identity(methodType.parameterType(0)) : DUMB.constructor(methodType); + } + + @Override + public MethodHandle component(MethodType methodType, int component) { + return methodType.parameterCount() == 1 ? MethodHandles.identity(methodType.parameterType(0)) : DUMB.component(methodType, component); + } + } + } + + /** + * Returns a method handle with the given method type that instantiates + * a new carrier object. + * + * @param methodType the types of the carrier elements + * @return the carrier factory + */ + public static MethodHandle carrierFactory(MethodType methodType) { + return factory.constructor(methodType); + } + + /** + * Returns a method handle that accepts a carrier and returns the i'th component + * + * @param methodType the type of the carrier elements + * @param i the index of the component + * @return the component method handle + */ + public static MethodHandle carrierComponent(MethodType methodType, int i) { + return factory.component(methodType, i); + } + + /** + * Return all the components method handles for a carrier + * @param methodType the type of the carrier elements + * @return the component method handles + */ + public static MethodHandle[] carrierComponents(MethodType methodType) { + MethodHandle[] components = new MethodHandle[methodType.parameterCount()]; + Arrays.setAll(components, i -> factory.component(methodType, i)); + return components; + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java 2019-03-22 01:09:23.924693779 +0100 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.compiler; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.List; +import java.util.Objects; + +/** + * Non-public implementation of {@link Extractor} + */ +class ExtractorImpl implements Extractor { + private final MethodType descriptor; + private final MethodHandle tryMatch; + private final List components; + + // These are helpers for Extractors + static final MethodHandle MH_OF_TYPE_HELPER; + static final MethodHandle MH_OF_TYPE_NULLABLE_HELPER; + static final MethodHandle MH_ADAPT_HELPER; + static final MethodHandle MH_OBJECTS_ISNULL; + static final MethodHandle MH_OBJECTS_NONNULL; + static final MethodHandle MH_OBJECTS_EQUAL; + static { + try { + MH_OF_TYPE_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "ofTypeHelper", MethodType.methodType(Object.class, Class.class, Object.class)); + MH_OF_TYPE_NULLABLE_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "ofTypeNullableHelper", MethodType.methodType(Object.class, Class.class, Object.class)); + MH_ADAPT_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "adaptHelper", MethodType.methodType(boolean.class, Class.class, Object.class)); + MH_OBJECTS_ISNULL = MethodHandles.lookup().findStatic(Objects.class, "isNull", MethodType.methodType(boolean.class, Object.class)); + MH_OBJECTS_NONNULL = MethodHandles.lookup().findStatic(Objects.class, "nonNull", MethodType.methodType(boolean.class, Object.class)); + MH_OBJECTS_EQUAL = MethodHandles.lookup().findStatic(Objects.class, "equals", MethodType.methodType(boolean.class, Object.class, Object.class)); + } + catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + /** + * Construct an {@link Extractor} from components + * Constraints: + * - output of tryMatch must match input of components + * - input of tryMatch must match descriptor + * - output of components must match descriptor + * + * @param descriptor The {@code descriptor} method type + * @param tryMatch The {@code tryMatch} method handle + * @param components The {@code component} method handles + */ + ExtractorImpl(MethodType descriptor, MethodHandle tryMatch, MethodHandle... components) { + Class carrierType = tryMatch.type().returnType(); + if (descriptor.parameterCount() != components.length) + throw new IllegalArgumentException(String.format("MethodType %s arity should match component count %d", descriptor, components.length)); + if (!descriptor.returnType().equals(tryMatch.type().parameterType(0))) + throw new IllegalArgumentException(String.format("Descriptor %s should match tryMatch input %s", descriptor, tryMatch.type())); + for (int i = 0; i < components.length; i++) { + MethodHandle component = components[i]; + if (component.type().parameterCount() != 1 + || component.type().returnType().equals(void.class) + || !component.type().parameterType(0).equals(carrierType)) + throw new IllegalArgumentException("Invalid component descriptor " + component.type()); + if (!component.type().returnType().equals(descriptor.parameterType(i))) + throw new IllegalArgumentException(String.format("Descriptor %s should match %d'th component %s", descriptor, i, component)); + } + + this.descriptor = descriptor; + this.tryMatch = tryMatch; + this.components = List.of(components); + } + + @Override + public MethodHandle tryMatch() { + return tryMatch; + } + + @Override + public MethodHandle component(int i) { + return components.get(i); + } + + @Override + public MethodHandle[] components() { + return components.toArray(new MethodHandle[0]); + } + + @Override + public MethodType descriptor() { + return descriptor; + } + + private static Object ofTypeHelper(Class type, Object o) { + return o != null && type.isAssignableFrom(o.getClass()) ? o : null; + } + + private static Object ofTypeNullableHelper(Class type, Object o) { + return o == null || type.isAssignableFrom(o.getClass()) ? o : null; + } + + private static boolean adaptHelper(Class type, Object o) { + return o != null && type.isAssignableFrom(o.getClass()); + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/compiler/PatternSim.java 2019-03-22 01:09:24.136693306 +0100 @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.compiler; + +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Temporary scaffolding to allow matching / switching on constrained patterns + * without language support. + */ +public class PatternSim { + private static String DEFAULT_LABEL = ""; + + /** + * Simulator for statement switch + * @param label the switch label + * @param target the switch target + * @return the switch simulator + */ + public static StatementSwitch _switch(String label, Object target) { + return new SwitchImpl(target, label); + } + + /** + * Simulator for statement switch + * @param target the switch target + * @return the switch simulator + */ + public static StatementSwitch _switch(Object target) { + return new SwitchImpl(target, DEFAULT_LABEL); + } + + /** + * Simulator for expression switch + * @param label the switch label + * @param target the switch target + * @param the the return type + * @return the switch simulator + */ + public static ExpressionSwitch _expswitch(String label, Object target) { + return new ExprSwitchImpl<>(target, label); + } + + /** + * Simulator for expression switch + * @param target the switch target + * @param the the return type + * @return the switch simulator + */ + public static ExpressionSwitch _expswitch(Object target) { + return new ExprSwitchImpl<>(target, DEFAULT_LABEL); + } + + /** + * Simulator for continuing out of a switch + */ + public static void _continue() { + throw new ContinueSignal(DEFAULT_LABEL); + } + + /** + * Simulator for continuing out of the labeled switch + * + * @param label the label of the switch to continue at + */ + public static void _continue(String label) { + throw new ContinueSignal(label); + } + + /** + * Simulator type for statement switch + */ + public interface StatementSwitch { + /** + * Simulate a case of a statement switch + * @param pattern the pattern to match against + * @param action the success action + * @param the type of the binding variable + * @return the switch + */ + StatementSwitch _case(Supplier<_pattern> pattern, Consumer action); + + /** + * Simulate a case of a statement switch + * @param pattern the pattern to match against + * @param action the success action + * @param the type of the binding variable + * @return the switch + */ + StatementSwitch _case(Supplier<_pattern> pattern, Runnable action); + + /** + * Simulate the default clause of a statement switch + * @param r the action + * @return the switch + */ + StatementSwitch _default(Runnable r); + } + + /** + * Simulator type for expression switch + * @param the switch type + */ + public interface ExpressionSwitch { + /** + * Simulate a case of an expression switch + * @param pattern the pattern to match against + * @param action the success action + * @param the type of the binding variable + * @return the switch + */ + ExpressionSwitch _case(Supplier<_pattern> pattern, Function action); + + /** + * Simulate a case of an expression switch + * @param pattern the pattern to match against + * @param action the success action + * @param the type of the binding variable + * @return the switch + */ + ExpressionSwitch _case(Supplier<_pattern> pattern, Supplier action); + + /** + * Simulate the default clause of an expression switch + * @param r the action + * @return the switch + */ + ExpressionSwitch _default(Supplier r); + + /** + * Get the result of an expression switch + * @return the result + */ + T result(); + } + + /** + * Helper method for nested pattern in a statement switch + * @param target the nested target + * @param pattern the nested pattern + * @param action the success action + * @param the type of the nested target + */ + public static void _nest(Object target, Supplier<_pattern> pattern, Consumer action) { + Optional match = pattern.get().match(target); + if (match.isPresent()) + action.accept(match.get()); + else + throw new ContinueSignal("<$nested$>"); + } + + /** + * Helper method for nested pattern in a statement switch + * @param target the nested target + * @param pattern the nested pattern + * @param action the success action + * @param the type of the nested target + */ + public static void _nest(Object target, Supplier<_pattern> pattern, Runnable action) { + Optional match = pattern.get().match(target); + if (match.isPresent()) + action.run(); + else + throw new ContinueSignal("<$nested$>"); + } + + /** + * Helper method for nested pattern in an expression switch + * @param target the nested target + * @param pattern the nested pattern + * @param action the success action + * @param the type of the nested target + * @param the return type of the success action + * @return the return value of the success action + */ + public static T _expnest(Object target, Supplier<_pattern> pattern, Function action) { + Optional match = pattern.get().match(target); + if (match.isPresent()) + return action.apply(match.get()); + else + throw new ContinueSignal("<$nested$>"); + } + + /** + * Helper method for nested pattern in an expression switch + * @param target the nested target + * @param pattern the nested pattern + * @param action the success action + * @param the type of the nested target + * @param the return type of the success action + * @return the return value of the success action + */ + public static T _expnest(Object target, Supplier<_pattern> pattern, Supplier action) { + Optional match = pattern.get().match(target); + if (match.isPresent()) + return action.get(); + else + throw new ContinueSignal("<$nested$>"); + } + + @SuppressWarnings("serial") + static class ContinueSignal extends RuntimeException { + String label; + + ContinueSignal(String label) { + super(); + this.label = label; + } + + void maybeRethrow(String label) { + if (!this.label.equals(label)) + throw this; + } + } +} + +class SwitchImpl implements PatternSim.StatementSwitch { + private final Object target; + private final String label; + private boolean done = false; + + SwitchImpl(Object target, + String label) { + this.target = target; + this.label = label; + } + + public PatternSim.StatementSwitch _case(Supplier<_pattern> pattern, Consumer action) { + if (!done) { + Optional match = pattern.get().match(target); + if (match.isPresent()) { + try { + action.accept(match.get()); + done = true; + } + catch (PatternSim.ContinueSignal signal) { + signal.maybeRethrow(label); + } + } + } + return this; + } + + public PatternSim.StatementSwitch _case(Supplier<_pattern> pattern, Runnable action) { + if (!done) { + Optional match = pattern.get().match(target); + if (match.isPresent()) { + try { + action.run(); + done = true; + } + catch (PatternSim.ContinueSignal signal) { + signal.maybeRethrow(label); + } + } + } + return this; + } + + public PatternSim.StatementSwitch _default(Runnable r) { + if (!done) { + try { + r.run(); + done = true; + } + catch (PatternSim.ContinueSignal signal) { + signal.maybeRethrow(label); + } + } + return this; + } + +} + +class ExprSwitchImpl implements PatternSim.ExpressionSwitch { + private final Object target; + private final String label; + private boolean done = false; + private T result = null; + + ExprSwitchImpl(Object target, + String label) { + this.target = target; + this.label = label; + } + + public PatternSim.ExpressionSwitch _case(Supplier<_pattern> pattern, Function action) { + if (!done) { + Optional match = pattern.get().match(target); + if (match.isPresent()) { + try { + result = action.apply(match.get()); + done = true; + } + catch (PatternSim.ContinueSignal signal) { + signal.maybeRethrow(label); + } + } + } + return this; + } + + @Override + public PatternSim.ExpressionSwitch _case(Supplier<_pattern> pattern, Supplier action) { + if (!done) { + Optional match = pattern.get().match(target); + if (match.isPresent()) { + try { + result = action.get(); + done = true; + } + catch (PatternSim.ContinueSignal signal) { + signal.maybeRethrow(label); + } + } + } + return this; + } + + public PatternSim.ExpressionSwitch _default(Supplier r) { + if (!done) { + try { + result = r.get(); + done = true; + } + catch (PatternSim.ContinueSignal signal) { + signal.maybeRethrow(label); + } + } + return this; + } + + public T result() { + return result; + } + +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/compiler/SwitchBootstraps.java 2019-03-22 01:09:24.348692834 +0100 @@ -0,0 +1,882 @@ +/* + * Copyright (c) 2017, 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.compiler; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +/** + * Bootstrap methods for linking {@code invokedynamic} call sites that implement + * the selection functionality of the {@code switch} statement. The bootstraps + * take additional static arguments corresponding to the {@code case} labels + * of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}. + * + *

    The bootstrap call site accepts a single parameter of the type of the + * operand of the {@code switch}, and return an {@code int} that is the index of + * the matched {@code case} label, {@code -1} if the target is {@code null}, + * or {@code N} if the target is not null but matches no {@code case} label. + */ +public class SwitchBootstraps { + + // Shared INIT_HOOK for all switch call sites; looks the target method up in a map + private static final MethodHandle CONSTANT_INIT_HOOK; + private static final MethodHandle PATTERN_INIT_HOOK; + private static final MethodHandle TYPE_INIT_HOOK; + private static final MethodHandle PATTERN_SWITCH_METHOD; + private static final MethodHandle TYPE_SWITCH_METHOD; + private static final Map, MethodHandle> switchMethods = new ConcurrentHashMap<>(); + + private static final Set> BOOLEAN_TYPES + = Set.of(boolean.class, Boolean.class); + // Types that can be handled as int switches + private static final Set> INT_TYPES + = Set.of(int.class, short.class, byte.class, char.class, + Integer.class, Short.class, Byte.class, Character.class); + private static final Set> FLOAT_TYPES + = Set.of(float.class, Float.class); + private static final Set> LONG_TYPES + = Set.of(long.class, Long.class); + private static final Set> DOUBLE_TYPES + = Set.of(double.class, Double.class); + + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private static final Function, MethodHandle> lookupSwitchMethod = + new Function<>() { + @Override + public MethodHandle apply(Class c) { + try { + Class switchClass; + if (c == Enum.class) + switchClass = EnumSwitchCallSite.class; + else if (c == String.class) + switchClass = StringSwitchCallSite.class; + else if (BOOLEAN_TYPES.contains(c) || INT_TYPES.contains(c) || + FLOAT_TYPES.contains(c)) + switchClass = IntSwitchCallSite.class; + else if (LONG_TYPES.contains(c) || DOUBLE_TYPES.contains(c)) + switchClass = LongSwitchCallSite.class; + else if (c == Object.class) + switchClass = TypeSwitchCallSite.class; + else + throw new BootstrapMethodError("Invalid switch type: " + c); + + return LOOKUP.findVirtual(switchClass, "doSwitch", + MethodType.methodType(int.class, c)); + } + catch (ReflectiveOperationException e) { + throw new BootstrapMethodError("Invalid switch type: " + c); + } + } + }; + + static { + try { + CONSTANT_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "constantInitHook", + MethodType.methodType(MethodHandle.class, CallSite.class)); + PATTERN_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "patternInitHook", + MethodType.methodType(MethodHandle.class, CallSite.class)); + TYPE_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "typeInitHook", + MethodType.methodType(MethodHandle.class, CallSite.class)); + PATTERN_SWITCH_METHOD = LOOKUP.findVirtual(PatternSwitchCallSite.class, "doSwitch", + MethodType.methodType(PatternSwitchResult.class, Object.class)); + TYPE_SWITCH_METHOD = LOOKUP.findVirtual(TypeSwitchCallSite.class, "doSwitch", + MethodType.methodType(int.class, Object.class)); + } + catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static MethodHandle constantInitHook(T receiver) { + return switchMethods.computeIfAbsent(receiver.type().parameterType(0), lookupSwitchMethod) + .bindTo(receiver); + } + + private static MethodHandle typeInitHook(T receiver) { + return TYPE_SWITCH_METHOD.bindTo(receiver); + } + + private static MethodHandle patternInitHook(T receiver) { + return PATTERN_SWITCH_METHOD.bindTo(receiver); + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on a {@code boolean} or {@code Boolean}. + * The static arguments are a varargs array of {@code boolean} labels, + * + *

    The results are undefined if the labels array contains duplicates. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter which is + * {@code boolean} or {@code Boolean},and return {@code int}. + * When used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param booleanLabels boolean values corresponding to the case labels of the + * {@code switch} statement. + * @return the index into {@code booleanLabels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code booleanLabels.length} if the target value does + * not match any of the labels. + * @throws NullPointerException if any required argument is null + * @throws IllegalArgumentException if the invocation type is not + * {@code (boolean)int} or {@code (Boolean)int} + * @throws Throwable if there is any error linking the call site + */ + public static CallSite booleanSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + boolean... booleanLabels) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!BOOLEAN_TYPES.contains(invocationType.parameterType(0)))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(booleanLabels); + + int[] intLabels = IntStream.range(0, booleanLabels.length) + .map(i -> booleanLabels[i] ? 1 : 0) + .toArray(); + + assert IntStream.of(intLabels).distinct().count() == intLabels.length + : "switch labels are not distinct: " + Arrays.toString(booleanLabels); + + return new IntSwitchCallSite(invocationType, intLabels); + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on an {@code int}, {@code short}, {@code byte}, + * {@code char}, or one of their box types. The static arguments are a + * varargs array of {@code int} labels. + * + *

    The results are undefined if the labels array contains duplicates. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter which is + * one of the 32-bit or shorter primitive types, or + * one of their box types, and return {@code int}. When + * used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param intLabels integral values corresponding to the case labels of the + * {@code switch} statement. + * @return the index into {@code intLabels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code intLabels.length} if the target value does + * not match any of the labels. + * @throws NullPointerException if any required argument is null + * @throws IllegalArgumentException if the invocation type is not + * {@code (T)int}, where {@code T} is one of the 32-bit or smaller integral + * primitive types, or one of their box types + * @throws Throwable if there is any error linking the call site + */ + public static CallSite intSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + int... intLabels) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!INT_TYPES.contains(invocationType.parameterType(0)))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(intLabels); + + assert IntStream.of(intLabels).distinct().count() == intLabels.length + : "switch labels are not distinct: " + Arrays.toString(intLabels); + + return new IntSwitchCallSite(invocationType, intLabels); + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on an {@code float} or {@code Float}. + * The static arguments are a varargs array of {@code float} labels. + * + *

    The results are undefined if the labels array contains duplicates + * according to {@link Float#floatToIntBits(float)}. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter which is + * one of the 32-bit or shorter primitive types, or + * one of their box types, and return {@code int}. When + * used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param floatLabels float values corresponding to the case labels of the + * {@code switch} statement. + * @return the index into {@code floatLabels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code floatLabels.length} if the target value does + * not match any of the labels. + * @throws NullPointerException if any required argument is null + * @throws IllegalArgumentException if the invocation type is not + * {@code (float)int} or {@code (Float)int} + * @throws Throwable if there is any error linking the call site + */ + public static CallSite floatSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + float... floatLabels) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!FLOAT_TYPES.contains(invocationType.parameterType(0)))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(floatLabels); + + int[] intLabels = new int[floatLabels.length]; + for (int i=0; i intLabels[a])) + .mapToInt(Integer::intValue) + .toArray(); + labels = new int[indexes.length]; + for (int i=0; i= 0) ? indexes[index] : indexes.length; + } + + int doSwitch(boolean target) { + return doSwitch(target ? 1 : 0); + } + + int doSwitch(float target) { + return doSwitch(Float.floatToIntBits(target)); + } + + int doSwitch(short target) { + return doSwitch((int) target); + } + + int doSwitch(byte target) { + return doSwitch((int) target); + } + + int doSwitch(char target) { + return doSwitch((int) target); + } + + int doSwitch(Boolean target) { + return (target == null) ? -1 : doSwitch((boolean) target); + } + + int doSwitch(Integer target) { + return (target == null) ? -1 : doSwitch((int) target); + } + + int doSwitch(Float target) { + return (target == null) ? -1 : doSwitch((float) target); + } + + int doSwitch(Short target) { + return (target == null) ? -1 : doSwitch((int) target); + } + + int doSwitch(Character target) { + return (target == null) ? -1 : doSwitch((int) target); + } + + int doSwitch(Byte target) { + return (target == null) ? -1 : doSwitch((int) target); + } + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on a {@code long} or {@code Long}. + * The static arguments are a varargs array of {@code long} labels. + * + *

    The results are undefined if the labels array contains duplicates. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter which is + * one of the 32-bit or shorter primitive types, or + * one of their box types, and return {@code int}. When + * used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param longLabels long values corresponding to the case labels of the + * {@code switch} statement. + * @return the index into {@code longLabels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code longLabels.length} if the target value does + * not match any of the labels. + * @throws NullPointerException if any required argument is null + * @throws IllegalArgumentException if the invocation type is not + * {@code (long)int} or {@code (Long)int} + * @throws Throwable if there is any error linking the call site + */ + public static CallSite longSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + long... longLabels) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!LONG_TYPES.contains(invocationType.parameterType(0)))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(longLabels); + + assert LongStream.of(longLabels).distinct().count() == longLabels.length + : "switch labels are not distinct: " + Arrays.toString(longLabels); + + return new LongSwitchCallSite(invocationType, longLabels); + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on a {@code double} or {@code Double}. + * The static arguments are a varargs array of {@code double} labels. + * + *

    The results are undefined if the labels array contains duplicates + * according to {@link Double#doubleToLongBits(double)}. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter which is + * one of the 32-bit or shorter primitive types, or + * one of their box types, and return {@code int}. When + * used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param doubleLabels long values corresponding to the case labels of the + * {@code switch} statement. + * @return the index into {@code doubleLabels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code doubleLabels.length} if the target value does + * not match any of the labels. + * @throws NullPointerException if any required argument is null + * @throws IllegalArgumentException if the invocation type is not + * {@code (double)int} or {@code (Double)int} + * @throws Throwable if there is any error linking the call site + */ + public static CallSite doubleSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + double... doubleLabels) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!DOUBLE_TYPES.contains(invocationType.parameterType(0)))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(doubleLabels); + + long[] longLabels = new long[doubleLabels.length]; + for (int i=0; i longLabels[a])) + .mapToInt(Integer::intValue) + .toArray(); + labels = new long[indexes.length]; + for (int i=0; i= 0) ? indexes[index] : indexes.length; + } + + int doSwitch(double target) { + return doSwitch(Double.doubleToLongBits(target)); + } + + int doSwitch(Long target) { + return (target == null) ? -1 : doSwitch((long) target); + } + + int doSwitch(Double target) { + return (target == null) ? -1 : doSwitch((double) target); + } + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on a {@code String} target. The static + * arguments are a varargs array of {@code String} labels. + * + *

    The results are undefined if the labels array contains duplicates + * according to {@link String#equals(Object)}. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter of + * {@code String}, and return {@code int}. When + * used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param stringLabels non-null string values corresponding to the case + * labels of the {@code switch} statement. + * @return the index into {@code labels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code stringLabels.length} if the target value + * does not match any of the labels. + * @throws NullPointerException if any required argument is null + * @throws IllegalArgumentException if any labels are null, or if the + * invocation type is not {@code (String)int} + * @throws Throwable if there is any error linking the call site + */ + public static CallSite stringSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + String... stringLabels) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!invocationType.parameterType(0).equals(String.class))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(stringLabels); + if (Stream.of(stringLabels).anyMatch(Objects::isNull)) + throw new IllegalArgumentException("null label found"); + + assert Stream.of(stringLabels).distinct().count() == stringLabels.length + : "switch labels are not distinct: " + Arrays.toString(stringLabels); + + return new StringSwitchCallSite(invocationType, stringLabels); + } + + static class StringSwitchCallSite extends ConstantCallSite { + private static final Comparator STRING_BY_HASH + = Comparator.comparingInt(Objects::hashCode); + + private final String[] sortedByHash; + private final int[] indexes; + private final boolean collisions; + + StringSwitchCallSite(MethodType targetType, + String[] stringLabels) throws Throwable { + super(targetType, CONSTANT_INIT_HOOK); + + // expensive way to index an array + indexes = IntStream.range(0, stringLabels.length) + .boxed() + .sorted(Comparator.comparingInt(i -> stringLabels[i].hashCode())) + .mapToInt(Integer::intValue) + .toArray(); + sortedByHash = new String[indexes.length]; + for (int i=0; i sortedByHash[i].hashCode() == sortedByHash[i + 1].hashCode()); + } + + int doSwitch(String target) { + if (target == null) + return -1; + + int index = Arrays.binarySearch(sortedByHash, target, STRING_BY_HASH); + if (index < 0) + return indexes.length; + else if (target.equals(sortedByHash[index])) { + return indexes[index]; + } + else if (collisions) { + int hash = target.hashCode(); + while (index > 0 && sortedByHash[index-1].hashCode() == hash) + --index; + for (; index < sortedByHash.length && sortedByHash[index].hashCode() == hash; index++) + if (target.equals(sortedByHash[index])) + return indexes[index]; + } + + return indexes.length; + } + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements a {@code switch} on an {@code Enum} target. The static + * arguments are the enum class, and a varargs arrays of {@code String} + * that are the names of the enum constants corresponding to the + * {@code case} labels. + * + *

    The results are undefined if the names array contains duplicates. + * + * @implNote + * + * The implementation only enforces the requirement that the labels array + * be duplicate-free if system assertions are enabled. + * + * @param the enum type + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code invokedynamic}, + * this is stacked automatically by the VM. + * @param invocationName The invocation name, which is ignored. When used with + * {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param invocationType The invocation type of the {@code CallSite}. This + * method type should have a single parameter of + * {@code Enum}, and return {@code int}. When + * used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param enumClass the enum class + * @param enumNames names of the enum constants against which the target + * should be matched + * @return the index into {@code labels} of the target value, if the target + * matches any of the labels, {@literal -1} if the target value is + * {@code null}, or {@code stringLabels.length} if the target value + * does not match any of the labels. + * @throws IllegalArgumentException if the specified class is not an + * enum class, or any label name is null, + * or if the invocation type is not + * {@code (Enum)int} + * @throws NullPointerException if any required argument is null + * @throws Throwable if there is any error linking the call site + */ + public static> CallSite enumSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + Class enumClass, + String... enumNames) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || (!invocationType.parameterType(0).equals(Enum.class))) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(enumClass); + requireNonNull(enumNames); + if (!enumClass.isEnum()) + throw new IllegalArgumentException("not an enum class"); + if (Stream.of(enumNames).anyMatch(Objects::isNull)) + throw new IllegalArgumentException("null label found"); + + assert Stream.of(enumNames).distinct().count() == enumNames.length + : "switch labels are not distinct: " + Arrays.toString(enumNames); + + return new EnumSwitchCallSite<>(invocationType, enumClass, enumNames); + } + + static class EnumSwitchCallSite> extends ConstantCallSite { + private final int[] ordinalMap; + + EnumSwitchCallSite(MethodType targetType, + Class enumClass, + String... enumNames) throws Throwable { + super(targetType, CONSTANT_INIT_HOOK); + + ordinalMap = new int[enumClass.getEnumConstants().length]; + Arrays.fill(ordinalMap, enumNames.length); + + for (int i=0; i... types) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(int.class)) + || invocationType.parameterType(0).isPrimitive()) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(types); + + types = types.clone(); + if (Stream.of(types).anyMatch(Objects::isNull)) + throw new IllegalArgumentException("null label found"); + + assert Stream.of(types).distinct().count() == types.length + : "switch labels are not distinct: " + Arrays.toString(types); + + return new TypeSwitchCallSite(invocationType, types); + } + + static class TypeSwitchCallSite extends ConstantCallSite { + private final Class[] types; + + TypeSwitchCallSite(MethodType targetType, + Class[] types) throws Throwable { + super(targetType, TYPE_INIT_HOOK); + this.types = types; + } + + int doSwitch(Object target) { + if (target == null) + return -1; + + // Dumbest possible strategy + Class targetClass = target.getClass(); + for (int i = 0; i < types.length; i++) { + Class c = types[i]; + if (c.isAssignableFrom(targetClass)) + return i; + } + + return types.length; + } + } + + /** + * Result type for pattern switches + */ + public static class PatternSwitchResult { + /** + * The selected index, -1 if input was null, or length if not matched + */ + public final int index; + + /** + * The carrier + */ + public final Object carrier; + + /** + * Construct a PatternSwitchResult + * + * @param index the index + * @param carrier the carrier + */ + public PatternSwitchResult(int index, Object carrier) { + this.index = index; + this.carrier = carrier; + } + } + + /** + * Bootstrap for pattern switches + * + * @param lookup the lookup (ignored) + * @param invocationName the invocation name (ignored) + * @param invocationType the invocation type (must return PatternSwitchResult) + * @param patterns the patterns + * @return the result + * @throws Throwable if something went wrong + */ + public static CallSite patternSwitch(MethodHandles.Lookup lookup, + String invocationName, + MethodType invocationType, + Extractor... patterns) throws Throwable { + if (invocationType.parameterCount() != 1 + || (!invocationType.returnType().equals(PatternSwitchResult.class)) + || invocationType.parameterType(0).isPrimitive()) + throw new IllegalArgumentException("Illegal invocation type " + invocationType); + requireNonNull(patterns); + + patterns = patterns.clone(); + Class targetType = invocationType.parameterType(0); + + for (int i = 0; i < patterns.length; i++) { + Extractor pattern = patterns[i]; + if (pattern.descriptor().returnType() != targetType) + patterns[i] = Extractor.adapt(pattern, targetType); + } + + if (Stream.of(patterns).anyMatch(Objects::isNull)) + throw new IllegalArgumentException("null pattern found"); + + return new PatternSwitchCallSite(invocationType, patterns); + } + + static class PatternSwitchCallSite extends ConstantCallSite { + private final Extractor[] patterns; + + PatternSwitchCallSite(MethodType targetType, + Extractor[] patterns) throws Throwable { + super(targetType, PATTERN_INIT_HOOK); + this.patterns = patterns; + } + + PatternSwitchResult doSwitch(Object target) throws Throwable { + if (target == null) + return new PatternSwitchResult(-1, null); + + // Dumbest possible strategy + for (int i = 0; i < patterns.length; i++) { + Extractor e = patterns[i]; + Object o = e.tryMatch().invoke(target); + if (o != null) + return new PatternSwitchResult(i, o); + } + + return new PatternSwitchResult(patterns.length, null); + + } + } +} --- /dev/null 2019-01-26 14:41:39.448009659 +0100 +++ new/src/java.base/share/classes/java/lang/compiler/_pattern.java 2019-03-22 01:09:24.568692344 +0100 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.compiler; + +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Temporary scaffolding to allow declaration of constrained patterns + * without language support. + */ +public interface _pattern { + /** + * Attempt to match + * @param o the target + * @return the result, or an empty optional + */ + Optional match(Object o); + + /** + * Construct a PatternDecl for a partial pattern + * @param predicate the applicability test + * @param extract the extraction logic + * @param the type of a successful target + * @param the type of the binding + * @return the PatternDecl + */ + @SuppressWarnings("unchecked") + static _pattern of(Predicate predicate, Function extract) { + return (Object o) -> + (predicate.test((T) o)) + ? Optional.of(extract.apply((T) o)) + : Optional.empty(); + } + + /** + * Construct a PatternDecl for a total pattern on Object + * @param extract the extraction logic + * @param the type of the binding + * @return the PatternDecl + */ + static _pattern of(Function extract) { + return of(o -> true, extract); + } + + /** + * Construct a PatternDecl for a type test pattern + * @param clazz The type to test against + * @param extract the extraction logic + * @param the type of a successful target + * @param the type of the binding + * @return the PatternDecl + */ + static _pattern ofType(Class clazz, Function extract) { + return of(o -> clazz.isAssignableFrom(o.getClass()), + o -> extract.apply(clazz.cast(o))); + } + + /** + * Construct a PatternDecl for a type test pattern + * @param clazz The type to test against + * @param the type of a successful target + * @return the PatternDecl + */ + static _pattern ofType(Class clazz) { + return of(o -> clazz.isAssignableFrom(o.getClass()), clazz::cast); + } + + /** + * Construct a PatternDecl for a constant + * @param constant the constant + * @param the type of the constant + * @return the PatternDecl