OpenJDK / jigsaw / jake / jdk
changeset 3029:74f844c02cdd
Merge
author | lana |
---|---|
date | Fri, 12 Nov 2010 10:49:52 -0800 |
parents | 9491a74b842e ecab7eefb8f2 |
children | 286b14273037 |
files | src/share/classes/java/dyn/JavaMethodHandle.java src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java src/share/classes/sun/java2d/pisces/LineSink.java src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java test/java/nio/channels/AsynchronousDatagramChannel/Basic.java |
diffstat | 239 files changed, 18392 insertions(+), 5487 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Wed Nov 03 14:12:45 2010 -0700 +++ b/.hgtags Fri Nov 12 10:49:52 2010 -0800 @@ -91,3 +91,5 @@ e250cef36ea05e627e7e6f7d75e5e19f529e2ba3 jdk7-b114 449bad8d67b5808ecf0f927683acc0a5940f8c85 jdk7-b115 1657ed4e1d86c8aa2028ab5a41f9da1ac4a369f8 jdk7-b116 +3e6726bbf80a4254ecd01051c8ed77ee19325e46 jdk7-b117 +b357910aa04aead2a16b6d6ff395a8df4b51d1dd jdk7-b118
--- a/make/java/java/FILES_java.gmk Wed Nov 03 14:12:45 2010 -0700 +++ b/make/java/java/FILES_java.gmk Fri Nov 12 10:49:52 2010 -0800 @@ -413,6 +413,7 @@ java/io/FilePermission.java \ java/io/Serializable.java \ java/io/Externalizable.java \ + java/io/SerialCallbackContext.java \ java/io/Bits.java \ java/io/ObjectInput.java \ java/io/ObjectInputStream.java \
--- a/make/java/jli/Makefile Wed Nov 03 14:12:45 2010 -0700 +++ b/make/java/jli/Makefile Fri Nov 12 10:49:52 2010 -0800 @@ -148,14 +148,9 @@ # ifeq ($(PLATFORM), windows) -STATIC_LIBRARY_DIR = $(OBJDIR)/static -STATIC_LIBRARY_NAME = $(LIBPREFIX)$(LIBRARY).lib -STATIC_LIBRARY = $(STATIC_LIBRARY_DIR)/$(STATIC_LIBRARY_NAME) +STATIC_LIBRARY = $(OBJDIR)/static/$(LIBPREFIX)$(LIBRARY).lib -$(STATIC_LIBRARY_DIR): $(OBJDIR) - @$(MKDIR) $(STATIC_LIBRARY_DIR) - -$(STATIC_LIBRARY): $(STATIC_LIBRARY_DIR) +$(STATIC_LIBRARY): $(FILES_o) @$(prep-target) $(LIBEXE) -nologo -out:$@ $(FILES_o)
--- a/make/java/nio/FILES_java.gmk Wed Nov 03 14:12:45 2010 -0700 +++ b/make/java/nio/FILES_java.gmk Fri Nov 12 10:49:52 2010 -0800 @@ -33,7 +33,6 @@ java/nio/channels/AsynchronousByteChannel.java \ java/nio/channels/AsynchronousChannel.java \ java/nio/channels/AsynchronousChannelGroup.java \ - java/nio/channels/AsynchronousDatagramChannel.java \ java/nio/channels/AsynchronousFileChannel.java \ java/nio/channels/AsynchronousServerSocketChannel.java \ java/nio/channels/AsynchronousSocketChannel.java \ @@ -207,7 +206,6 @@ sun/nio/ch/SelChImpl.java \ sun/nio/ch/ServerSocketAdaptor.java \ sun/nio/ch/ServerSocketChannelImpl.java \ - sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java \ sun/nio/ch/SinkChannelImpl.java \ sun/nio/ch/SocketAdaptor.java \ sun/nio/ch/SocketChannelImpl.java \
--- a/make/netbeans/jmx/build.properties Wed Nov 03 14:12:45 2010 -0700 +++ b/make/netbeans/jmx/build.properties Fri Nov 12 10:49:52 2010 -0800 @@ -48,8 +48,8 @@ jar.jmx.sealed = true jar.jmx.spec.title = JSR 003, 160, 255 - JMX API jar.jmx.spec.version = ${project.spec.version} -jar.jmx.spec.vendor = Sun Microsystems, Inc. -jar.jmx.impl.title = JSR 003, 160, 255 - OpenJDK 7 JMX API +jar.jmx.spec.vendor = Oracle Corporation +jar.jmx.impl.title = JSR 003, 160, 255 - OpenJDK 7 JMX API jar.jmx.impl.vendor = Project OpenJDK javadoc.options=-J-Xmx256m
--- a/src/share/bin/java.c Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/bin/java.c Fri Nov 12 10:49:52 2010 -0800 @@ -355,7 +355,6 @@ JavaVM *vm = 0; JNIEnv *env = 0; - jstring mainClassName; jclass mainClass; jmethodID mainID; jobjectArray mainArgs;
--- a/src/share/bin/parse_manifest.c Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/bin/parse_manifest.c Fri Nov 12 10:49:52 2010 -0800 @@ -72,7 +72,7 @@ if (entry->how == STORED) { *(char *)((size_t)in + entry->csize) = '\0'; if (size_out) { - *size_out = entry->csize; + *size_out = (int)entry->csize; } return (in); } else if (entry->how == DEFLATED) { @@ -103,7 +103,7 @@ return (NULL); } if (size_out) { - *size_out = entry->isize; + *size_out = (int)entry->isize; } return (out); } else @@ -317,7 +317,7 @@ * manifest. If so, build the entry record from the data found in * the header located and return success. */ - if (CENNAM(p) == JLI_StrLen(file_name) && + if ((size_t)CENNAM(p) == JLI_StrLen(file_name) && memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) { if (lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (off_t)0) { free(buffer); @@ -606,8 +606,5 @@ } free(mp); close(fd); - if (rc == 0) - return (0); - else - return (-2); + return (rc == 0) ? 0 : -2; }
--- a/src/share/bin/wildcard.c Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/bin/wildcard.c Fri Nov 12 10:49:52 2010 -0800 @@ -290,12 +290,12 @@ char *path; char *p; for (i = 0, size = 1; i < fl->size; i++) - size += JLI_StrLen(fl->files[i]) + 1; + size += (int)JLI_StrLen(fl->files[i]) + 1; path = JLI_MemAlloc(size); for (i = 0, p = path; i < fl->size; i++) { - int len = JLI_StrLen(fl->files[i]); + int len = (int)JLI_StrLen(fl->files[i]); if (i > 0) *p++ = sep; memcpy(p, fl->files[i], len); p += len; @@ -309,7 +309,7 @@ FileList_split(const char *path, char sep) { const char *p, *q; - int len = JLI_StrLen(path); + int len = (int)JLI_StrLen(path); int count; FileList fl; for (count = 1, p = path; p < path + len; p++) @@ -330,7 +330,7 @@ static int isJarFileName(const char *filename) { - int len = JLI_StrLen(filename); + int len = (int)JLI_StrLen(filename); return (len >= 4) && (filename[len - 4] == '.') && (equal(filename + len - 3, "jar") || @@ -342,8 +342,8 @@ static char * wildcardConcat(const char *wildcard, const char *basename) { - int wildlen = JLI_StrLen(wildcard); - int baselen = JLI_StrLen(basename); + int wildlen = (int)JLI_StrLen(wildcard); + int baselen = (int)JLI_StrLen(basename); char *filename = (char *) JLI_MemAlloc(wildlen + baselen); /* Replace the trailing '*' with basename */ memcpy(filename, wildcard, wildlen-1); @@ -369,7 +369,7 @@ static int isWildcard(const char *filename) { - int len = JLI_StrLen(filename); + int len = (int)JLI_StrLen(filename); return (len > 0) && (filename[len - 1] == '*') && (len == 1 || IS_FILE_SEPARATOR(filename[len - 2])) &&
--- a/src/share/classes/com/sun/crypto/provider/AESCrypt.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/AESCrypt.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -253,7 +253,8 @@ for (j = 0; j < 8; j++) { if (AA[i][j] != 0) { AA[i][j] = (byte) - alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]; + alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) + % 255]; } } for (t = 0; t < 4; t++) {
--- a/src/share/classes/com/sun/crypto/provider/ARCFOURCipher.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/ARCFOURCipher.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -92,7 +92,8 @@ } // core crypt code. OFB style, so works for both encryption and decryption - private void crypt(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { + private void crypt(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) { if (is < 0) { // doFinal() was called, need to reset the cipher to initial state init(lastKey);
--- a/src/share/classes/com/sun/crypto/provider/DESedeCipher.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/DESedeCipher.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -31,8 +31,8 @@ import javax.crypto.spec.*; /** - * This class implements the DESede algorithm (DES-EDE, tripleDES) in its various - * modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, + * This class implements the DESede algorithm (DES-EDE, tripleDES) in + * its various modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, * <code>CBC</code>, <code>PCBC</code>) and padding schemes * (<code>PKCS5Padding</code>, <code>NoPadding</code>, * <code>ISO10126Padding</code>).
--- a/src/share/classes/com/sun/crypto/provider/DHPrivateKey.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/DHPrivateKey.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -190,7 +190,8 @@ ike.initCause(e); throw ike; } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException("Error parsing key encoding: " + e.getMessage()); + InvalidKeyException ike = new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage()); ike.initCause(e); throw ike; } @@ -300,7 +301,8 @@ DerInputStream in = new DerInputStream(this.key); this.x = in.getBigInteger(); } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException("Error parsing key encoding: " + e.getMessage()); + InvalidKeyException ike = new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage()); ike.initCause(e); throw ike; }
--- a/src/share/classes/com/sun/crypto/provider/DHPublicKey.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/DHPublicKey.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -180,7 +180,8 @@ throw new InvalidKeyException("Private-value length too big"); } catch (IOException e) { - throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); + throw new InvalidKeyException( + "Error parsing key encoding: " + e.toString()); } } @@ -281,7 +282,8 @@ DerInputStream in = new DerInputStream(this.key); this.y = in.getBigInteger(); } catch (IOException e) { - throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); + throw new InvalidKeyException( + "Error parsing key encoding: " + e.toString()); } }
--- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2010, 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 @@ -764,7 +764,8 @@ cf = (CertificateFactory)cfs.get(certType); } else { // create new certificate factory - cf = CertificateFactory.getInstance(certType); + cf = CertificateFactory.getInstance( + certType); // store the certificate factory so we can // reuse it later cfs.put(certType, cf); @@ -863,8 +864,9 @@ dis.readFully(actual); for (int i = 0; i < computed.length; i++) { if (computed[i] != actual[i]) { - throw new IOException("Keystore was tampered with, or " - + "password was incorrect"); + throw new IOException( + "Keystore was tampered with, or " + + "password was incorrect"); } } }
--- a/src/share/classes/com/sun/crypto/provider/OAEPParameters.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/OAEPParameters.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -139,7 +139,8 @@ if (!val.getOID().equals((Object) OID_MGF1)) { throw new IOException("Only MGF1 mgf is supported"); } - AlgorithmId params = AlgorithmId.parse(new DerValue(val.getEncodedParams())); + AlgorithmId params = AlgorithmId.parse( + new DerValue(val.getEncodedParams())); String mgfDigestName = convertToStandardName(params.getName()); if (mgfDigestName.equals("SHA-1")) { mgfSpec = MGF1ParameterSpec.SHA1; @@ -150,7 +151,8 @@ } else if (mgfDigestName.equals("SHA-512")) { mgfSpec = MGF1ParameterSpec.SHA512; } else { - throw new IOException("Unrecognized message digest algorithm"); + throw new IOException( + "Unrecognized message digest algorithm"); } } else if (data.isContextSpecific((byte) 0x02)) { // pSource algid
--- a/src/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -121,8 +121,8 @@ this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); } - private static byte[] deriveKey(final Mac prf, final byte[] password, byte[] salt, - int iterCount, int keyLengthInBit) { + private static byte[] deriveKey(final Mac prf, final byte[] password, + byte[] salt, int iterCount, int keyLengthInBit) { int keyLength = keyLengthInBit/8; byte[] key = new byte[keyLength]; try { @@ -155,8 +155,9 @@ if (this == obj) return true; if (this.getClass() != obj.getClass()) return false; SecretKey sk = (SecretKey)obj; - return prf.getAlgorithm().equalsIgnoreCase(sk.getAlgorithm()) && - Arrays.equals(password, sk.getEncoded()); + return prf.getAlgorithm().equalsIgnoreCase( + sk.getAlgorithm()) && + Arrays.equals(password, sk.getEncoded()); } }; prf.init(macKey);
--- a/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -206,7 +206,8 @@ (algo.equalsIgnoreCase("RC2")?"RC2_40":algo), "SunJCE"); } catch (GeneralSecurityException gse) { // should never happen - throw new RuntimeException("SunJCE provider is not configured properly"); + throw new RuntimeException( + "SunJCE provider is not configured properly"); } try { params.init(pbeSpec); @@ -316,7 +317,8 @@ try { paramSpec = params.getParameterSpec(PBEParameterSpec.class); } catch (InvalidParameterSpecException ipse) { - throw new InvalidAlgorithmParameterException("requires PBE parameters"); + throw new InvalidAlgorithmParameterException( + "requires PBE parameters"); } } implInit(opmode, key, paramSpec, random);
--- a/src/share/classes/com/sun/crypto/provider/SunJCE.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/SunJCE.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -423,15 +423,31 @@ /* * SSL/TLS mechanisms + * + * These are strictly internal implementations and may + * be changed at any time. These names were chosen + * because PKCS11/SunPKCS11 does not yet have TLS1.2 + * mechanisms, and it will cause calls to come here. */ put("KeyGenerator.SunTlsPrf", - "com.sun.crypto.provider.TlsPrfGenerator"); + "com.sun.crypto.provider.TlsPrfGenerator$V10"); + put("KeyGenerator.SunTls12Prf", + "com.sun.crypto.provider.TlsPrfGenerator$V12"); + + put("KeyGenerator.SunTlsMasterSecret", + "com.sun.crypto.provider.TlsMasterSecretGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12MasterSecret", + "SunTlsMasterSecret"); + + put("KeyGenerator.SunTlsKeyMaterial", + "com.sun.crypto.provider.TlsKeyMaterialGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12KeyMaterial", + "SunTlsKeyMaterial"); + put("KeyGenerator.SunTlsRsaPremasterSecret", - "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); - put("KeyGenerator.SunTlsMasterSecret", - "com.sun.crypto.provider.TlsMasterSecretGenerator"); - put("KeyGenerator.SunTlsKeyMaterial", - "com.sun.crypto.provider.TlsKeyMaterialGenerator"); + "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12RsaPremasterSecret", + "SunTlsRsaPremasterSecret"); return null; }
--- a/src/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -65,12 +65,14 @@ } this.spec = (TlsKeyMaterialParameterSpec)params; if ("RAW".equals(spec.getMasterSecret().getFormat()) == false) { - throw new InvalidAlgorithmParameterException("Key format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key format must be RAW"); } - protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0302)) { - throw new InvalidAlgorithmParameterException - ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + protocolVersion = (spec.getMajorVersion() << 8) + | spec.getMinorVersion(); + if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + throw new InvalidAlgorithmParameterException( + "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); } } @@ -80,8 +82,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsKeyMaterialGenerator must be initialized"); + throw new IllegalStateException( + "TlsKeyMaterialGenerator must be initialized"); } try { return engineGenerateKey0(); @@ -99,8 +101,8 @@ SecretKey clientMacKey = null; SecretKey serverMacKey = null; SecretKey clientCipherKey = null; + SecretKey serverCipherKey = null; IvParameterSpec clientIv = null; - SecretKey serverCipherKey = null; IvParameterSpec serverIv = null; int macLength = spec.getMacKeyLength(); @@ -109,21 +111,33 @@ int keyLength = spec.getCipherKeyLength(); int ivLength = spec.getIvLength(); - int keyBlockLen = macLength + keyLength + (isExportable ? 0 : ivLength); + int keyBlockLen = macLength + keyLength + + (isExportable ? 0 : ivLength); keyBlockLen <<= 1; byte[] keyBlock = new byte[keyBlockLen]; - MessageDigest md5 = MessageDigest.getInstance("MD5"); - MessageDigest sha = MessageDigest.getInstance("SHA1"); + // These may be used again later for exportable suite calculations. + MessageDigest md5 = null; + MessageDigest sha = null; // generate key block - if (protocolVersion >= 0x0301) { - // TLS + if (protocolVersion >= 0x0303) { + // TLS 1.2 byte[] seed = concat(serverRandom, clientRandom); - keyBlock = doPRF(masterSecret, LABEL_KEY_EXPANSION, seed, + keyBlock = doTLS12PRF(masterSecret, LABEL_KEY_EXPANSION, seed, + keyBlockLen, spec.getPRFHashAlg(), + spec.getPRFHashLength(), spec.getPRFBlockSize()); + } else if (protocolVersion >= 0x0301) { + // TLS 1.0/1.1 + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA1"); + byte[] seed = concat(serverRandom, clientRandom); + keyBlock = doTLS10PRF(masterSecret, LABEL_KEY_EXPANSION, seed, keyBlockLen, md5, sha); } else { // SSL + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA1"); keyBlock = new byte[keyBlockLen]; byte[] tmp = new byte[20]; @@ -169,6 +183,7 @@ String alg = spec.getCipherAlgorithm(); + // cipher keys byte[] clientKeyBytes = new byte[keyLength]; System.arraycopy(keyBlock, ofs, clientKeyBytes, 0, keyLength); ofs += keyLength; @@ -182,6 +197,7 @@ clientCipherKey = new SecretKeySpec(clientKeyBytes, alg); serverCipherKey = new SecretKeySpec(serverKeyBytes, alg); + // IV keys if needed. if (ivLength != 0) { tmp = new byte[ivLength]; @@ -194,21 +210,28 @@ serverIv = new IvParameterSpec(tmp); } } else { + // if exportable suites, calculate the alternate // cipher key expansion and IV generation - if (protocolVersion >= 0x0301) { + if (protocolVersion >= 0x0302) { + // TLS 1.1+ + throw new RuntimeException( + "Internal Error: TLS 1.1+ should not be negotiating" + + "exportable ciphersuites"); + } else if (protocolVersion == 0x0301) { + // TLS 1.0 byte[] seed = concat(clientRandom, serverRandom); - tmp = doPRF(clientKeyBytes, LABEL_CLIENT_WRITE_KEY, seed, + tmp = doTLS10PRF(clientKeyBytes, LABEL_CLIENT_WRITE_KEY, seed, expandedKeyLength, md5, sha); clientCipherKey = new SecretKeySpec(tmp, alg); - tmp = doPRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed, + tmp = doTLS10PRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed, expandedKeyLength, md5, sha); serverCipherKey = new SecretKeySpec(tmp, alg); if (ivLength != 0) { tmp = new byte[ivLength]; - byte[] block = doPRF(null, LABEL_IV_BLOCK, seed, + byte[] block = doTLS10PRF(null, LABEL_IV_BLOCK, seed, ivLength << 1, md5, sha); System.arraycopy(block, 0, tmp, 0, ivLength); clientIv = new IvParameterSpec(tmp); @@ -216,6 +239,7 @@ serverIv = new IvParameterSpec(tmp); } } else { + // SSLv3 tmp = new byte[expandedKeyLength]; md5.update(clientKeyBytes);
--- a/src/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -64,12 +64,14 @@ } this.spec = (TlsMasterSecretParameterSpec)params; if ("RAW".equals(spec.getPremasterSecret().getFormat()) == false) { - throw new InvalidAlgorithmParameterException("Key format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key format must be RAW"); } - protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0302)) { - throw new InvalidAlgorithmParameterException - ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + protocolVersion = (spec.getMajorVersion() << 8) + | spec.getMinorVersion(); + if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + throw new InvalidAlgorithmParameterException( + "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); } } @@ -79,8 +81,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsMasterSecretGenerator must be initialized"); + throw new IllegalStateException( + "TlsMasterSecretGenerator must be initialized"); } SecretKey premasterKey = spec.getPremasterSecret(); byte[] premaster = premasterKey.getEncoded(); @@ -103,7 +105,11 @@ if (protocolVersion >= 0x0301) { byte[] seed = concat(clientRandom, serverRandom); - master = doPRF(premaster, LABEL_MASTER_SECRET, seed, 48); + master = ((protocolVersion >= 0x0303) ? + doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48)); } else { master = new byte[48]; MessageDigest md5 = MessageDigest.getInstance("MD5"); @@ -124,7 +130,8 @@ } - return new TlsMasterSecretKey(master, premasterMajor, premasterMinor); + return new TlsMasterSecretKey(master, premasterMajor, + premasterMinor); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); } catch (DigestException e) {
--- a/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -37,11 +37,15 @@ /** * KeyGenerator implementation for the TLS PRF function. + * <p> + * This class duplicates the HMAC functionality (RFC 2104) with + * performance optimizations (e.g. XOR'ing keys with padding doesn't + * need to be redone for each HMAC operation). * * @author Andreas Sterbenz * @since 1.6 */ -public final class TlsPrfGenerator extends KeyGeneratorSpi { +abstract class TlsPrfGenerator extends KeyGeneratorSpi { // magic constants and utility functions, also used by other files // in this package @@ -69,8 +73,10 @@ * TLS HMAC "inner" and "outer" padding. This isn't a function * of the digest algorithm. */ - private static final byte[] HMAC_ipad = genPad((byte)0x36, 64); - private static final byte[] HMAC_opad = genPad((byte)0x5c, 64); + private static final byte[] HMAC_ipad64 = genPad((byte)0x36, 64); + private static final byte[] HMAC_ipad128 = genPad((byte)0x36, 128); + private static final byte[] HMAC_opad64 = genPad((byte)0x5c, 64); + private static final byte[] HMAC_opad128 = genPad((byte)0x5c, 128); // SSL3 magic mix constants ("A", "BB", "CCC", ...) final static byte[][] SSL3_CONST = genConst(); @@ -123,8 +129,8 @@ this.spec = (TlsPrfParameterSpec)params; SecretKey key = spec.getSecret(); if ((key != null) && ("RAW".equals(key.getFormat()) == false)) { - throw new InvalidAlgorithmParameterException - ("Key encoding format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key encoding format must be RAW"); } } @@ -132,17 +138,21 @@ throw new InvalidParameterException(MSG); } - protected SecretKey engineGenerateKey() { + SecretKey engineGenerateKey0(boolean tls12) { if (spec == null) { - throw new IllegalStateException - ("TlsPrfGenerator must be initialized"); + throw new IllegalStateException( + "TlsPrfGenerator must be initialized"); } SecretKey key = spec.getSecret(); byte[] secret = (key == null) ? null : key.getEncoded(); try { byte[] labelBytes = spec.getLabel().getBytes("UTF8"); int n = spec.getOutputLength(); - byte[] prfBytes = doPRF(secret, labelBytes, spec.getSeed(), n); + byte[] prfBytes = (tls12 ? + doTLS12PRF(secret, labelBytes, spec.getSeed(), n, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(secret, labelBytes, spec.getSeed(), n)); return new SecretKeySpec(prfBytes, "TlsPrf"); } catch (GeneralSecurityException e) { throw new ProviderException("Could not generate PRF", e); @@ -151,16 +161,67 @@ } } - static final byte[] doPRF(byte[] secret, byte[] labelBytes, byte[] seed, - int outputLength) throws NoSuchAlgorithmException, DigestException { + static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, + String prfHash, int prfHashLength, int prfBlockSize) + throws NoSuchAlgorithmException, DigestException { + if (prfHash == null) { + throw new NoSuchAlgorithmException("Unspecified PRF algorithm"); + } + MessageDigest prfMD = MessageDigest.getInstance(prfHash); + return doTLS12PRF(secret, labelBytes, seed, outputLength, + prfMD, prfHashLength, prfBlockSize); + } + + static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, + MessageDigest mdPRF, int mdPRFLen, int mdPRFBlockSize) + throws DigestException { + + if (secret == null) { + secret = B0; + } + + // If we have a long secret, digest it first. + if (secret.length > mdPRFBlockSize) { + secret = mdPRF.digest(secret); + } + + byte[] output = new byte[outputLength]; + byte [] ipad; + byte [] opad; + + switch (mdPRFBlockSize) { + case 64: + ipad = HMAC_ipad64.clone(); + opad = HMAC_opad64.clone(); + break; + case 128: + ipad = HMAC_ipad128.clone(); + opad = HMAC_opad128.clone(); + break; + default: + throw new DigestException("Unexpected block size."); + } + + // P_HASH(Secret, label + seed) + expand(mdPRF, mdPRFLen, secret, 0, secret.length, labelBytes, + seed, output, ipad, opad); + + return output; + } + + static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength) throws NoSuchAlgorithmException, + DigestException { MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest sha = MessageDigest.getInstance("SHA1"); - return doPRF(secret, labelBytes, seed, outputLength, md5, sha); + return doTLS10PRF(secret, labelBytes, seed, outputLength, md5, sha); } - static final byte[] doPRF(byte[] secret, byte[] labelBytes, byte[] seed, - int outputLength, MessageDigest md5, MessageDigest sha) - throws DigestException { + static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, MessageDigest md5, + MessageDigest sha) throws DigestException { /* * Split the secret into two halves S1 and S2 of same length. * S1 is taken from the first half of the secret, S2 from the @@ -183,10 +244,12 @@ byte[] output = new byte[outputLength]; // P_MD5(S1, label + seed) - expand(md5, 16, secret, 0, seclen, labelBytes, seed, output); + expand(md5, 16, secret, 0, seclen, labelBytes, seed, output, + HMAC_ipad64.clone(), HMAC_opad64.clone()); // P_SHA-1(S2, label + seed) - expand(sha, 20, secret, off, seclen, labelBytes, seed, output); + expand(sha, 20, secret, off, seclen, labelBytes, seed, output, + HMAC_ipad64.clone(), HMAC_opad64.clone()); return output; } @@ -201,16 +264,13 @@ * @param seed the seed * @param output the output array */ - private static final void expand(MessageDigest digest, int hmacSize, + private static void expand(MessageDigest digest, int hmacSize, byte[] secret, int secOff, int secLen, byte[] label, byte[] seed, - byte[] output) throws DigestException { + byte[] output, byte[] pad1, byte[] pad2) throws DigestException { /* * modify the padding used, by XORing the key into our copy of that * padding. That's to avoid doing that for each HMAC computation. */ - byte[] pad1 = HMAC_ipad.clone(); - byte[] pad2 = HMAC_opad.clone(); - for (int i = 0; i < secLen; i++) { pad1[i] ^= secret[i + secOff]; pad2[i] ^= secret[i + secOff]; @@ -275,7 +335,34 @@ } remaining -= k; } - } + /** + * A KeyGenerator implementation that supports TLS 1.2. + * <p> + * TLS 1.2 uses a different hash algorithm than 1.0/1.1 for the PRF + * calculations. As of 2010, there is no PKCS11-level support for TLS + * 1.2 PRF calculations, and no known OS's have an internal variant + * we could use. Therefore for TLS 1.2, we are updating JSSE to request + * a different provider algorithm: "SunTls12Prf". If we reused the + * name "SunTlsPrf", the PKCS11 provider would need be updated to + * fail correctly when presented with the wrong version number + * (via Provider.Service.supportsParameters()), and add the + * appropriate supportsParamters() checks into KeyGenerators (not + * currently there). + */ + static public class V12 extends TlsPrfGenerator { + protected SecretKey engineGenerateKey() { + return engineGenerateKey0(true); + } + } + + /** + * A KeyGenerator implementation that supports TLS 1.0/1.1. + */ + static public class V10 extends TlsPrfGenerator { + protected SecretKey engineGenerateKey() { + return engineGenerateKey0(false); + } + } }
--- a/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -69,8 +69,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsRsaPremasterSecretGenerator must be initialized"); + throw new IllegalStateException( + "TlsRsaPremasterSecretGenerator must be initialized"); } if (random == null) { random = new SecureRandom();
--- a/src/share/classes/com/sun/jmx/defaults/ServiceName.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/jmx/defaults/ServiceName.java Fri Nov 12 10:49:52 2010 -0800 @@ -76,9 +76,9 @@ /** * The vendor of the JMX specification implemented by this product. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_SPEC_VENDOR = "Sun Microsystems"; + public static final String JMX_SPEC_VENDOR = "Oracle Corporation"; /** * The name of this product implementing the JMX specification. @@ -91,7 +91,7 @@ * The name of the vendor of this product implementing the * JMX specification. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_IMPL_VENDOR = "Sun Microsystems"; + public static final String JMX_IMPL_VENDOR = "Oracle Corporation"; }
--- a/src/share/classes/com/sun/jmx/snmp/ServiceName.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/jmx/snmp/ServiceName.java Fri Nov 12 10:49:52 2010 -0800 @@ -27,7 +27,7 @@ /** * Used for storing default values used by SNMP Runtime services. - * <p><b>This API is a Sun Microsystems internal API and is subject + * <p><b>This API is an Oracle Corporation internal API and is subject * to change without notice.</b></p> */ public class ServiceName { @@ -144,16 +144,16 @@ /** * The vendor of the JMX specification implemented by this product. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_SPEC_VENDOR = "Sun Microsystems"; + public static final String JMX_SPEC_VENDOR = "Oracle Corporation"; /** * The name of the vendor of this product implementing the JMX specification. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_IMPL_VENDOR = "Sun Microsystems"; + public static final String JMX_IMPL_VENDOR = "Oracle Corporation"; /** * The build number of the current product version, of the form <CODE>rXX</CODE>.
--- a/src/share/classes/com/sun/management/package.html Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/com/sun/management/package.html Fri Nov 12 10:49:52 2010 -0800 @@ -30,7 +30,7 @@ </head> <body bgcolor="white"> -This package contains Sun Microsystem's platform extension to +This package contains Oracle Corporation's platform extension to the implementation of the <a href="{@docRoot}/../../../../api/java/lang/management/package-summary.html"> java.lang.management</a> API and also defines the management
--- a/src/share/classes/java/awt/GridBagConstraints.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/awt/GridBagConstraints.java Fri Nov 12 10:49:52 2010 -0800 @@ -126,7 +126,7 @@ /** * Place the component centered along the edge of its display area * associated with the start of a page for the current - * <code>ComponentOrienation</code>. Equal to NORTH for horizontal + * {@code ComponentOrientation}. Equal to NORTH for horizontal * orientations. */ public static final int PAGE_START = 19; @@ -134,7 +134,7 @@ /** * Place the component centered along the edge of its display area * associated with the end of a page for the current - * <code>ComponentOrienation</code>. Equal to SOUTH for horizontal + * {@code ComponentOrientation}. Equal to SOUTH for horizontal * orientations. */ public static final int PAGE_END = 20; @@ -142,7 +142,7 @@ /** * Place the component centered along the edge of its display area where * lines of text would normally begin for the current - * <code>ComponentOrienation</code>. Equal to WEST for horizontal, + * {@code ComponentOrientation}. Equal to WEST for horizontal, * left-to-right orientations and EAST for horizontal, right-to-left * orientations. */ @@ -151,7 +151,7 @@ /** * Place the component centered along the edge of its display area where * lines of text would normally end for the current - * <code>ComponentOrienation</code>. Equal to EAST for horizontal, + * {@code ComponentOrientation}. Equal to EAST for horizontal, * left-to-right orientations and WEST for horizontal, right-to-left * orientations. */ @@ -160,7 +160,7 @@ /** * Place the component in the corner of its display area where * the first line of text on a page would normally begin for the current - * <code>ComponentOrienation</code>. Equal to NORTHWEST for horizontal, + * {@code ComponentOrientation}. Equal to NORTHWEST for horizontal, * left-to-right orientations and NORTHEAST for horizontal, right-to-left * orientations. */ @@ -169,7 +169,7 @@ /** * Place the component in the corner of its display area where * the first line of text on a page would normally end for the current - * <code>ComponentOrienation</code>. Equal to NORTHEAST for horizontal, + * {@code ComponentOrientation}. Equal to NORTHEAST for horizontal, * left-to-right orientations and NORTHWEST for horizontal, right-to-left * orientations. */ @@ -178,7 +178,7 @@ /** * Place the component in the corner of its display area where * the last line of text on a page would normally start for the current - * <code>ComponentOrienation</code>. Equal to SOUTHWEST for horizontal, + * {@code ComponentOrientation}. Equal to SOUTHWEST for horizontal, * left-to-right orientations and SOUTHEAST for horizontal, right-to-left * orientations. */ @@ -187,7 +187,7 @@ /** * Place the component in the corner of its display area where * the last line of text on a page would normally end for the current - * <code>ComponentOrienation</code>. Equal to SOUTHEAST for horizontal, + * {@code ComponentOrientation}. Equal to SOUTHEAST for horizontal, * left-to-right orientations and SOUTHWEST for horizontal, right-to-left * orientations. */ @@ -437,7 +437,7 @@ * <code>LINE_START</code>, <code>LINE_END</code>, * <code>FIRST_LINE_START</code>, <code>FIRST_LINE_END</code>, * <code>LAST_LINE_START</code> and <code>LAST_LINE_END</code>. The - * baseline relvative values are: + * baseline relative values are: * <code>BASELINE</code>, <code>BASELINE_LEADING</code>, * <code>BASELINE_TRAILING</code>, * <code>ABOVE_BASELINE</code>, <code>ABOVE_BASELINE_LEADING</code>,
--- a/src/share/classes/java/awt/Scrollbar.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/awt/Scrollbar.java Fri Nov 12 10:49:52 2010 -0800 @@ -213,7 +213,8 @@ * The size of the <code>Scrollbar</code>'s bubble. * When a scroll bar is used to select a range of values, * the visibleAmount represents the size of this range. - * This is visually indicated by the size of the bubble. + * Depending on platform, this may be visually indicated + * by the size of the bubble. * * @serial * @see #getVisibleAmount @@ -637,6 +638,8 @@ * bubble (also called a thumb or scroll box), usually gives a * visual representation of the relationship of the visible * amount to the range of the scroll bar. + * Note that depending on platform, the value of the visible amount property + * may not be visually indicated by the size of the bubble. * <p> * The scroll bar's bubble may not be displayed when it is not * moveable (e.g. when it takes up the entire length of the @@ -670,6 +673,8 @@ * bubble (also called a thumb or scroll box), usually gives a * visual representation of the relationship of the visible * amount to the range of the scroll bar. + * Note that depending on platform, the value of the visible amount property + * may not be visually indicated by the size of the bubble. * <p> * The scroll bar's bubble may not be displayed when it is not * moveable (e.g. when it takes up the entire length of the
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/BootstrapMethod.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010, 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.dyn; + +import java.lang.annotation.*; + +/** + * Annotation on InvokeDynamic method calls which requests the JVM to use a specific + * <a href="package-summary.html#bsm">bootstrap method</a> + * to link the call. This annotation is not retained as such in the class file, + * but is transformed into a constant-pool entry for the invokedynamic instruction which + * specifies the desired bootstrap method. + * <p> + * If only the <code>value</code> is given, it must name a subclass of {@link CallSite} + * with a constructor which accepts a class, string, and method type. + * If the <code>value</code> and <code>name</code> are both given, there must be + * a static method in the given class of the given name which accepts a class, string, + * and method type, and returns a reference coercible to {@link CallSite}. + * <p> + * This annotation can be placed either on the return type of a single {@link InvokeDynamic} + * call (see examples) or else it can be placed on an enclosing class or method, where it + * determines a default bootstrap method for any {@link InvokeDynamic} calls which are not + * specifically annotated with a bootstrap method. + * Every {@link InvokeDynamic} call must be given a bootstrap method. + * <p> + * Examples: +<blockquote><pre> +@BootstrapMethod(value=MyLanguageRuntime.class, name="bootstrapDynamic") +String x = (String) InvokeDynamic.greet(); +//BSM => MyLanguageRuntime.bootstrapDynamic(Here.class, "greet", methodType(String.class)) +@BootstrapMethod(MyCallSite.class) +void example() throws Throwable { + InvokeDynamic.greet(); + //BSM => new MyCallSite(Here.class, "greet", methodType(void.class)) +} +</pre></blockquote> + * <p> + */ +@Target({ElementType.TYPE_USE, + // For defaulting every indy site within a class or method; cf. @SuppressWarnings: + ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR + }) +@Retention(RetentionPolicy.SOURCE) +public @interface BootstrapMethod { + /** The class containing the bootstrap method. */ + Class<?> value(); + + /** The name of the bootstrap method. + * If this is the empty string, an instance of the bootstrap class is created, + * and a constructor is invoked. + * Otherwise, there must be a static method of the required name. + */ + String name() default ""; // empty string denotes a constructor with 'new' + + /** The argument types of the bootstrap method, as passed out by the JVM. + * There is usually no reason to override the default. + */ + Class<?>[] arguments() default {Class.class, String.class, MethodType.class}; +}
--- a/src/share/classes/java/dyn/CallSite.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/CallSite.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,56 +25,26 @@ package java.dyn; -import sun.dyn.Access; -import sun.dyn.MemberName; -import sun.dyn.CallSiteImpl; +import sun.dyn.*; +import java.util.Collection; /** - * A {@code CallSite} reifies an {@code invokedynamic} instruction from bytecode, - * and controls its linkage. - * Every linked {@code CallSite} object corresponds to a distinct instance - * of the {@code invokedynamic} instruction, and vice versa. + * A {@code CallSite} is a holder for a variable {@link MethodHandle}, + * which is called its {@code target}. + * Every call to a {@code CallSite} is delegated to the site's current target. * <p> - * Every linked {@code CallSite} object has one state variable, - * a {@link MethodHandle} reference called the {@code target}. - * This reference is never null. Though it can change its value - * successive values must always have exactly the {@link MethodType method type} - * called for by the bytecodes of the associated {@code invokedynamic} instruction + * A call site is initially created in an <em>unlinked</em> state, + * which is distinguished by a null target variable. + * Before the call site may be invoked (and before certain other + * operations are attempted), the call site must be linked to + * a non-null target. * <p> - * It is the responsibility of each class's - * {@link Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method} - * to produce call sites which have been pre-linked to an initial target method. - * The required {@link MethodType type} for the target method is a parameter - * to each bootstrap method call. - * <p> - * The bootstrap method may elect to produce call sites of a - * language-specific subclass of {@code CallSite}. In such a case, - * the subclass may claim responsibility for initializing its target to - * a non-null value, by overriding {@link #initialTarget}. - * <p> - * An {@code invokedynamic} instruction which has not yet been executed - * is said to be <em>unlinked</em>. When an unlinked call site is executed, - * the containing class's bootstrap method is called to manufacture a call site, - * for the instruction. If the bootstrap method does not assign a non-null - * value to the new call site's target variable, the method {@link #initialTarget} - * is called to produce the new call site's first target method. - * <p> - * A freshly-created {@code CallSite} object is not yet in a linked state. - * An unlinked {@code CallSite} object reports null for its {@code callerClass}. - * When the JVM receives a {@code CallSite} object from a bootstrap method, - * it first ensures that its target is non-null and of the correct type. - * The JVM then links the {@code CallSite} object to the call site instruction, - * enabling the {@code callerClass} to return the class in which the instruction occurs. - * <p> - * Next, the JVM links the instruction to the {@code CallSite}, at which point - * any further execution of the {@code invokedynamic} instruction implicitly - * invokes the current target of the {@code CallSite} object. - * After this two-way linkage, both the instruction and the {@code CallSite} - * object are said to be linked. - * <p> - * This state of linkage continues until the method containing the - * dynamic call site is garbage collected, or the dynamic call site - * is invalidated by an explicit request. + * A call site may be <em>relinked</em> by changing its target. + * The new target must be non-null and must have the same + * {@linkplain MethodHandle#type() type} + * as the previous target. + * Thus, though a call site can be relinked to a series of + * successive targets, it cannot change its type. * <p> * Linkage happens once in the lifetime of any given {@code CallSite} object. * Because of call site invalidation, this linkage can be repeated for @@ -87,6 +57,10 @@ * Here is a sample use of call sites and bootstrap methods which links every * dynamic call site to print its arguments: <blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java --> +@BootstrapMethod(value=PrintArgsDemo.class, name="bootstrapDynamic") +static void test() throws Throwable { + InvokeDynamic.baz("baz arg", 2, 3.14); +} private static void printArgs(Object... args) { System.out.println(java.util.Arrays.deepToString(args)); } @@ -96,17 +70,16 @@ Class thisClass = lookup.lookupClass(); // (who am I?) printArgs = lookup.findStatic(thisClass, "printArgs", MethodType.methodType(void.class, Object[].class)); - Linkage.registerBootstrapMethod("bootstrapDynamic"); } private static CallSite bootstrapDynamic(Class caller, String name, MethodType type) { // ignore caller and name, but match the type: return new CallSite(MethodHandles.collectArguments(printArgs, type)); } </pre></blockquote> - * @see Linkage#registerBootstrapMethod(java.lang.Class, java.dyn.MethodHandle) * @author John Rose, JSR 292 EG */ public class CallSite + implements MethodHandleProvider { private static final Access IMPL_TOKEN = Access.getToken(); @@ -209,6 +182,7 @@ * {@code InvokeDynamicBootstrapError}, which in turn causes the * linkage of the {@code invokedynamic} instruction to terminate * abnormally. + * @deprecated transitional form defined in EDR but removed in PFD */ protected MethodHandle initialTarget(Class<?> callerClass, String name, MethodType type) { throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+name+type); @@ -278,16 +252,44 @@ */ @Override public String toString() { - StringBuilder buf = new StringBuilder("CallSite#"); - buf.append(hashCode()); - if (!isLinked()) - buf.append("[unlinked]"); - else - buf.append("[") - .append("from ").append(vmmethod.getDeclaringClass().getName()) - .append(" : ").append(getTarget().type()) - .append(" => ").append(getTarget()) - .append("]"); - return buf.toString(); + return "CallSite"+(target == null ? "" : target.type()); } + + /** + * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * Produce a method handle equivalent to an invokedynamic instruction + * which has been linked to this call site. + * <p>If this call site is a {@link ConstantCallSite}, this method + * simply returns the call site's target, since that will not change. + * <p>Otherwise, this method is equivalent to the following code: + * <p><blockquote><pre> + * MethodHandle getTarget, invoker, result; + * getTarget = MethodHandles.lookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class)); + * invoker = MethodHandles.exactInvoker(this.type()); + * result = MethodHandles.foldArguments(invoker, getTarget) + * </pre></blockquote> + * @return a method handle which always invokes this call site's current target + */ + public final MethodHandle dynamicInvoker() { + if (this instanceof ConstantCallSite) + return getTarget(); // will not change dynamically + MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this); + MethodHandle invoker = MethodHandles.exactInvoker(this.type()); + return MethodHandles.foldArguments(invoker, getTarget); + } + private static final MethodHandle GET_TARGET; + static { + try { + GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP. + findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); + } catch (NoAccessException ignore) { + throw new InternalError(); + } + } + + /** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */ + public final MethodHandle asMethodHandle() { return dynamicInvoker(); } + + /** Implementation of {@link MethodHandleProvider}, which returns {@code this.dynamicInvoker().asType(type)}. */ + public final MethodHandle asMethodHandle(MethodType type) { return dynamicInvoker().asType(type); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/ClassValue.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2010, 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.dyn; + +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Lazily associate a computed value with (potentially) every class. + * @author John Rose, JSR 292 EG + */ +public abstract class ClassValue<T> { + /** + * Compute the given class's derived value for this {@code ClassValue}. + * <p> + * This method will be invoked within the first thread that accesses + * the value with the {@link #get}. + * <p> + * Normally, this method is invoked at most once per class, + * but it may be invoked again in case of subsequent invocations + * of {@link #remove} followed by {@link #get}. + * + * @return the computed value for this thread-local + */ + protected abstract T computeValue(Class<?> type); + + /** + * Creates a new class value. + */ + protected ClassValue() { + } + + /** + * Returns the value for the given class. + * If no value has yet been computed, it is obtained by + * by an invocation of the {@link #computeValue} method. + * <p> + * The actual installation of the value on the class + * is performed while the class's synchronization lock + * is held. At that point, if racing threads have + * computed values, one is chosen, and returned to + * all the racing threads. + * + * @return the current thread's value of this thread-local + */ + public T get(Class<?> type) { + ClassValueMap map = getMap(type); + if (map != null) { + Object x = map.get(this); + if (x != null) { + return (T) map.unmaskNull(x); + } + } + return setComputedValue(type); + } + + /** + * Removes the associated value for the given class. + * If this value is subsequently {@linkplain #get read} for the same class, + * its value will be reinitialized by invoking its {@link #computeValue} method. + * This may result in an additional invocation of the + * {@code computeValue} method for the given class. + */ + public void remove(Class<?> type) { + ClassValueMap map = getMap(type); + if (map != null) { + synchronized (map) { + map.remove(this); + } + } + } + + /// Implementation... + + /** The hash code for this type is based on the identity of the object, + * and is well-dispersed for power-of-two tables. + */ + public final int hashCode() { return hashCode; } + private final int hashCode = HASH_CODES.getAndAdd(0x61c88647); + private static final AtomicInteger HASH_CODES = new AtomicInteger(); + + private static final AtomicInteger STORE_BARRIER = new AtomicInteger(); + + /** Slow path for {@link #get}. */ + private T setComputedValue(Class<?> type) { + ClassValueMap map = getMap(type); + if (map == null) { + map = initializeMap(type); + } + T value = computeValue(type); + STORE_BARRIER.lazySet(0); + // All stores pending from computeValue are completed. + synchronized (map) { + // Warm up the table with a null entry. + map.preInitializeEntry(this); + } + // All stores pending from table expansion are completed. + synchronized (map) { + value = (T) map.initializeEntry(this, value); + // One might fear a possible race condition here + // if the code for map.put has flushed the write + // to map.table[*] before the writes to the Map.Entry + // are done. This is not possible, since we have + // warmed up the table with an empty entry. + } + return value; + } + + // Replace this map by a per-class slot. + private static final WeakHashMap<Class<?>, ClassValueMap> ROOT + = new WeakHashMap<Class<?>, ClassValueMap>(); + + private static ClassValueMap getMap(Class<?> type) { + return ROOT.get(type); + } + + private static ClassValueMap initializeMap(Class<?> type) { + synchronized (ClassValue.class) { + ClassValueMap map = ROOT.get(type); + if (map == null) + ROOT.put(type, map = new ClassValueMap()); + return map; + } + } + + static class ClassValueMap extends WeakHashMap<ClassValue, Object> { + /** Make sure this table contains an Entry for the given key, even if it is empty. */ + void preInitializeEntry(ClassValue key) { + if (!this.containsKey(key)) + this.put(key, null); + } + /** Make sure this table contains a non-empty Entry for the given key. */ + Object initializeEntry(ClassValue key, Object value) { + Object prior = this.get(key); + if (prior != null) { + return unmaskNull(prior); + } + this.put(key, maskNull(value)); + return value; + } + + Object maskNull(Object x) { + return x == null ? this : x; + } + Object unmaskNull(Object x) { + return x == this ? null : x; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/ConstantCallSite.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010, 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.dyn; + +/** + * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed. + * The only way to relink an {@code invokedynamic} instruction bound to a {@code ConstantCallSite} is + * to invalidate the instruction as a whole. + * @author John Rose, JSR 292 EG + */ +public class ConstantCallSite extends CallSite { + /** Create a call site with a permanent target. */ + public ConstantCallSite(MethodHandle target) { + super(target); + } + /** Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target. */ + @Override public final void setTarget(MethodHandle ignore) { + throw new IllegalArgumentException("ConstantCallSite"); + } +}
--- a/src/share/classes/java/dyn/InvokeDynamic.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/InvokeDynamic.java Fri Nov 12 10:49:52 2010 -0800 @@ -35,7 +35,7 @@ * The target method is a property of the reified {@linkplain CallSite call site object} * which is linked to each active {@code invokedynamic} instruction. * The call site object is initially produced by a - * {@linkplain java.dyn.Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method} + * {@linkplain BootstrapMethod bootstrap method} * associated with the class whose bytecodes include the dynamic call site. * <p> * The type {@code InvokeDynamic} has no particular meaning as a @@ -45,22 +45,31 @@ * It may be imported for ease of use. * <p> * Here are some examples: - * <p><blockquote><pre> - * Object x; String s; int i; - * x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object; - * s = InvokeDynamic.<String>hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String; - * InvokeDynamic.<void>cogito(); // cogito()V - * i = InvokeDynamic.<int>#"op:+"(2, 3); // "op:+"(II)I - * </pre></blockquote> +<blockquote><pre><!-- see indy-demo/src/JavaDocExamples.java --> +@BootstrapMethod(value=Here.class, name="bootstrapDynamic") +static void example() throws Throwable { + Object x; String s; int i; + x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object; + s = (String) InvokeDynamic.hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String; + InvokeDynamic.cogito(); // cogito()V + i = (int) InvokeDynamic.#"op:+"(2, 3); // "op:+"(II)I +} +static MethodHandle bootstrapDynamic(Class caller, String name, MethodType type) { ... } +</pre></blockquote> * Each of the above calls generates a single invokedynamic instruction * with the name-and-type descriptors indicated in the comments. + * <p> * The argument types are taken directly from the actual arguments, - * while the return type is taken from the type parameter. - * (This type parameter may be a primtive, and it defaults to {@code Object}.) + * while the return type corresponds to the target of the assignment. + * (Currently, the return type must be given as a false type parameter. + * This type parameter is an irregular use of the generic type syntax, + * and is likely to change in favor of a convention based on target typing.) + * <p> * The final example uses a special syntax for uttering non-Java names. * Any name legal to the JVM may be given between the double quotes. + * <p> * None of these calls is complete without a bootstrap method, - * which must be registered by the static initializer of the enclosing class. + * which must be declared for the enclosing class or method. * @author John Rose, JSR 292 EG */ @MethodHandle.PolymorphicSignature
--- a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Fri Nov 12 10:49:52 2010 -0800 @@ -28,15 +28,11 @@ /** * Thrown to indicate that an {@code invokedynamic} instruction has * failed to find its - * {@linkplain Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method}, + * {@linkplain BootstrapMethod bootstrap method}, * or the bootstrap method has * failed to provide a * {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target} * of the correct {@linkplain MethodType method type}. - * <p> - * The bootstrap method must have been declared during a class's initialization - * by a call to one of the overloadings of - * {@link Linkage#registerBootstrapMethod registerBootstrapMethod}. * * @author John Rose, JSR 292 EG * @since 1.7
--- a/src/share/classes/java/dyn/JavaMethodHandle.java Wed Nov 03 14:12:45 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 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.dyn; - -import sun.dyn.Access; - -/** - * A Java method handle is a deprecated proposal for extending - * the basic method handle type with additional - * programmer defined methods and fields. - * Its behavior as a method handle is determined at instance creation time, - * by providing the new instance with an "entry point" method handle - * to handle calls. This entry point must accept a leading argument - * whose type is the Java method handle itself or a supertype, and the - * entry point is always called with the Java method handle itself as - * the first argument. This is similar to ordinary virtual methods, which also - * accept the receiver object {@code this} as an implicit leading argument. - * The {@code MethodType} of the Java method handle is the same as that - * of the entry point method handle, with the leading parameter type - * omitted. - * <p> - * Here is an example of usage, creating a hybrid object/functional datum: - * <p><blockquote><pre> - * class Greeter extends JavaMethodHandle { - * private String greeting = "hello"; - * public void setGreeting(String s) { greeting = s; } - * public void run() { System.out.println(greeting+", "+greetee); } - * private final String greetee; - * Greeter(String greetee) { - * super(RUN); // alternatively, super("run") - * this.greetee = greetee; - * } - * // the entry point function is computed once: - * private static final MethodHandle RUN - * = MethodHandles.lookup().findVirtual(Greeter.class, "run", - * MethodType.make(void.class)); - * } - * // class Main { public static void main(String... av) { ... - * Greeter greeter = new Greeter("world"); - * greeter.run(); // prints "hello, world" - * // Statically typed method handle invocation (most direct): - * MethodHandle mh = greeter; - * mh.<void>invokeExact(); // also prints "hello, world" - * // Dynamically typed method handle invocation: - * MethodHandles.invokeExact(greeter); // also prints "hello, world" - * greeter.setGreeting("howdy"); - * mh.invokeExact(); // prints "howdy, world" (object-like mutable behavior) - * </pre></blockquote> - * <p> - * In the example of {@code Greeter}, the method {@code run} provides the entry point. - * The entry point need not be a constant value; it may be independently - * computed in each call to the constructor. The entry point does not - * even need to be a method on the {@code Greeter} class, though - * that is the typical case. - * <p> - * The entry point may also be provided symbolically, in which case the the - * {@code JavaMethodHandle} constructor performs the lookup of the entry point. - * This makes it possible to use {@code JavaMethodHandle} to create an anonymous - * inner class: - * <p><blockquote><pre> - * // We can also do this with symbolic names and/or inner classes: - * MethodHandles.invokeExact(new JavaMethodHandle("yow") { - * void yow() { System.out.println("yow, world"); } - * }); - * </pre></blockquote> - * <p> - * Here is similar lower-level code which works in terms of a bound method handle. - * <p><blockquote><pre> - * class Greeter { - * public void run() { System.out.println("hello, "+greetee); } - * private final String greetee; - * Greeter(String greetee) { this.greetee = greetee; } - * // the entry point function is computed once: - * private static final MethodHandle RUN - * = MethodHandles.findVirtual(Greeter.class, "run", - * MethodType.make(void.class)); - * } - * // class Main { public static void main(String... av) { ... - * Greeter greeter = new Greeter("world"); - * greeter.run(); // prints "hello, world" - * MethodHandle mh = MethodHanndles.insertArgument(Greeter.RUN, 0, greeter); - * mh.invokeExact(); // also prints "hello, world" - * </pre></blockquote> - * Note that the method handle must be separately created as a view on the base object. - * This increases footprint, complexity, and dynamic indirections. - * <p> - * Here is a pure functional value expressed most concisely as an anonymous inner class: - * <p><blockquote><pre> - * // class Main { public static void main(String... av) { ... - * final String greetee = "world"; - * MethodHandle greeter = new JavaMethodHandle("run") { - * private void run() { System.out.println("hello, "+greetee); } - * } - * greeter.invokeExact(); // prints "hello, world" - * </pre></blockquote> - * <p> - * Here is an abstract parameterized lvalue, efficiently expressed as a subtype of MethodHandle, - * and instantiated as an anonymous class. The data structure is a handle to 1-D array, - * with a specialized index type (long). It is created by inner class, and uses - * signature-polymorphic APIs throughout. - * <p><blockquote><pre> - * abstract class AssignableMethodHandle extends JavaMethodHandle { - * private final MethodHandle setter; - * public MethodHandle setter() { return setter; } - * public AssignableMethodHandle(String get, String set) { - * super(get); - * MethodType getType = this.type(); - * MethodType setType = getType.insertParameterType(getType.parameterCount(), getType.returnType()).changeReturnType(void.class); - * this.setter = MethodHandles.publicLookup().bind(this, set, setType); - * } - * } - * // class Main { public static void main(String... av) { ... - * final Number[] stuff = { 123, 456 }; - * AssignableMethodHandle stuffPtr = new AssignableMethodHandle("get", "set") { - * public Number get(long i) { return stuff[(int)i]; } - * public void set(long i, Object x) { stuff[(int)i] = x; } - * } - * int x = (Integer) stuffPtr.<Number>invokeExact(1L); // 456 - * stuffPtr.setter().<void>invokeExact(0L, (Number) 789); // replaces 123 with 789 - * </pre></blockquote> - * @see MethodHandle - * @deprecated The JSR 292 EG intends to replace {@code JavaMethodHandle} with - * an interface-based API for mixing method handle behavior with other classes. - * @author John Rose, JSR 292 EG - */ -public abstract class JavaMethodHandle - // Note: This is an implementation inheritance hack, and will be removed - // with a JVM change which moves the required hidden behavior onto this class. - extends sun.dyn.BoundMethodHandle -{ - private static final Access IMPL_TOKEN = Access.getToken(); - - /** - * When creating a {@code JavaMethodHandle}, the actual method handle - * invocation behavior will be delegated to the specified {@code entryPoint}. - * This may be any method handle which can take the newly constructed object - * as a leading parameter. - * <p> - * The method handle type of {@code this} (i.e, the fully constructed object) - * will be {@code entryPoint}, minus the leading argument. - * The leading argument will be bound to {@code this} on every method - * handle invocation. - * @param entryPoint the method handle to handle calls - */ - protected JavaMethodHandle(MethodHandle entryPoint) { - super(entryPoint); - } - - /** - * Create a method handle whose entry point is a non-static method - * visible in the exact (most specific) class of - * the newly constructed object. - * <p> - * The method is specified by name and type, as if via this expression: - * {@code MethodHandles.lookup().findVirtual(this.getClass(), name, type)}. - * The class defining the method might be an anonymous inner class. - * <p> - * The method handle type of {@code this} (i.e, the fully constructed object) - * will be the given method handle type. - * A call to {@code this} will invoke the selected method. - * The receiver argument will be bound to {@code this} on every method - * handle invocation. - * <p> - * <i>Rationale:</i> - * Although this constructor may seem to be a mere luxury, - * it is not subsumed by the more general constructor which - * takes any {@code MethodHandle} as the entry point argument. - * In order to convert an entry point name to a method handle, - * the self-class of the object is required (in order to do - * the lookup). The self-class, in turn, is generally not - * available at the time of the constructor invocation, - * due to the rules of Java and the JVM verifier. - * One cannot call {@code this.getClass()}, because - * the value of {@code this} is inaccessible at the point - * of the constructor call. (Changing this would require - * change to the Java language, verifiers, and compilers.) - * In particular, this constructor allows {@code JavaMethodHandle}s - * to be created in combination with the anonymous inner class syntax. - * @param entryPointName the name of the entry point method - * @param type (optional) the desired type of the method handle - */ - protected JavaMethodHandle(String entryPointName, MethodType type) { - super(entryPointName, type, true); - - } - - /** - * Create a method handle whose entry point is a non-static method - * visible in the exact (most specific) class of - * the newly constructed object. - * <p> - * The method is specified only by name. - * There must be exactly one method of that name visible in the object class, - * either inherited or locally declared. - * (That is, the method must not be overloaded.) - * <p> - * The method handle type of {@code this} (i.e, the fully constructed object) - * will be the same as the type of the selected non-static method. - * The receiver argument will be bound to {@code this} on every method - * handle invocation. - * <p>ISSUE: This signature wildcarding feature does not correspond to - * any MethodHandles.Lookup API element. Can we eliminate it? - * Alternatively, it is useful for naming non-overloaded methods. - * Shall we make type arguments optional in the Lookup methods, - * throwing an error in cases of ambiguity? - * <p> - * For this method's rationale, see the documentation - * for {@link #JavaMethodHandle(String,MethodType)}. - * @param entryPointName the name of the entry point method - */ - protected JavaMethodHandle(String entryPointName) { - super(entryPointName, (MethodType) null, false); - } -}
--- a/src/share/classes/java/dyn/Linkage.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/Linkage.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,7 +25,6 @@ package java.dyn; -import java.lang.annotation.Annotation; import java.dyn.MethodHandles.Lookup; import java.util.WeakHashMap; import sun.dyn.Access; @@ -56,11 +55,7 @@ * <li>the class containing the {@code invokedynamic} instruction, for which the bootstrap method was registered * <li>the name of the method being invoked (a {@link String}) * <li>the type of the method being invoked (a {@link MethodType}) - * <li><em>TBD</em> optionally, an unordered array of {@link Annotation}s attached to the call site - * <em>(Until this feature is implemented, this will always receive an empty array.)</em> * </ul> - * <em>(TBD: The final argument type may be missing from the method handle's type. - * Additional arguments may be added in the future.)</em> * The bootstrap method acts as a factory method which accepts the given arguments * and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}). * <p> @@ -86,6 +81,7 @@ * or is already running in another thread * @exception SecurityException if there is a security manager installed, * and a {@link LinkagePermission} check fails for "registerBootstrapMethod" + * @deprecated Use @{@link BootstrapMethod} annotations instead */ public static void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) { @@ -97,14 +93,9 @@ static private void checkBSM(MethodHandle mh) { if (mh == null) throw newIllegalArgumentException("null bootstrap method"); - if (mh.type() == BOOTSTRAP_METHOD_TYPE_2) - // For now, always pass an empty array for the Annotations argument - mh = MethodHandles.insertArguments(mh, BOOTSTRAP_METHOD_TYPE_2.parameterCount()-1, - (Object)NO_ANNOTATIONS); if (mh.type() == BOOTSTRAP_METHOD_TYPE) return; throw new WrongMethodTypeException(mh.toString()); } - static private final Annotation[] NO_ANNOTATIONS = { }; /** * <em>PROVISIONAL API, WORK IN PROGRESS:</em> @@ -115,6 +106,7 @@ * @throws NoSuchMethodException if there is no such method * @throws IllegalStateException if the caller class's static initializer * has already run, or is already running in another thread + * @deprecated Use @{@link BootstrapMethod} annotations instead */ public static void registerBootstrapMethod(Class<?> runtime, String name) { @@ -131,6 +123,7 @@ * @throws IllegalArgumentException if there is no such method * @throws IllegalStateException if the caller class's static initializer * has already run, or is already running in another thread + * @deprecated Use @{@link BootstrapMethod} annotations instead */ public static void registerBootstrapMethod(String name) { @@ -142,18 +135,10 @@ void registerBootstrapMethodLookup(Class<?> callerClass, Class<?> runtime, String name) { Lookup lookup = new Lookup(IMPL_TOKEN, callerClass); MethodHandle bootstrapMethod; - // Try both types. TBD try { - bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE_2); + bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE); } catch (NoAccessException ex) { - bootstrapMethod = null; - } - if (bootstrapMethod == null) { - try { - bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE); - } catch (NoAccessException ex) { - throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex); - } + throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex); } checkBSM(bootstrapMethod); MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod); @@ -172,6 +157,7 @@ * and the immediate caller of this method is not in the same * package as the caller class * and a {@link LinkagePermission} check fails for "getBootstrapMethod" + * @deprecated */ public static MethodHandle getBootstrapMethod(Class callerClass) { @@ -188,10 +174,6 @@ public static final MethodType BOOTSTRAP_METHOD_TYPE = MethodType.methodType(CallSite.class, Class.class, String.class, MethodType.class); - static final MethodType BOOTSTRAP_METHOD_TYPE_2 - = MethodType.methodType(CallSite.class, - Class.class, String.class, MethodType.class, - Annotation[].class); /** * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
--- a/src/share/classes/java/dyn/LinkagePermission.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/LinkagePermission.java Fri Nov 12 10:49:52 2010 -0800 @@ -31,6 +31,7 @@ import java.util.StringTokenizer; /** + * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * This class is for managing runtime permission checking for * operations performed by methods in the {@link Linkage} class. * Like a {@link RuntimePermission}, on which it is modeled, @@ -52,13 +53,6 @@ * </tr> * * <tr> - * <td>registerBootstrapMethod.{class name}</td> - * <td>Specifying a bootstrap method for {@code invokedynamic} instructions within a class of the given name</td> - * <td>An attacker could attempt to attach a bootstrap method to a class which - * has just been loaded, thus gaining control of its {@code invokedynamic} calls.</td> - * </tr> - * - * <tr> * <td>invalidateAll</td> * <td>Force the relinking of invokedynamic call sites everywhere.</td> * <td>This could allow an attacker to slow down the system, @@ -73,8 +67,9 @@ * <td>See {@code invalidateAll}.</td> * </tr> * </table> + * <p>ISSUE: Is this still needed? * - * @see java.security.RuntimePermission + * @see java.lang.RuntimePermission * @see java.lang.SecurityManager * * @author John Rose, JSR 292 EG @@ -86,7 +81,7 @@ /** * Create a new LinkagePermission with the given name. * The name is the symbolic name of the LinkagePermission, such as - * "registerBootstrapMethod", "invalidateCallerClass.*", etc. An asterisk + * "invalidateCallerClass.*", etc. An asterisk * may appear at the end of the name, following a ".", or by itself, to * signify a wildcard match. *
--- a/src/share/classes/java/dyn/MethodHandle.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/MethodHandle.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2010, 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 @@ -36,11 +36,13 @@ /** * A method handle is a typed, directly executable reference to a method, * constructor, field, or similar low-level operation, with optional - * conversion or substitution of arguments or return values. + * transformations of arguments or return values. + * (These transformations include conversion, insertion, deletion, + * substitution. See the methods of this class and of {@link MethodHandles}.) * <p> * Method handles are strongly typed according to signature. * They are not distinguished by method name or enclosing class. - * A method handle must be invoked under a signature which exactly matches + * A method handle must be invoked under a signature which matches * the method handle's own {@link MethodType method type}. * <p> * Every method handle confesses its type via the {@code type} accessor. @@ -174,9 +176,10 @@ * merely a documentation convention. These type parameters do * not play a role in type-checking method handle invocations. * <p> - * Note: Like classes and strings, method handles that correspond directly - * to fields and methods can be represented directly as constants to be - * loaded by {@code ldc} bytecodes. + * Like classes and strings, method handles that correspond to accessible + * fields, methods, and constructors can be represented directly + * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. + * Loading such a constant causes the component classes of its type to be loaded as necessary. * * @see MethodType * @see MethodHandles @@ -186,6 +189,7 @@ // Note: This is an implementation inheritance hack, and will be removed // with a JVM change which moves the required hidden state onto this class. extends MethodHandleImpl + implements MethodHandleProvider { private static Access IMPL_TOKEN = Access.getToken(); @@ -197,7 +201,7 @@ * those methods which are signature polymorphic. */ @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.TYPE}) - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @interface PolymorphicSignature { } private MethodType type; @@ -274,10 +278,14 @@ * and performing simple conversions for arguments and return types. * The signature at the call site of {@code invokeGeneric} must * have the same arity as this method handle's {@code type}. - * The same conversions are allowed on arguments or return values as are supported by - * by {@link MethodHandles#convertArguments}. + * <p> * If the call site signature exactly matches this method handle's {@code type}, * the call proceeds as if by {@link #invokeExact}. + * <p> + * Otherwise, the call proceeds as if this method handle were first + * adjusted by calling {@link #asType} to adjust this method handle + * to the required type, and then the call proceeds as if by + * {@link #invokeExact} on the adjusted method handle. */ public final native @PolymorphicSignature <R,A> R invokeGeneric(A... args) throws Throwable; @@ -538,4 +546,10 @@ public final MethodHandle bindTo(Object x) { return MethodHandles.insertArguments(this, 0, x); } + + /** Implementation of {@link MethodHandleProvider}, which returns {@code this}. */ + public final MethodHandle asMethodHandle() { return this; } + + /** Implementation of {@link MethodHandleProvider}, which returns {@code this.asType(type)}. */ + public final MethodHandle asMethodHandle(MethodType type) { return this.asType(type); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/MethodHandleProvider.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009, 2010, 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.dyn; + +/** + * An interface for an object to provide a target {@linkplain MethodHandle method handle} to a {@code invokedynamic} instruction. + * There are many function-like objects in various Java APIs. + * This interface provides a standard way for such function-like objects to be bound + * to a dynamic call site, by providing a view of their behavior in the form of a low-level method handle. + * <p> + * The type {@link MethodHandle} is a concrete class whose implementation + * hierarchy (if any) may be tightly coupled to the underlying JVM implementation. + * It cannot also serve as a base type for user-defined functional APIs. + * For this reason, {@code MethodHandle} cannot be subclassed to add new + * behavior to method handles. But this interface can be used to provide + * a link between a user-defined function and the {@code invokedynamic} + * instruction and the method handle API. + */ +public interface MethodHandleProvider { + /** Produce a method handle which will serve as a behavioral proxy for the current object. + * The type and invocation behavior of the proxy method handle are user-defined, + * and should have some relation to the intended meaning of the original object itself. + * <p> + * The current object may have a changeable behavior. + * For example, {@link CallSite} has a {@code setTarget} method which changes its invocation. + * In such a case, it is <em>incorrect</em> for {@code asMethodHandle} to return + * a method handle whose behavior may diverge from that of the current object. + * Rather, the returned method handle must stably and permanently access + * the behavior of the current object, even if that behavior is changeable. + * <p> + * The reference identity of the proxy method handle is not guaranteed to + * have any particular relation to the reference identity of the object. + * In particular, several objects with the same intended meaning could + * share a common method handle, or the same object could return different + * method handles at different times. In the latter case, the different + * method handles should have the same type and invocation behavior, + * and be usable from any thread at any time. + * In particular, if a MethodHandleProvider is bound to an <code>invokedynamic</code> + * call site, the proxy method handle extracted at the time of binding + * will be used for an unlimited time, until the call site is rebound. + * <p> + * The type {@link MethodHandle} itself implements {@code MethodHandleProvider}, and + * for this method simply returns {@code this}. + */ + public MethodHandle asMethodHandle(); + + /** Produce a method handle of a given type which will serve as a behavioral proxy for the current object. + * As for the no-argument version {@link #asMethodHandle()}, the invocation behavior of the + * proxy method handle is user-defined. But the type must be the given type, + * or else a {@link WrongMethodTypeException} must be thrown. + * <p> + * If the current object somehow represents a variadic or overloaded behavior, + * the method handle returned for a given type might represent only a subset of + * the current object's repertoire of behaviors, which correspond to that type. + */ + public MethodHandle asMethodHandle(MethodType type) throws WrongMethodTypeException; +}
--- a/src/share/classes/java/dyn/MethodHandles.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/MethodHandles.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,15 +25,12 @@ package java.dyn; -import java.lang.reflect.Constructor; +import java.lang.reflect.*; import sun.dyn.Access; import sun.dyn.MemberName; import sun.dyn.MethodHandleImpl; import sun.dyn.util.VerifyAccess; import sun.dyn.util.Wrapper; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.List; import java.util.ArrayList; import java.util.Arrays; @@ -81,6 +78,14 @@ * Return a {@link Lookup lookup object} which is trusted minimally. * It can only be used to create method handles to * publicly accessible fields and methods. + * <p> + * As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class} + * of this lookup object will be {@link java.lang.Object}. + * <p> + * The lookup class can be changed to any other class {@code C} using an expression of the form + * {@linkplain Lookup#in <code>publicLookup().in(C.class)</code>}. + * Since all classes have equal access to public names, + * such a change would confer no new access rights. */ public static Lookup publicLookup() { return Lookup.PUBLIC_LOOKUP; @@ -90,9 +95,10 @@ * A <em>lookup object</em> is a factory for creating method handles, * when the creation requires access checking. * Method handles do not perform - * access checks when they are called; this is a major difference + * access checks when they are called, but rather when they are created. + * (This is a major difference * from reflective {@link Method}, which performs access checking - * against every caller, on every call. + * against every caller, on every call.) * Therefore, method handle access * restrictions must be enforced when a method handle is created. * The caller class against which those restrictions are enforced @@ -107,7 +113,7 @@ * It may then use this factory to create method handles on * all of its methods, including private ones. * It may also delegate the lookup (e.g., to a metaobject protocol) - * by passing the {@code Lookup} object to other code. + * by passing the lookup object to other code. * If this other code creates method handles, they will be access * checked against the original lookup class, and not with any higher * privileges. @@ -125,23 +131,28 @@ * It can also fail if a security manager is installed and refuses * access. In any of these cases, an exception will be * thrown from the attempted lookup. + * <p> * In general, the conditions under which a method handle may be * created for a method {@code M} are exactly as restrictive as the conditions * under which the lookup class could have compiled a call to {@code M}. - * At least some of these error conditions are likely to be - * represented by checked exceptions in the final version of this API. + * This rule is applied even if the Java compiler might have created + * an wrapper method to access a private method of another class + * in the same top-level declaration. + * For example, a lookup object created for a nested class {@code C.D} + * can access private members within other related classes such as + * {@code C}, {@code C.D.E}, or {@code C.B}. */ public static final class Lookup { /** The class on behalf of whom the lookup is being performed. */ private final Class<?> lookupClass; - /** The allowed sorts of members which may be looked up (public, etc.), with STRICT for package. */ + /** The allowed sorts of members which may be looked up (public, etc.), with STATIC for package. */ private final int allowedModes; private static final int PUBLIC = Modifier.PUBLIC, - PACKAGE = Modifier.STRICT, + PACKAGE = Modifier.STATIC, PROTECTED = Modifier.PROTECTED, PRIVATE = Modifier.PRIVATE, ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE), @@ -155,8 +166,10 @@ /** Which class is performing the lookup? It is this class against * which checks are performed for visibility and access permissions. * <p> - * This value is null if and only if this lookup was produced - * by {@link MethodHandles#publicLookup}. + * The class implies a maximum level of access permission, + * but the permissions may be additionally limited by the bitmask + * {@link #lookupModes}, which controls whether non-public members + * can be accessed. */ public Class<?> lookupClass() { return lookupClass; @@ -168,10 +181,15 @@ } /** Which types of members can this lookup object produce? - * The result is a bit-mask of the modifier bits PUBLIC, PROTECTED, PRIVATE, and STRICT. - * The modifier bit STRICT stands in for the (non-existent) package protection mode. + * The result is a bit-mask of the {@link Modifier} bits + * {@linkplain Modifier#PUBLIC PUBLIC (0x01)}, + * {@linkplain Modifier#PROTECTED PROTECTED (0x02)}, + * {@linkplain Modifier#PRIVATE PRIVATE (0x04)}, + * and {@linkplain Modifier#STATIC STATIC (0x08)}. + * The modifier bit {@code STATIC} stands in for the package protection mode, + * which does not have an explicit modifier bit. */ - int lookupModes() { + public int lookupModes() { return allowedModes & ALL_MODES; } @@ -621,32 +639,32 @@ /// Helper methods, all package-private. - MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) { + MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoAccessException { checkSymbolicClass(refc); // do this before attempting to resolve int mods = (isStatic ? Modifier.STATIC : 0); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); } - MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) { + MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoAccessException { checkSymbolicClass(refc); // do this before attempting to resolve int mods = (isStatic ? Modifier.STATIC : 0); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull()); } MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic, - boolean searchSupers, Class<?> specialCaller) { + boolean searchSupers, Class<?> specialCaller) throws NoAccessException { checkSymbolicClass(refc); // do this before attempting to resolve int mods = (isStatic ? Modifier.STATIC : 0); return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller); } - void checkSymbolicClass(Class<?> refc) { + void checkSymbolicClass(Class<?> refc) throws NoAccessException { Class<?> caller = lookupClassOrNull(); if (caller != null && !VerifyAccess.isClassAccessible(refc, caller)) throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller); } - void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) { + void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws NoAccessException { String message; if (m.isConstructor()) message = "expected a method, not a constructor"; @@ -659,7 +677,7 @@ throw newNoAccessException(message, m, lookupClass()); } - void checkAccess(Class<?> refc, MemberName m) { + void checkAccess(Class<?> refc, MemberName m) throws NoAccessException { int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; int mods = m.getModifiers(); @@ -695,14 +713,14 @@ return "member is private to package"; } - void checkSpecialCaller(Class<?> specialCaller) { + void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException { if (allowedModes == TRUSTED) return; if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass())) throw newNoAccessException("no private access for invokespecial", new MemberName(specialCaller), lookupClass()); } - MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) { + MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException { // The accessing class only has the right to use a protected member // on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc. if (!method.isProtected() || method.isStatic() @@ -712,7 +730,7 @@ else return restrictReceiver(method, mh, lookupClass()); } - MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) { + MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws NoAccessException { assert(!method.isStatic()); Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide if (defc.isInterface() || !defc.isAssignableFrom(caller)) { @@ -898,11 +916,16 @@ * @return a method handle which always invokes the call site's target */ public static - MethodHandle dynamicInvoker(CallSite site) { + MethodHandle dynamicInvoker(CallSite site) throws NoAccessException { MethodHandle getCSTarget = GET_TARGET; - if (getCSTarget == null) - GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP. - findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); + if (getCSTarget == null) { + try { + GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP. + findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); + } catch (NoAccessException ex) { + throw new InternalError(); + } + } MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site); MethodHandle invoker = exactInvoker(site.type()); return foldArguments(invoker, getTarget); @@ -1260,17 +1283,20 @@ * <p> * <b>Example:</b> * <p><blockquote><pre> - * MethodHandle cat = MethodHandles.lookup(). - * findVirtual(String.class, "concat", String.class, String.class); - * System.out.println(cat.<String>invokeExact("x", "y")); // xy + * import static java.dyn.MethodHandles.*; + * import static java.dyn.MethodType.*; + * ... + * MethodHandle cat = lookup().findVirtual(String.class, + * "concat", methodType(String.class, String.class)); + * System.out.println((String) cat.invokeExact("x", "y")); // xy * MethodHandle d0 = dropArguments(cat, 0, String.class); - * System.out.println(d0.<String>invokeExact("x", "y", "z")); // xy + * System.out.println((String) d0.invokeExact("x", "y", "z")); // yz * MethodHandle d1 = dropArguments(cat, 1, String.class); - * System.out.println(d1.<String>invokeExact("x", "y", "z")); // xz + * System.out.println((String) d1.invokeExact("x", "y", "z")); // xz * MethodHandle d2 = dropArguments(cat, 2, String.class); - * System.out.println(d2.<String>invokeExact("x", "y", "z")); // yz - * MethodHandle d12 = dropArguments(cat, 1, String.class, String.class); - * System.out.println(d12.<String>invokeExact("w", "x", "y", "z")); // wz + * System.out.println((String) d2.invokeExact("x", "y", "z")); // xy + * MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); + * System.out.println((String) d12.invokeExact("x", 12, true, "z")); // xz * </pre></blockquote> * @param target the method handle to invoke after the argument is dropped * @param valueTypes the type(s) of the argument to drop @@ -1562,4 +1588,107 @@ MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) { return MethodHandleImpl.throwException(IMPL_TOKEN, MethodType.methodType(returnType, exType)); } + + /** + * Produce a wrapper instance of the given "SAM" type which redirects its calls to the given method handle. + * A SAM type is a type which declares a single abstract method. + * Additionally, it must have either no constructor (as an interface) + * or have a public or protected constructor of zero arguments (as a class). + * <p> + * The resulting instance of the required SAM type will respond to + * invocation of the SAM type's single abstract method by calling + * the given {@code target} on the incoming arguments, + * and returning or throwing whatever the {@code target} + * returns or throws. The invocation will be as if by + * {@code target.invokeExact}. + * <p> + * The method handle may throw an <em>undeclared exception</em>, + * which means any checked exception (or other checked throwable) + * not declared by the SAM type's single abstract method. + * If this happens, the throwable will be wrapped in an instance + * of {@link UndeclaredThrowableException} and thrown in that + * wrapped form. + * <p> + * The wrapper instance is guaranteed to be of a non-public + * implementation class C in a package containing no classes + * or methods except system-defined classes and methods. + * The implementation class C will have no public supertypes + * or public methods beyond the following: + * <ul> + * <li>the SAM type itself and any methods in the SAM type + * <li>the supertypes of the SAM type (if any) and their methods + * <li>{@link Object} and its methods + * <li>{@link MethodHandleProvider} and its methods + * </ul> + * <p> + * No stable mapping is promised between the SAM type and + * the implementation class C. Over time, several implementation + * classes might be used for the same SAM type. + * <p> + * This method is not guaranteed to return a distinct + * wrapper object for each separate call. If the JVM is able + * to prove that a wrapper has already been created for a given + * method handle, or for another method handle with the + * same behavior, the JVM may return that wrapper in place of + * a new wrapper. + * @param target the method handle to invoke from the wrapper + * @param samType the desired type of the wrapper, a SAM type + * @return a correctly-typed wrapper for the given {@code target} + * @throws IllegalArgumentException if the {@code target} throws + * an undeclared exception + */ + // ISSUE: Should we delegate equals/hashCode to the targets? + // Not useful unless there is a stable equals/hashCode behavior + // for MethodHandle, and for MethodHandleProvider.asMethodHandle. + public static + <T> T asInstance(MethodHandle target, Class<T> samType) { + // POC implementation only; violates the above contract several ways + final Method sam = getSamMethod(samType); + if (sam == null) + throw new IllegalArgumentException("not a SAM type: "+samType.getName()); + MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes()); + if (!samMT.equals(target.type())) + throw new IllegalArgumentException("wrong method type"); + final MethodHandle mh = target; + return samType.cast(Proxy.newProxyInstance( + samType.getClassLoader(), + new Class[]{ samType, MethodHandleProvider.class }, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getDeclaringClass() == MethodHandleProvider.class) { + return method.invoke(mh, args); + } + assert method.equals(sam) : method; + return mh.invokeVarargs(args); + } + })); + } + + private static + Method getSamMethod(Class<?> samType) { + Method sam = null; + for (Method m : samType.getMethods()) { + int mod = m.getModifiers(); + if (Modifier.isAbstract(mod)) { + if (sam != null) + return null; // too many abstract methods + sam = m; + } + } + if (!samType.isInterface() && getSamConstructor(samType) == null) + return null; // wrong kind of constructor + return sam; + } + + private static + Constructor getSamConstructor(Class<?> samType) { + for (Constructor c : samType.getDeclaredConstructors()) { + if (c.getParameterTypes().length == 0) { + int mod = c.getModifiers(); + if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) + return c; + } + } + return null; + } }
--- a/src/share/classes/java/dyn/MethodType.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/MethodType.java Fri Nov 12 10:49:52 2010 -0800 @@ -40,24 +40,37 @@ * returned by a method handle, or the arguments and return type passed * and expected by a method handle caller. Method types must be properly * matched between a method handle and all its callers, - * and the JVM's operations enforce this matching at all times. + * and the JVM's operations enforce this matching at, specifically + * during calls to {@link MethodHandle#invokeExact} + * and {@link MethodHandle#invokeGeneric}, and during execution + * of {@code invokedynamic} instructions. * <p> * The structure is a return type accompanied by any number of parameter types. - * The types (primitive, void, and reference) are represented by Class objects. + * The types (primitive, {@code void}, and reference) are represented by {@link Class} objects. + * (For ease of exposition, we treat {@code void} as if it were a type. + * In fact, it denotes the absence of a return type.) * <p> - * All instances of <code>MethodType</code> are immutable. + * All instances of {@code MethodType} are immutable. * Two instances are completely interchangeable if they compare equal. * Equality depends on pairwise correspondence of the return and parameter types and on nothing else. * <p> * This type can be created only by factory methods. * All factory methods may cache values, though caching is not guaranteed. * <p> - * Note: Like classes and strings, method types can be represented directly - * as constants to be loaded by {@code ldc} bytecodes. + * {@code MethodType} objects are sometimes derived from bytecode instructions + * such as {@code invokedynamic}, specifically from the type descriptor strings associated + * with the instructions in a class file's constant pool. + * When this occurs, any classes named in the descriptor strings must be loaded. + * (But they need not be initialized.) + * This loading may occur at any time before the {@code MethodType} object is first derived. + * <p> + * Like classes and strings, method types can be represented directly + * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. + * Loading such a constant causes its component classes to be loaded as necessary. * @author John Rose, JSR 292 EG */ public final -class MethodType { +class MethodType implements java.lang.reflect.Type { private final Class<?> rtype; private final Class<?>[] ptypes; private MethodTypeForm form; // erased form, plus cached data about primitives @@ -636,11 +649,11 @@ /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * Find or create an instance of the given method type. - * Any class or interface name embedded in the signature string + * Any class or interface name embedded in the descriptor string * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)} * on the given loader (or if it is null, on the system class loader). * <p> - * Note that it is possible to build method types which cannot be + * Note that it is possible to encounter method types which cannot be * constructed by this method, because their component types are * not all reachable from a common class loader. * <p> @@ -662,8 +675,11 @@ } /** - * Create a bytecode signature representation of the type. - * Note that this is not a strict inverse of + * Create a bytecode descriptor representation of the method type. + * <p> + * Note that this is not a strict inverse of {@link #fromMethodDescriptorString}. + * Two distinct classes which share a common name but have different class loaders + * will appear identical when viewed within descriptor strings. * <p> * This method is included for the benfit of applications that must * generate bytecodes that process method handles and invokedynamic.
--- a/src/share/classes/java/dyn/NoAccessException.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/NoAccessException.java Fri Nov 12 10:49:52 2010 -0800 @@ -37,7 +37,7 @@ * @author John Rose, JSR 292 EG * @since 1.7 */ -public class NoAccessException extends RuntimeException { +public class NoAccessException extends ReflectiveOperationException { private static final long serialVersionUID = 292L; /**
--- a/src/share/classes/java/dyn/package-info.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/dyn/package-info.java Fri Nov 12 10:49:52 2010 -0800 @@ -40,20 +40,18 @@ * The JVM links any such call (regardless of signature) to a dynamically * typed method handle invocation. In the case of {@code invokeGeneric}, * argument and return value conversions are applied. + * </li> * - * <li>In source code, the class {@link java.dyn.InvokeDynamic} appears to accept + * <li>In source code, the class {@link java.dyn.InvokeDynamic InvokeDynamic} appears to accept * any static method invocation, of any name and any signature. * But instead of emitting * an {@code invokestatic} instruction for such a call, the Java compiler emits * an {@code invokedynamic} instruction with the given name and signature. - * - * <li>When the JVM links an {@code invokedynamic} instruction, it calls the - * {@linkplain java.dyn.Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method} - * of the containing class to obtain a {@linkplain java.dyn.CallSite call site} object through which - * the call site will link its target {@linkplain java.dyn.MethodHandle method handle}. + * </li> * * <li>The JVM bytecode format supports immediate constants of - * the classes {@link java.dyn.MethodHandle} and {@link java.dyn.MethodType}. + * the classes {@link java.dyn.MethodHandle MethodHandle} and {@link java.dyn.MethodType MethodType}. + * </li> * </ul> * * <h2><a name="jvm_mods"></a>Corresponding JVM bytecode format changes</h2> @@ -65,18 +63,50 @@ * The first byte is the opcode 186 (hexadecimal {@code BA}). * The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions). * The final two bytes are reserved for future use and required to be zero. - * The constant pool reference is to a entry with tag {@code CONSTANT_NameAndType} - * (decimal 12). It is thus not a method reference of any sort, but merely - * the method name, argument types, and return type of the dynamic call site. - * <em>(TBD: The EG is discussing the possibility of a special constant pool entry type, - * so that other information may be added, such as a per-instruction bootstrap - * method and/or annotations.)</em> + * The constant pool reference of an {@code invokedynamic} instruction is to a entry + * with tag {@code CONSTANT_InvokeDynamic} (decimal 17). See below for its format. + * The entry specifies the bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant), + * the dynamic invocation name, and the argument types and return type of the call. + * <p> + * Each instance of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>. + * Multiple instances of an {@code invokedynamic} instruction can share a single + * {@code CONSTANT_InvokeDynamic} entry. + * In any case, distinct call sites always have distinct linkage state. + * <p> + * Moreover, for the purpose of distinguishing dynamic call sites, + * the JVM is allowed (but not required) to make internal copies + * of {@code invokedynamic} instructions, each one + * constituting a separate dynamic call site with its own linkage state. + * Such copying, if it occurs, cannot be observed except indirectly via + * execution of bootstrap methods and target methods. + * <p> + * A dynamic call site is originally in an unlinked state. In this state, there is + * no target method for the call site to invoke. + * A dynamic call site is linked by means of a bootstrap method, + * as <a href="#bsm">described below</a>. + * <p> + * <em>(Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType} + * instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the + * bootstrap method was specified dynamically, in a per-class basis, during class initialization.)</em> + * + * <h3>constant pool entries for {@code invokedynamic} instructions</h3> + * If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 17), + * it must contain exactly four more bytes. + * The first two bytes after the tag must be an index to a {@code CONSTANT_MethodHandle} + * entry, and the second two bytes must be an index to a {@code CONSTANT_NameAndType}. + * The first index specifies a bootstrap method used by the associated dynamic call sites. + * The second index specifies the method name, argument types, and return type of the dynamic call site. + * The structure of such an entry is therefore analogous to a {@code CONSTANT_Methodref}, + * except that the {@code CONSTANT_Class} reference in a {@code CONSTANT_Methodref} entry + * is replaced by a bootstrap method reference. * * <h3>constant pool entries for {@code MethodType}s</h3> * If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16), - * it must contain exactly two more bytes, which are an index to a {@code CONSTANT_Utf8} - * entry which represents a method type signature. The JVM will ensure that on first - * execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType} + * it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8} + * entry which represents a method type signature. + * <p> + * The JVM will ensure that on first + * execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType MethodType} * will be created which represents the signature. * Any classes mentioned in the {@code MethodType} will be loaded if necessary, * but not initialized. @@ -86,12 +116,15 @@ * <h3>constant pool entries for {@code MethodHandle}s</h3> * If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15), * it must contain exactly three more bytes. The first byte after the tag is a subtag - * value in the range 1 through 9, and the last two are an index to a + * value which must be in the range 1 through 9, and the last two must be an index to a * {@code CONSTANT_Fieldref}, {@code CONSTANT_Methodref}, or * {@code CONSTANT_InterfaceMethodref} entry which represents a field or method * for which a method handle is to be created. + * Furthermore, the subtag value and the type of the constant index value + * must agree according to the table below. + * <p> * The JVM will ensure that on first execution of an {@code ldc} instruction - * for this entry, a {@link java.dyn.MethodHandle} will be created which represents + * for this entry, a {@link java.dyn.MethodHandle MethodHandle} will be created which represents * the field or method reference, according to the specific mode implied by the subtag. * <p> * As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants, @@ -126,6 +159,129 @@ * Method handles for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic} * may force class initialization on their first invocation, just like the corresponding bytecodes. * + * <h2><a name="bsm"></a>Bootstrap Methods</h2> + * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction), + * the call site must first be <em>linked</em>. + * Linking is accomplished by calling a <em>bootstrap method</em> + * which is given the static information content of the call site, + * and which must produce a {@link java.dyn.MethodHandle method handle} + * that gives the behavior of the call site. + * <p> + * Each {@code invokedynamic} instruction statically specifies its own + * bootstrap method as a constant pool reference. + * The constant pool reference also specifies the call site's name and type signature, + * just like {@code invokevirtual} and the other invoke instructions. + * <p> + * Linking starts with resolving the constant pool entry for the + * bootstrap method, and resolving a {@link java.dyn.MethodType MethodType} object for + * the type signature of the dynamic call site. + * This resolution process may trigger class loading. + * It may therefore throw an error if a class fails to load. + * This error becomes the abnormal termination of the dynamic + * call site execution. + * Linkage does not trigger class initialization. + * <p> + * Next, the bootstrap method call is started, with four values being stacked: + * <ul> + * <li>a {@code MethodHandle}, the resolved bootstrap method itself </li> + * <li>a {@code Class}, the <em>caller class</em> in which dynamic call site occurs </li> + * <li>a {@code String}, the method name mentioned in the call site </li> + * <li>a {@code MethodType}, the resolved type signature of the call </li> + * </ul> + * The method handle is then applied to the other values as if by + * {@linkplain java.dyn.MethodHandle#invokeGeneric the <code>invokeGeneric</code> method}. + * The returned result must be a {@link java.dyn.CallSite CallSite}, a {@link java.dyn.MethodHandle MethodHandle}, + * or another {@link java.dyn.MethodHandleProvider MethodHandleProvider} value. + * The method {@linkplain java.dyn.MethodHandleProvider#asMethodHandle asMethodHandle} + * is then called on the returned value. The result of that second + * call is the {@code MethodHandle} which becomes the + * permanent binding for the dynamic call site. + * That method handle's type must be exactly equal to the type + * derived from the dynamic call site signature and passed to + * the bootstrap method. + * <p> + * After resolution, the linkage process may fail in a variety of ways. + * All failures are reported by an {@link java.dyn.InvokeDynamicBootstrapError InvokeDynamicBootstrapError}, + * which is thrown as the abnormal termination of the dynamic call + * site execution. + * The following circumstances will cause this: + * <ul> + * <li>the bootstrap method invocation completes abnormally </li> + * <li>the result from the bootstrap invocation is not a reference to + * an object of type {@link java.dyn.MethodHandleProvider MethodHandleProvider} </li> + * <li>the call to {@code asMethodHandle} completes abnormally </li> + * <li>the call to {@code asMethodHandle} fails to return a reference to + * an object of type {@link java.dyn.MethodHandle MethodHandle} </li> + * <li>the method handle produced by {@code asMethodHandle} does not have + * the expected {@code MethodType} </li> + * </ul> + * <h3>timing of linkage</h3> + * A dynamic call site is linked just before its first execution. + * The bootstrap method call implementing the linkage occurs within + * a thread that is attempting a first execution. + * <p> + * If there are several such threads, the JVM picks one thread + * and runs the bootstrap method while the others wait for the + * invocation to terminate normally or abnormally. + * <p> + * After a bootstrap method is called and a method handle target + * successfully extracted, the JVM attempts to link the instruction + * being executed to the target method handle. + * This may fail if there has been intervening linkage + * or invalidation event for the same instruction. + * If such a failure occurs, the dynamic call site must be + * re-executed from the beginning, either re-linking it + * (if it has been invalidated) or invoking the target + * (if it the instruction has been linked by some other means). + * <p> + * If the instruction is linked successfully, the target method + * handle is invoked to complete the instruction execution. + * The state of linkage continues until the method containing the + * dynamic call site is garbage collected, or the dynamic call site + * is invalidated by an explicit request, + * such as {@link java.dyn.Linkage#invalidateCallerClass Linkage.invalidateCallerClass}. + * <p> + * In an application which requires dynamic call sites with individually + * mutable behaviors, their bootstrap methods should produce distinct + * {@link java.dyn.CallSite CallSite} objects, one for each linkage request. + * <p> + * If a class containing {@code invokedynamic} instructions + * is {@linkplain java.dyn.Linkage#invalidateCallerClass(Class) invalidated}, + * subsequent execution of those {@code invokedynamic} instructions + * will require linking. + * It is as if they had never been executed in the first place. + * (However, invalidation does not cause constant pool entries to be + * resolved a second time.) + * <p> + * Invalidation events and bootstrap method calls for a particular + * dynamic call site are globally ordered relative to each other. + * When an invokedynamic instruction is invalidated, if there is + * simultaneously a bootstrap method invocation in process + * (in the same thread or a different thread), the result + * eventually returned must not be used to link the call site. + * Put another way, when a call site is invalidated, its + * subsequent linkage (if any) must be performed by a bootstrap method + * call initiated after the invalidation occurred. + * <p> + * If several threads simultaneously execute a bootstrap method for a single dynamic + * call site, the JVM must choose one target object and installs it visibly to + * all threads. Any other bootstrap method calls are allowed to complete, but their + * results are ignored, and their dynamic call site invocations proceed with the originally + * chosen target object. + * <p> + * The JVM is free to duplicate dynamic call sites. + * This means that, even if a class contains just one {@code invokedynamic} + * instruction, its bootstrap method may be executed several times, + * once for each duplicate. Thus, bootstrap method code should not + * assume an exclusive one-to-one correspondence between particular occurrences + * of {@code invokedynamic} bytecodes in class files and linkage events. + * <p> + * In principle, each individual execution of an {@code invokedynamic} + * instruction could be deemed (by a conforming implementation) to be a separate + * duplicate, requiring its own execution of the bootstrap method. + * However, implementations are expected to perform code duplication + * (if at all) in order to improve performance, not make it worse. + * * @author John Rose, JSR 292 EG */
--- a/src/share/classes/java/io/ObjectInputStream.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/io/ObjectInputStream.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -265,7 +265,7 @@ * object currently being deserialized and descriptor for current class. * Null when not during readObject upcall. */ - private CallbackContext curContext; + private SerialCallbackContext curContext; /** * Creates an ObjectInputStream that reads from the specified InputStream. @@ -1798,7 +1798,7 @@ private void readExternalData(Externalizable obj, ObjectStreamClass desc) throws IOException { - CallbackContext oldContext = curContext; + SerialCallbackContext oldContext = curContext; curContext = null; try { boolean blocked = desc.hasBlockExternalData(); @@ -1857,10 +1857,10 @@ slotDesc.hasReadObjectMethod() && handles.lookupException(passHandle) == null) { - CallbackContext oldContext = curContext; + SerialCallbackContext oldContext = curContext; try { - curContext = new CallbackContext(obj, slotDesc); + curContext = new SerialCallbackContext(obj, slotDesc); bin.setBlockDataMode(true); slotDesc.invokeReadObject(obj, this); @@ -3505,42 +3505,4 @@ } } - /** - * Context that during upcalls to class-defined readObject methods; holds - * object currently being deserialized and descriptor for current class. - * This context keeps a boolean state to indicate that defaultReadObject - * or readFields has already been invoked with this context or the class's - * readObject method has returned; if true, the getObj method throws - * NotActiveException. - */ - private static class CallbackContext { - private final Object obj; - private final ObjectStreamClass desc; - private final AtomicBoolean used = new AtomicBoolean(); - - public CallbackContext(Object obj, ObjectStreamClass desc) { - this.obj = obj; - this.desc = desc; - } - - public Object getObj() throws NotActiveException { - checkAndSetUsed(); - return obj; - } - - public ObjectStreamClass getDesc() { - return desc; - } - - private void checkAndSetUsed() throws NotActiveException { - if (!used.compareAndSet(false, true)) { - throw new NotActiveException( - "not in readObject invocation or fields already read"); - } - } - - public void setUsed() { - used.set(true); - } - } }
--- a/src/share/classes/java/io/ObjectOutputStream.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/io/ObjectOutputStream.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.io.ObjectStreamClass.processQueue; +import java.io.SerialCallbackContext; /** * An ObjectOutputStream writes primitive data types and graphs of Java objects @@ -191,10 +192,12 @@ private boolean enableReplace; // values below valid only during upcalls to writeObject()/writeExternal() - /** object currently being serialized */ - private Object curObj; - /** descriptor for current class (null if in writeExternal()) */ - private ObjectStreamClass curDesc; + /** + * Context during upcalls to class-defined writeObject methods; holds + * object currently being serialized and descriptor for current class. + * Null when not during writeObject upcall. + */ + private SerialCallbackContext curContext; /** current PutField object */ private PutFieldImpl curPut; @@ -426,9 +429,11 @@ * <code>OutputStream</code> */ public void defaultWriteObject() throws IOException { - if (curObj == null || curDesc == null) { + if ( curContext == null ) { throw new NotActiveException("not in call to writeObject"); } + Object curObj = curContext.getObj(); + ObjectStreamClass curDesc = curContext.getDesc(); bout.setBlockDataMode(false); defaultWriteFields(curObj, curDesc); bout.setBlockDataMode(true); @@ -446,9 +451,11 @@ */ public ObjectOutputStream.PutField putFields() throws IOException { if (curPut == null) { - if (curObj == null || curDesc == null) { + if (curContext == null) { throw new NotActiveException("not in call to writeObject"); } + Object curObj = curContext.getObj(); + ObjectStreamClass curDesc = curContext.getDesc(); curPut = new PutFieldImpl(curDesc); } return curPut; @@ -1420,17 +1427,15 @@ * writeExternal() method. */ private void writeExternalData(Externalizable obj) throws IOException { - Object oldObj = curObj; - ObjectStreamClass oldDesc = curDesc; PutFieldImpl oldPut = curPut; - curObj = obj; - curDesc = null; curPut = null; if (extendedDebugInfo) { debugInfoStack.push("writeExternal data"); } + SerialCallbackContext oldContext = curContext; try { + curContext = null; if (protocol == PROTOCOL_VERSION_1) { obj.writeExternal(this); } else { @@ -1440,13 +1445,12 @@ bout.writeByte(TC_ENDBLOCKDATA); } } finally { + curContext = oldContext; if (extendedDebugInfo) { debugInfoStack.pop(); } } - curObj = oldObj; - curDesc = oldDesc; curPut = oldPut; } @@ -1461,12 +1465,9 @@ for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; if (slotDesc.hasWriteObjectMethod()) { - Object oldObj = curObj; - ObjectStreamClass oldDesc = curDesc; PutFieldImpl oldPut = curPut; - curObj = obj; - curDesc = slotDesc; curPut = null; + SerialCallbackContext oldContext = curContext; if (extendedDebugInfo) { debugInfoStack.push( @@ -1474,18 +1475,19 @@ slotDesc.getName() + "\")"); } try { + curContext = new SerialCallbackContext(obj, slotDesc); bout.setBlockDataMode(true); slotDesc.invokeWriteObject(obj, this); bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); } finally { + curContext.setUsed(); + curContext = oldContext; if (extendedDebugInfo) { debugInfoStack.pop(); } } - curObj = oldObj; - curDesc = oldDesc; curPut = oldPut; } else { defaultWriteFields(obj, slotDesc);
--- a/src/share/classes/java/io/ObjectStreamClass.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/io/ObjectStreamClass.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -1830,8 +1830,10 @@ private final ObjectStreamField[] fields; /** number of primitive fields */ private final int numPrimFields; - /** unsafe field keys */ - private final long[] keys; + /** unsafe field keys for reading fields - may contain dupes */ + private final long[] readKeys; + /** unsafe fields keys for writing fields - no dupes */ + private final long[] writeKeys; /** field data offsets */ private final int[] offsets; /** field type codes */ @@ -1849,16 +1851,22 @@ FieldReflector(ObjectStreamField[] fields) { this.fields = fields; int nfields = fields.length; - keys = new long[nfields]; + readKeys = new long[nfields]; + writeKeys = new long[nfields]; offsets = new int[nfields]; typeCodes = new char[nfields]; ArrayList<Class<?>> typeList = new ArrayList<Class<?>>(); + Set<Long> usedKeys = new HashSet<Long>(); + for (int i = 0; i < nfields; i++) { ObjectStreamField f = fields[i]; Field rf = f.getField(); - keys[i] = (rf != null) ? + long key = (rf != null) ? unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET; + readKeys[i] = key; + writeKeys[i] = usedKeys.add(key) ? + key : Unsafe.INVALID_FIELD_OFFSET; offsets[i] = f.getOffset(); typeCodes[i] = f.getTypeCode(); if (!f.isPrimitive()) { @@ -1894,7 +1902,7 @@ * in array should be equal to Unsafe.INVALID_FIELD_OFFSET. */ for (int i = 0; i < numPrimFields; i++) { - long key = keys[i]; + long key = readKeys[i]; int off = offsets[i]; switch (typeCodes[i]) { case 'Z': @@ -1945,7 +1953,7 @@ throw new NullPointerException(); } for (int i = 0; i < numPrimFields; i++) { - long key = keys[i]; + long key = writeKeys[i]; if (key == Unsafe.INVALID_FIELD_OFFSET) { continue; // discard value } @@ -2006,7 +2014,7 @@ switch (typeCodes[i]) { case 'L': case '[': - vals[offsets[i]] = unsafe.getObject(obj, keys[i]); + vals[offsets[i]] = unsafe.getObject(obj, readKeys[i]); break; default: @@ -2027,7 +2035,7 @@ throw new NullPointerException(); } for (int i = numPrimFields; i < fields.length; i++) { - long key = keys[i]; + long key = writeKeys[i]; if (key == Unsafe.INVALID_FIELD_OFFSET) { continue; // discard value }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/io/SerialCallbackContext.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,58 @@ + /* + * %W% %E% + * + * Copyright (c) 2006, 2010 Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + + package java.io; + + /** + * Context during upcalls from object stream to class-defined + * readObject/writeObject methods. + * Holds object currently being deserialized and descriptor for current class. + * + * This context keeps track of the thread it was constructed on, and allows + * only a single call of defaultReadObject, readFields, defaultWriteObject + * or writeFields which must be invoked on the same thread before the class's + * readObject/writeObject method has returned. + * If not set to the current thread, the getObj method throws NotActiveException. + */ + final class SerialCallbackContext { + private final Object obj; + private final ObjectStreamClass desc; + /** + * Thread this context is in use by. + * As this only works in one thread, we do not need to worry about thread-safety. + */ + private Thread thread; + + public SerialCallbackContext(Object obj, ObjectStreamClass desc) { + this.obj = obj; + this.desc = desc; + this.thread = Thread.currentThread(); + } + + public Object getObj() throws NotActiveException { + checkAndSetUsed(); + return obj; + } + + public ObjectStreamClass getDesc() { + return desc; + } + + private void checkAndSetUsed() throws NotActiveException { + if (thread != Thread.currentThread()) { + throw new NotActiveException( + "not in readObject invocation or fields already read"); + } + thread = null; + } + + public void setUsed() { + thread = null; + } + } + +
--- a/src/share/classes/java/lang/AbstractStringBuilder.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/lang/AbstractStringBuilder.java Fri Nov 12 10:49:52 2010 -0800 @@ -100,7 +100,8 @@ * @param minimumCapacity the minimum desired capacity. */ public void ensureCapacity(int minimumCapacity) { - ensureCapacityInternal(minimumCapacity); + if (minimumCapacity > 0) + ensureCapacityInternal(minimumCapacity); } /** @@ -108,6 +109,7 @@ * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { + // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); }
--- a/src/share/classes/java/lang/ClassLoader.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/lang/ClassLoader.java Fri Nov 12 10:49:52 2010 -0800 @@ -553,6 +553,13 @@ * If either <tt>off</tt> or <tt>len</tt> is negative, or if * <tt>off+len</tt> is greater than <tt>b.length</tt>. * + * @throws SecurityException + * If an attempt is made to add this class to a package that + * contains classes that were signed by a different set of + * certificates than this class, or if an attempt is made + * to define a class in a package with a fully-qualified name + * that starts with "{@code java.}". + * * @see #loadClass(String, boolean) * @see #resolveClass(Class) *
--- a/src/share/classes/java/net/AbstractPlainSocketImpl.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/net/AbstractPlainSocketImpl.java Fri Nov 12 10:49:52 2010 -0800 @@ -28,9 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.InterruptedIOException; import java.io.FileDescriptor; -import java.io.ByteArrayOutputStream; import sun.net.ConnectionResetException; import sun.net.NetHooks; @@ -58,7 +56,7 @@ protected int fdUseCount = 0; /* lock when increment/decrementing fdUseCount */ - protected Object fdLock = new Object(); + protected final Object fdLock = new Object(); /* indicates a close is pending on the file descriptor */ protected boolean closePending = false; @@ -68,7 +66,7 @@ private int CONNECTION_RESET_PENDING = 1; private int CONNECTION_RESET = 2; private int resetState; - private Object resetLock = new Object(); + private final Object resetLock = new Object(); /** * Load net library into runtime. @@ -100,25 +98,24 @@ protected void connect(String host, int port) throws UnknownHostException, IOException { - IOException pending = null; + boolean connected = false; try { InetAddress address = InetAddress.getByName(host); this.port = port; this.address = address; - try { - connectToAddress(address, port, timeout); - return; - } catch (IOException e) { - pending = e; + connectToAddress(address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + /* Do nothing. If connect threw an exception then + it will be passed up the call stack */ + } } - } catch (UnknownHostException e) { - pending = e; } - - // everything failed - close(); - throw pending; } /** @@ -151,22 +148,29 @@ * SocketAddress subclass not supported by this socket * @since 1.4 */ - protected void connect(SocketAddress address, int timeout) throws IOException { - if (address == null || !(address instanceof InetSocketAddress)) - throw new IllegalArgumentException("unsupported address type"); - InetSocketAddress addr = (InetSocketAddress) address; - if (addr.isUnresolved()) - throw new UnknownHostException(addr.getHostName()); - this.port = addr.getPort(); - this.address = addr.getAddress(); + protected void connect(SocketAddress address, int timeout) + throws IOException { + boolean connected = false; + try { + if (address == null || !(address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.isUnresolved()) + throw new UnknownHostException(addr.getHostName()); + this.port = addr.getPort(); + this.address = addr.getAddress(); - try { connectToAddress(this.address, port, timeout); - return; - } catch (IOException e) { - // everything failed - close(); - throw e; + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + /* Do nothing. If connect threw an exception then + it will be passed up the call stack */ + } + } } } @@ -311,7 +315,7 @@ } } try { - FileDescriptor fd = acquireFD(); + acquireFD(); try { socketConnect(address, port, timeout); /* socket may have been closed during poll/select */ @@ -370,7 +374,7 @@ * @param s the connection */ protected void accept(SocketImpl s) throws IOException { - FileDescriptor fd = acquireFD(); + acquireFD(); try { socketAccept(s); } finally { @@ -562,7 +566,6 @@ close(); } - /* * "Acquires" and returns the FileDescriptor for this impl *
--- a/src/share/classes/java/nio/Direct-X-Buffer.java.template Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/nio/Direct-X-Buffer.java.template Fri Nov 12 10:49:52 2010 -0800 @@ -27,6 +27,7 @@ package java.nio; +import java.io.FileDescriptor; import sun.misc.Cleaner; import sun.misc.Unsafe; import sun.misc.VM; @@ -114,7 +115,7 @@ // Direct$Type$Buffer$RW$(int cap) { // package-private #if[rw] - super(-1, 0, cap, cap, false); + super(-1, 0, cap, cap); boolean pa = VM.isDirectMemoryPageAligned(); int ps = Bits.pageSize(); long size = Math.max(1L, (long)cap + (pa ? ps : 0)); @@ -145,7 +146,7 @@ // Invoked only by JNI: NewDirectByteBuffer(void*, long) // private Direct$Type$Buffer(long addr, int cap) { - super(-1, 0, cap, cap, false); + super(-1, 0, cap, cap); address = addr; cleaner = null; } @@ -154,14 +155,17 @@ // For memory-mapped buffers -- invoked by FileChannelImpl via reflection // - protected Direct$Type$Buffer$RW$(int cap, long addr, Runnable unmapper) { + protected Direct$Type$Buffer$RW$(int cap, long addr, + FileDescriptor fd, + Runnable unmapper) + { #if[rw] - super(-1, 0, cap, cap, true); + super(-1, 0, cap, cap, fd); address = addr; viewedBuffer = null; cleaner = Cleaner.create(this, unmapper); #else[rw] - super(cap, addr, unmapper); + super(cap, addr, fd, unmapper); #end[rw] }
--- a/src/share/classes/java/nio/MappedByteBuffer.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/nio/MappedByteBuffer.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,6 +25,7 @@ package java.nio; +import java.io.FileDescriptor; import sun.misc.Unsafe; @@ -71,26 +72,26 @@ // for optimization purposes, it's easier to do it the other way around. // This works because DirectByteBuffer is a package-private class. - // Volatile to make sure that the finalization thread sees the current - // value of this so that a region is not accidentally unmapped again later. - volatile boolean isAMappedBuffer; // package-private + // For mapped buffers, a FileDescriptor that may be used for mapping + // operations if valid; null if the buffer is not mapped. + private final FileDescriptor fd; // This should only be invoked by the DirectByteBuffer constructors // MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private - boolean mapped) + FileDescriptor fd) { super(mark, pos, lim, cap); - isAMappedBuffer = mapped; + this.fd = fd; } MappedByteBuffer(int mark, int pos, int lim, int cap) { // package-private super(mark, pos, lim, cap); - isAMappedBuffer = false; + this.fd = null; } private void checkMapped() { - if (!isAMappedBuffer) + if (fd == null) // Can only happen if a luser explicitly casts a direct byte buffer throw new UnsupportedOperationException(); } @@ -191,13 +192,12 @@ checkMapped(); if ((address != 0) && (capacity() != 0)) { long offset = mappingOffset(); - force0(mappingAddress(offset), mappingLength(offset)); + force0(fd, mappingAddress(offset), mappingLength(offset)); } return this; } private native boolean isLoaded0(long address, long length, int pageCount); private native void load0(long address, long length); - private native void force0(long address, long length); - + private native void force0(FileDescriptor fd, long address, long length); }
--- a/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java Wed Nov 03 14:12:45 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,572 +0,0 @@ -/* - * Copyright (c) 2007, 2009, 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.nio.channels; - -import java.nio.channels.spi.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.Future; -import java.io.IOException; -import java.net.SocketOption; -import java.net.SocketAddress; -import java.net.ProtocolFamily; -import java.nio.ByteBuffer; - -/** - * An asynchronous channel for datagram-oriented sockets. - * - * <p> An asynchronous datagram channel is created by invoking one of the {@link - * #open open} methods defined by this class. It is not possible to create a channel - * for an arbitrary, pre-existing datagram socket. A newly-created asynchronous - * datagram channel is open but not connected. It need not be connected in order - * for the {@link #send send} and {@link #receive receive} methods to be used. - * A datagram channel may be connected, by invoking its {@link #connect connect} - * method, in order to avoid the overhead of the security checks that are otherwise - * performed as part of every send and receive operation when a security manager - * is set. The channel must be connected in order to use the {@link #read read} - * and {@link #write write} methods, since those methods do not accept or return - * socket addresses. Once connected, an asynchronous datagram channel remains - * connected until it is disconnected or closed. - * - * <p> Socket options are configured using the {@link #setOption(SocketOption,Object) - * setOption} method. An asynchronous datagram channel to an Internet Protocol - * (IP) socket supports the following options: - * <blockquote> - * <table border> - * <tr> - * <th>Option Name</th> - * <th>Description</th> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} </td> - * <td> The size of the socket send buffer </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td> - * <td> The size of the socket receive buffer </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td> - * <td> Re-use address </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} </td> - * <td> Allow transmission of broadcast datagrams </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_TOS IP_TOS} </td> - * <td> The Type of Service (ToS) octet in the Internet Protocol (IP) header </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} </td> - * <td> The network interface for Internet Protocol (IP) multicast datagrams </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_TTL - * IP_MULTICAST_TTL} </td> - * <td> The <em>time-to-live</em> for Internet Protocol (IP) multicast - * datagrams </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_LOOP - * IP_MULTICAST_LOOP} </td> - * <td> Loopback for Internet Protocol (IP) multicast datagrams </td> - * </tr> - * </table> - * </blockquote> - * Additional (implementation specific) options may also be supported. - * - * <p> Asynchronous datagram channels allow more than one read/receive and - * write/send to be oustanding at any given time. - * - * <p> <b>Usage Example:</b> - * <pre> - * final AsynchronousDatagramChannel dc = AsynchronousDatagramChannel.open() - * .bind(new InetSocketAddress(4000)); - * - * // print the source address of all packets that we receive - * dc.receive(buffer, buffer, new CompletionHandler<SocketAddress,ByteBuffer>() { - * public void completed(SocketAddress sa, ByteBuffer buffer) { - * System.out.println(sa); - * buffer.clear(); - * dc.receive(buffer, buffer, this); - * } - * public void failed(Throwable exc, ByteBuffer buffer) { - * ... - * } - * }); - * </pre> - * - * @since 1.7 - */ - -public abstract class AsynchronousDatagramChannel - implements AsynchronousByteChannel, MulticastChannel -{ - private final AsynchronousChannelProvider provider; - - /** - * Initializes a new instance of this class. - */ - protected AsynchronousDatagramChannel(AsynchronousChannelProvider provider) { - this.provider = provider; - } - - /** - * Returns the provider that created this channel. - */ - public final AsynchronousChannelProvider provider() { - return provider; - } - - /** - * Opens an asynchronous datagram channel. - * - * <p> The new channel is created by invoking the {@link - * java.nio.channels.spi.AsynchronousChannelProvider#openAsynchronousDatagramChannel - * openAsynchronousDatagramChannel} method on the {@link - * java.nio.channels.spi.AsynchronousChannelProvider} object that created - * the given group (or the default provider where {@code group} is {@code - * null}). - * - * <p> The {@code family} parameter is used to specify the {@link ProtocolFamily}. - * If the datagram channel is to be used for Internet Protocol {@link - * MulticastChannel multicasting} then this parameter should correspond to - * the address type of the multicast groups that this channel will join. - * - * @param family - * The protocol family, or {@code null} to use the default protocol - * family - * @param group - * The group to which the newly constructed channel should be bound, - * or {@code null} for the default group - * - * @return A new asynchronous datagram channel - * - * @throws UnsupportedOperationException - * If the specified protocol family is not supported. For example, - * suppose the parameter is specified as {@link - * java.net.StandardProtocolFamily#INET6 INET6} but IPv6 is not - * enabled on the platform. - * @throws ShutdownChannelGroupException - * The specified group is shutdown - * @throws IOException - * If an I/O error occurs - */ - public static AsynchronousDatagramChannel open(ProtocolFamily family, - AsynchronousChannelGroup group) - throws IOException - { - AsynchronousChannelProvider provider = (group == null) ? - AsynchronousChannelProvider.provider() : group.provider(); - return provider.openAsynchronousDatagramChannel(family, group); - } - - /** - * Opens an asynchronous datagram channel. - * - * <p> This method returns an asynchronous datagram channel that is - * bound to the <em>default group</em>. This method is equivalent to evaluating - * the expression: - * <blockquote><pre> - * open((ProtocolFamily)null, (AsynchronousChannelGroup)null); - * </pre></blockquote> - * - * @return A new asynchronous datagram channel - * - * @throws IOException - * If an I/O error occurs - */ - public static AsynchronousDatagramChannel open() - throws IOException - { - return open(null, null); - } - - // -- Socket-specific operations -- - - /** - * @throws AlreadyBoundException {@inheritDoc} - * @throws UnsupportedAddressTypeException {@inheritDoc} - * @throws ClosedChannelException {@inheritDoc} - * @throws IOException {@inheritDoc} - * @throws SecurityException - * If a security manager has been installed and its {@link - * SecurityManager#checkListen checkListen} method denies the - * operation - */ - @Override - public abstract AsynchronousDatagramChannel bind(SocketAddress local) - throws IOException; - - /** - * @throws IllegalArgumentException {@inheritDoc} - * @throws ClosedChannelException {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public abstract <T> AsynchronousDatagramChannel setOption(SocketOption<T> name, T value) - throws IOException; - - /** - * Returns the remote address to which this channel is connected. - * - * <p> Where the channel is connected to an Internet Protocol socket address - * then the return value from this method is of type {@link - * java.net.InetSocketAddress}. - * - * @return The remote address; {@code null} if the channel's socket is not - * connected - * - * @throws ClosedChannelException - * If the channel is closed - * @throws IOException - * If an I/O error occurs - */ - public abstract SocketAddress getRemoteAddress() throws IOException; - - /** - * Connects this channel's socket. - * - * <p> The channel's socket is configured so that it only receives - * datagrams from, and sends datagrams to, the given remote <i>peer</i> - * address. Once connected, datagrams may not be received from or sent to - * any other address. A datagram socket remains connected until it is - * explicitly disconnected or until it is closed. - * - * <p> This method performs exactly the same security checks as the {@link - * java.net.DatagramSocket#connect connect} method of the {@link - * java.net.DatagramSocket} class. That is, if a security manager has been - * installed then this method verifies that its {@link - * java.lang.SecurityManager#checkAccept checkAccept} and {@link - * java.lang.SecurityManager#checkConnect checkConnect} methods permit - * datagrams to be received from and sent to, respectively, the given - * remote address. - * - * <p> This method may be invoked at any time. Whether it has any effect - * on outstanding read or write operations is implementation specific and - * therefore not specified. - * - * @param remote - * The remote address to which this channel is to be connected - * - * @return This datagram channel - * - * @throws ClosedChannelException - * If this channel is closed - * - * @throws SecurityException - * If a security manager has been installed - * and it does not permit access to the given remote address - * - * @throws IOException - * If some other I/O error occurs - */ - public abstract AsynchronousDatagramChannel connect(SocketAddress remote) - throws IOException; - - /** - * Disconnects this channel's socket. - * - * <p> The channel's socket is configured so that it can receive datagrams - * from, and sends datagrams to, any remote address so long as the security - * manager, if installed, permits it. - * - * <p> This method may be invoked at any time. Whether it has any effect - * on outstanding read or write operations is implementation specific and - * therefore not specified. - * - * @return This datagram channel - * - * @throws IOException - * If some other I/O error occurs - */ - public abstract AsynchronousDatagramChannel disconnect() throws IOException; - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The {@code handler} parameter is a completion handler that is - * invoked when the receive operation completes (or fails). The result - * passed to the completion handler is the datagram's source address. - * - * <p> The datagram is transferred into the given byte buffer starting at - * its current position, as if by a regular {@link AsynchronousByteChannel#read - * read} operation. If there are fewer bytes remaining in the buffer - * than are required to hold the datagram then the remainder of the datagram - * is silently discarded. - * - * <p> If a timeout is specified and the timeout elapses before the operation - * completes then the operation completes with the exception {@link - * InterruptedByTimeoutException}. When a timeout elapses then the state of - * the {@link ByteBuffer} is not defined. The buffers should be discarded or - * at least care must be taken to ensure that the buffer is not accessed - * while the channel remains open. - * - * <p> When a security manager has been installed and the channel is not - * connected, then it verifies that the source's address and port number are - * permitted by the security manager's {@link SecurityManager#checkAccept - * checkAccept} method. The permission check is performed with privileges that - * are restricted by the calling context of this method. If the permission - * check fails then the operation completes with a {@link SecurityException}. - * The overhead of this security check can be avoided by first connecting the - * socket via the {@link #connect connect} method. - * - * @param dst - * The buffer into which the datagram is to be transferred - * @param timeout - * The timeout, or {@code 0L} for no timeout - * @param unit - * The time unit of the {@code timeout} argument - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws IllegalArgumentException - * If the timeout is negative or the buffer is read-only - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public abstract <A> void receive(ByteBuffer dst, - long timeout, - TimeUnit unit, - A attachment, - CompletionHandler<SocketAddress,? super A> handler); - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The {@code handler} parameter is a completion handler that is - * invoked when the receive operation completes (or fails). The result - * passed to the completion handler is the datagram's source address. - * - * <p> This method is equivalent to invoking {@link - * #receive(ByteBuffer,long,TimeUnit,Object,CompletionHandler)} with a - * timeout of {@code 0L}. - * - * @param dst - * The buffer into which the datagram is to be transferred - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws IllegalArgumentException - * If the buffer is read-only - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public final <A> void receive(ByteBuffer dst, - A attachment, - CompletionHandler<SocketAddress,? super A> handler) - { - receive(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); - } - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The method behaves in exactly the same manner as the {@link - * #receive(ByteBuffer,Object,CompletionHandler) - * receive(ByteBuffer,Object,CompletionHandler)} method except that instead - * of specifying a completion handler, this method returns a {@code Future} - * representing the pending result. The {@code Future}'s {@link Future#get() - * get} method returns the datagram's source address. - * - * @param dst - * The buffer into which the datagram is to be transferred - * - * @return a {@code Future} object representing the pending result - * - * @throws IllegalArgumentException - * If the buffer is read-only - */ - public abstract Future<SocketAddress> receive(ByteBuffer dst); - - /** - * Sends a datagram via this channel. - * - * <p> This method initiates sending of a datagram from the given buffer to - * the given address. The {@code handler} parameter is a completion handler - * that is invoked when the send completes (or fails). The result passed to - * the completion handler is the number of bytes sent. - * - * <p> Otherwise this method works in the same manner as the {@link - * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)} - * method. - * - * @param src - * The buffer containing the datagram to be sent - * @param target - * The address to which the datagram is to be sent - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws UnresolvedAddressException - * If the given remote address is not fully resolved - * @throws UnsupportedAddressTypeException - * If the type of the given remote address is not supported - * @throws IllegalArgumentException - * If the channel's socket is connected and is connected to an - * address that is not equal to {@code target} - * @throws SecurityException - * If a security manager has been installed and it does not permit - * datagrams to be sent to the given address - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public abstract <A> void send(ByteBuffer src, - SocketAddress target, - A attachment, - CompletionHandler<Integer,? super A> handler); - - /** - * Sends a datagram via this channel. - * - * <p> This method initiates sending of a datagram from the given buffer to - * the given address. The method behaves in exactly the same manner as the - * {@link #send(ByteBuffer,SocketAddress,Object,CompletionHandler) - * send(ByteBuffer,SocketAddress,Object,CompletionHandler)} method except - * that instead of specifying a completion handler, this method returns a - * {@code Future} representing the pending result. The {@code Future}'s - * {@link Future#get() get} method returns the number of bytes sent. - * - * @param src - * The buffer containing the datagram to be sent - * @param target - * The address to which the datagram is to be sent - * - * @return a {@code Future} object representing the pending result - * - * @throws UnresolvedAddressException - * If the given remote address is not fully resolved - * @throws UnsupportedAddressTypeException - * If the type of the given remote address is not supported - * @throws IllegalArgumentException - * If the channel's socket is connected and is connected to an - * address that is not equal to {@code target} - * @throws SecurityException - * If a security manager has been installed and it does not permit - * datagrams to be sent to the given address - */ - public abstract Future<Integer> send(ByteBuffer src, SocketAddress target); - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The {@code handler} parameter is a completion handler that is - * invoked when the receive operation completes (or fails). The result - * passed to the completion handler is number of bytes read. - * - * <p> This method may only be invoked if this channel is connected, and it - * only accepts datagrams from the peer that the channel is connected too. - * The datagram is transferred into the given byte buffer starting at - * its current position and exactly as specified in the {@link - * AsynchronousByteChannel} interface. If there are fewer bytes - * remaining in the buffer than are required to hold the datagram then the - * remainder of the datagram is silently discarded. - * - * <p> If a timeout is specified and the timeout elapses before the operation - * completes then the operation completes with the exception {@link - * InterruptedByTimeoutException}. When a timeout elapses then the state of - * the {@link ByteBuffer} is not defined. The buffers should be discarded or - * at least care must be taken to ensure that the buffer is not accessed - * while the channel remains open. - * - * @param dst - * The buffer into which the datagram is to be transferred - * @param timeout - * The timeout, or {@code 0L} for no timeout - * @param unit - * The time unit of the {@code timeout} argument - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws IllegalArgumentException - * If the timeout is negative or buffer is read-only - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public abstract <A> void read(ByteBuffer dst, - long timeout, - TimeUnit unit, - A attachment, - CompletionHandler<Integer,? super A> handler); - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public final <A> void read(ByteBuffer dst, - A attachment, - CompletionHandler<Integer,? super A> handler) - { - read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); - } - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public abstract Future<Integer> read(ByteBuffer dst); - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public abstract <A> void write(ByteBuffer src, - A attachment, - CompletionHandler<Integer,? super A> handler); - - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public abstract Future<Integer> write(ByteBuffer src); -}
--- a/src/share/classes/java/nio/channels/package-info.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/nio/channels/package-info.java Fri Nov 12 10:49:52 2010 -0800 @@ -232,8 +232,6 @@ * <td>An asynchronous channel to a stream-oriented connecting socket</td></tr> * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousServerSocketChannel} </tt></td> * <td>An asynchronous channel to a stream-oriented listening socket</td></tr> - * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousDatagramChannel}</tt></td> - * <td>An asynchronous channel to a datagram-oriented socket</td></tr> * <tr><td valign=top><tt>{@link java.nio.channels.CompletionHandler}</tt></td> * <td>A handler for consuming the result of an asynchronous operation</td></tr> * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousChannelGroup}</tt></td>
--- a/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java Fri Nov 12 10:49:52 2010 -0800 @@ -26,7 +26,6 @@ package java.nio.channels.spi; import java.nio.channels.*; -import java.net.ProtocolFamily; import java.io.IOException; import java.util.Iterator; import java.util.ServiceLoader; @@ -239,26 +238,4 @@ */ public abstract AsynchronousSocketChannel openAsynchronousSocketChannel (AsynchronousChannelGroup group) throws IOException; - - /** - * Opens an asynchronous datagram channel. - * - * @param family - * The protocol family, or {@code null} for the default protocol - * family - * @param group - * The group to which the channel is bound, or {@code null} to - * bind to the default group - * - * @return The new channel - * - * @throws IllegalChannelGroupException - * If the provider that created the group differs from this provider - * @throws ShutdownChannelGroupException - * The group is shutdown - * @throws IOException - * If an I/O error occurs - */ - public abstract AsynchronousDatagramChannel openAsynchronousDatagramChannel - (ProtocolFamily family, AsynchronousChannelGroup group) throws IOException; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/security/AlgorithmConstraints.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, 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.security; + +import java.util.Set; + +/** + * This interface specifies constraints for cryptographic algorithms, + * keys (key sizes), and other algorithm parameters. + * <p> + * {@code AlgorithmConstraints} objects are immutable. An implementation + * of this interface should not provide methods that can change the state + * of an instance once it has been created. + * <p> + * Note that {@code AlgorithmConstraints} can be used to represent the + * restrictions described by the security properties + * {@code jdk.certpath.disabledAlgorithms} and + * {@code jdk.tls.disabledAlgorithms}, or could be used by a + * concrete {@code PKIXCertPathChecker} to check whether a specified + * certificate in the certification path contains the required algorithm + * constraints. + * + * @see javax.net.ssl.SSLParameters#getAlgorithmConstraints + * @see javax.net.ssl.SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + +public interface AlgorithmConstraints { + + /** + * Determines whether an algorithm is granted permission for the + * specified cryptographic primitives. + * + * @param primitives a set of cryptographic primitives + * @param algorithm the algorithm name + * @param parameters the algorithm parameters, or null if no additional + * parameters + * + * @return true if the algorithm is permitted and can be used for all + * of the specified cryptographic primitives + * + * @throws IllegalArgumentException if primitives or algorithm is null + * or empty + */ + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, AlgorithmParameters parameters); + + /** + * Determines whether a key is granted permission for the specified + * cryptographic primitives. + * <p> + * This method is usually used to check key size and key usage. + * + * @param primitives a set of cryptographic primitives + * @param key the key + * + * @return true if the key can be used for all of the specified + * cryptographic primitives + * + * @throws IllegalArgumentException if primitives is null or empty, + * or the key is null + */ + public boolean permits(Set<CryptoPrimitive> primitives, Key key); + + /** + * Determines whether an algorithm and the corresponding key are granted + * permission for the specified cryptographic primitives. + * + * @param primitives a set of cryptographic primitives + * @param algorithm the algorithm name + * @param key the key + * @param parameters the algorithm parameters, or null if no additional + * parameters + * + * @return true if the key and the algorithm can be used for all of the + * specified cryptographic primitives + * + * @throws IllegalArgumentException if primitives or algorithm is null + * or empty, or the key is null + */ + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/security/CryptoPrimitive.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010, 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.security; + +/** + * An enumeration of cryptographic primitives. + * + * @since 1.7 + */ +public enum CryptoPrimitive { + /** + * Hash function + */ + MESSAGE_DIGEST, + + /** + * Cryptographic random number generator + */ + SECURE_RANDOM, + + /** + * Symmetric primitive: block cipher + */ + BLOCK_CIPHER, + + /** + * Symmetric primitive: stream cipher + */ + STREAM_CIPHER, + + /** + * Symmetric primitive: message authentication code + */ + MAC, + + /** + * Symmetric primitive: key wrap + */ + KEY_WRAP, + + /** + * Asymmetric primitive: public key encryption + */ + PUBLIC_KEY_ENCRYPTION, + + /** + * Asymmetric primitive: signature scheme + */ + SIGNATURE, + + /** + * Asymmetric primitive: key encapsulation mechanism + */ + KEY_ENCAPSULATION, + + /** + * Asymmetric primitive: key agreement and key distribution + */ + KEY_AGREEMENT +}
--- a/src/share/classes/java/util/ArrayList.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/util/ArrayList.java Fri Nov 12 10:49:52 2010 -0800 @@ -176,6 +176,11 @@ * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { + if (minCapacity > 0) + ensureCapacityInternal(minCapacity); + } + + private void ensureCapacityInternal(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) @@ -403,7 +408,7 @@ * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { - ensureCapacity(size + 1); // Increments modCount!! + ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } @@ -420,7 +425,7 @@ public void add(int index, E element) { rangeCheckForAdd(index); - ensureCapacity(size + 1); // Increments modCount!! + ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; @@ -524,7 +529,7 @@ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount + ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; @@ -550,7 +555,7 @@ Object[] a = c.toArray(); int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount + ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0)
--- a/src/share/classes/java/util/Vector.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/util/Vector.java Fri Nov 12 10:49:52 2010 -0800 @@ -222,8 +222,10 @@ * @param minCapacity the desired minimum capacity */ public synchronized void ensureCapacity(int minCapacity) { - modCount++; - ensureCapacityHelper(minCapacity); + if (minCapacity > 0) { + modCount++; + ensureCapacityHelper(minCapacity); + } } /**
--- a/src/share/classes/java/util/logging/LogRecord.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/java/util/logging/LogRecord.java Fri Nov 12 10:49:52 2010 -0800 @@ -529,8 +529,6 @@ Throwable throwable = new Throwable(); int depth = access.getStackTraceDepth(throwable); - String logClassName = "java.util.logging.Logger"; - String plogClassName = "sun.util.logging.PlatformLogger"; boolean lookingForLogger = true; for (int ix = 0; ix < depth; ix++) { // Calling getStackTraceElement directly prevents the VM @@ -538,13 +536,14 @@ StackTraceElement frame = access.getStackTraceElement(throwable, ix); String cname = frame.getClassName(); + boolean isLoggerImpl = isLoggerImplFrame(cname); if (lookingForLogger) { // Skip all frames until we have found the first logger frame. - if (cname.equals(logClassName) || cname.startsWith(plogClassName)) { + if (isLoggerImpl) { lookingForLogger = false; } } else { - if (!cname.equals(logClassName) && !cname.startsWith(plogClassName)) { + if (!isLoggerImpl) { // skip reflection call if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { // We've found the relevant frame. @@ -558,4 +557,11 @@ // We haven't found a suitable frame, so just punt. This is // OK as we are only committed to making a "best effort" here. } + + private boolean isLoggerImplFrame(String cname) { + // the log record could be created for a platform logger + return (cname.equals("java.util.logging.Logger") || + cname.startsWith("java.util.logging.LoggingProxyImpl") || + cname.startsWith("sun.util.logging.")); + } }
--- a/src/share/classes/javax/management/ObjectName.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/management/ObjectName.java Fri Nov 12 10:49:52 2010 -0800 @@ -211,8 +211,8 @@ * reverse DNS name of the organization that specifies the MBeans, * followed by a period and a string whose interpretation is * determined by that organization. For example, MBeans specified by - * Sun Microsystems Inc., DNS name <code>sun.com</code>, would have - * domains such as <code>com.sun.MyDomain</code>. This is essentially + * <code>example.com</code> would have + * domains such as <code>com.example.MyDomain</code>. This is essentially * the same convention as for Java-language package names.</p> * * <p>The <b>serialVersionUID</b> of this class is <code>1081892073854801359L</code>.
--- a/src/share/classes/javax/management/build.xml Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/management/build.xml Fri Nov 12 10:49:52 2010 -0800 @@ -35,18 +35,18 @@ . Please also read the important comment on basedir definition below. --> -<project name="JMX API Version 1.4 - Java SE 6 implementation" - default="jar" - basedir="../../../../.." +<project name="JMX API Version 1.4 - Java SE 6 implementation" + default="jar" + basedir="../../../../.." > - <!-- IMPORTANT: BASEDIR DEFINITION - This file is assumed to be in: - <src_bundle_dir>/j2se/src/share/classes/javax/management - Thus the basedir for this project is defined above as: - basedir="../../../../.." + <!-- IMPORTANT: BASEDIR DEFINITION + This file is assumed to be in: + <src_bundle_dir>/j2se/src/share/classes/javax/management + Thus the basedir for this project is defined above as: + basedir="../../../../.." in order to be the parent dir of src subdir. - Result of the build will be placed in ${basedir}/build_jmx + Result of the build will be placed in ${basedir}/build_jmx as defined by ${dir.build} property below. --> @@ -163,14 +163,14 @@ <echo message=" BUILD_DATE = ${BUILD_DATE}" /> </target> - + <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Call classes subtargets and rmic stubs generation --> - <target name="classes" depends="init,classes-javac,classes-rmic" - description="Call classes subtargets and rmic stubs generation" + <target name="classes" depends="init,classes-javac,classes-rmic" + description="Call classes subtargets and rmic stubs generation" /> @@ -183,13 +183,13 @@ <mkdir dir="${dir.build.classes}" /> <javac srcdir="${dir.src}" - destdir="${dir.build.classes}" + destdir="${dir.build.classes}" source="${flag.javac.source}" debug="${flag.debug}" debuglevel="${flag.debug.level}" optimize="${flag.optimize}" includeAntRuntime="no" - includeJavaRuntime="no" + includeJavaRuntime="no" > <include name="javax/management/**"/> <include name="com/sun/jmx/**"/> @@ -253,7 +253,7 @@ <mkdir dir="${dir.build.lib}" /> - <jar jarfile="${dir.build.lib}/jmx.jar" + <jar jarfile="${dir.build.lib}/jmx.jar" update="true" > @@ -269,10 +269,10 @@ <attribute name="Sealed" value="true" /> <attribute name="Specification-Title" value="JMX(TM) API" /> <attribute name="Specification-Version" value="1.4" /> - <attribute name="Specification-Vendor" value="Sun Microsystems, Inc." /> + <attribute name="Specification-Vendor" value="Oracle Corporation" /> <attribute name="Implementation-Title" value="JMX(TM) API, Java SE 6 implementation" /> - <attribute name="Implementation-Version" value="${BUILD_DATE} rebuild of Mustang JMX sources" /> - <attribute name="Implementation-Vendor" value="Source bundle from Sun Microsystems, Inc. - Customer rebuilt" /> + <attribute name="Implementation-Version" value="${BUILD_DATE} rebuild of Java SE JMX sources" /> + <attribute name="Implementation-Vendor" value="Source bundle from Oracle Corporation - Customer rebuilt" /> </section> </manifest> </jar> @@ -295,16 +295,16 @@ <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Delete build directory and all its contents --> - <target name="clean-all" + <target name="clean-all" description="Delete build directory and all its contents" > <delete dir="${dir.build}" /> - </target> - - + </target> + + <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Clean all and build everything --> - <target name="all" depends="clean-all,jar" + <target name="all" depends="clean-all,jar" description="Clean all and build everything" />
--- a/src/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java Fri Nov 12 10:49:52 2010 -0800 @@ -92,8 +92,6 @@ * @since 1.5 */ -// Sun Microsystems, Sept. 2002: Revisited for JMX 1.2 (DF) -// @SuppressWarnings("serial") // serialVersionUID is not constant public class ModelMBeanNotificationInfo extends MBeanNotificationInfo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/javax/net/ssl/ExtendedSSLSession.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010, 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 javax.net.ssl; + +/** + * Extends the <code>SSLSession</code> interface to support additional + * session attributes. + * + * @since 1.7 + */ +public abstract class ExtendedSSLSession implements SSLSession { + /** + * Obtains an array of supported signature algorithms that the local side + * is willing to use. + * <p> + * Note: this method is used to indicate to the peer which signature + * algorithms may be used for digital signatures in TLS 1.2. It is + * not meaningful for TLS versions prior to 1.2. + * <p> + * The signature algorithm name must be a standard Java Security + * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). + * See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * <p> + * Note: the local supported signature algorithms should conform to + * the algorithm constraints specified by + * {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()} + * method in <code>SSLParameters</code>. + * + * @return An array of supported signature algorithms, in descending + * order of preference. The return value is an empty array if + * no signature algorithm is supported. + * + * @see SSLParameters#getAlgorithmConstraints + */ + public abstract String[] getLocalSupportedSignatureAlgorithms(); + + /** + * Obtains an array of supported signature algorithms that the peer is + * able to use. + * <p> + * Note: this method is used to indicate to the local side which signature + * algorithms may be used for digital signatures in TLS 1.2. It is + * not meaningful for TLS versions prior to 1.2. + * <p> + * The signature algorithm name must be a standard Java Security + * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). + * See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * + * @return An array of supported signature algorithms, in descending + * order of preference. The return value is an empty array if + * the peer has not sent the supported signature algorithms. + * + * @see X509KeyManager + * @see X509ExtendedKeyManager + */ + public abstract String[] getPeerSupportedSignatureAlgorithms(); +}
--- a/src/share/classes/javax/net/ssl/HttpsURLConnection.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/net/ssl/HttpsURLConnection.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, 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 @@ -188,19 +188,8 @@ * <p> * The default implementation will deny such connections. */ - private static HostnameVerifier defaultHostnameVerifier; - - /** - * Initialize the default <code>HostnameVerifier</code>. - */ - static { - try { - defaultHostnameVerifier = - new sun.net.www.protocol.https.DefaultHostnameVerifier(); - } catch (NoClassDefFoundError e) { - defaultHostnameVerifier = new DefaultHostnameVerifier(); - } - } + private static HostnameVerifier defaultHostnameVerifier = + new DefaultHostnameVerifier(); /* * The initial default <code>HostnameVerifier</code>. Should be
--- a/src/share/classes/javax/net/ssl/SSLEngine.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/net/ssl/SSLEngine.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -968,6 +968,47 @@ /** + * Returns the {@code SSLSession} being constructed during a SSL/TLS + * handshake. + * <p> + * TLS protocols may negotiate parameters that are needed when using + * an instance of this class, but before the {@code SSLSession} has + * been completely initialized and made available via {@code getSession}. + * For example, the list of valid signature algorithms may restrict + * the type of certificates that can used during TrustManager + * decisions, or the maximum TLS fragment packet sizes can be + * resized to better support the network environment. + * <p> + * This method provides early access to the {@code SSLSession} being + * constructed. Depending on how far the handshake has progressed, + * some data may not yet be available for use. For example, if a + * remote server will be sending a Certificate chain, but that chain + * has yet not been processed, the {@code getPeerCertificates} + * method of {@code SSLSession} will throw a + * SSLPeerUnverifiedException. Once that chain has been processed, + * {@code getPeerCertificates} will return the proper value. + * + * @see SSLSocket + * @see SSLSession + * @see ExtendedSSLSession + * @see X509ExtendedKeyManager + * @see X509ExtendedTrustManager + * + * @return null if this instance is not currently handshaking, or + * if the current handshake has not progressed far enough to + * create a basic SSLSession. Otherwise, this method returns the + * {@code SSLSession} currently being negotiated. + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. + * + * @since 1.7 + */ + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + + /** * Initiates handshaking (initial or renegotiation) on this SSLEngine. * <P> * This method is not needed for the initial handshake, as the
--- a/src/share/classes/javax/net/ssl/SSLParameters.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/net/ssl/SSLParameters.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -25,24 +25,29 @@ package javax.net.ssl; +import java.security.AlgorithmConstraints; + /** * Encapsulates parameters for an SSL/TLS connection. The parameters * are the list of ciphersuites to be accepted in an SSL/TLS handshake, - * the list of protocols to be allowed, and whether SSL/TLS servers should - * request or require client authentication. - * - * <p>SSLParameters can be created via the constructors in this class. + * the list of protocols to be allowed, the endpoint identification + * algorithm during SSL/TLS handshaking, the algorithm constraints and + * whether SSL/TLS servers should request or require client authentication. + * <p> + * SSLParameters can be created via the constructors in this class. * Objects can also be obtained using the <code>getSSLParameters()</code> * methods in * {@link SSLSocket#getSSLParameters SSLSocket} and + * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and * {@link SSLEngine#getSSLParameters SSLEngine} or the * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} * methods in <code>SSLContext</code>. - * - * <P>SSLParameters can be applied to a connection via the methods + * <p> + * SSLParameters can be applied to a connection via the methods * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and - * {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}. + * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()} + * and {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}. * * @see SSLSocket * @see SSLEngine @@ -56,11 +61,13 @@ private String[] protocols; private boolean wantClientAuth; private boolean needClientAuth; + private String identificationAlgorithm; + private AlgorithmConstraints algorithmConstraints; /** * Constructs SSLParameters. - * - * <p>The cipherSuites and protocols values are set to <code>null</code>, + * <p> + * The cipherSuites and protocols values are set to <code>null</code>, * wantClientAuth and needClientAuth are set to <code>false</code>. */ public SSLParameters() { @@ -69,6 +76,7 @@ /** * Constructs SSLParameters from the specified array of ciphersuites. + * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by * <code>setCipherSuites(cipherSuites);</code>. @@ -82,6 +90,7 @@ /** * Constructs SSLParameters from the specified array of ciphersuites * and protocols. + * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>. @@ -178,4 +187,71 @@ this.needClientAuth = needClientAuth; } + /** + * Returns the cryptographic algorithm constraints. + * + * @return the cryptographic algorithm constraints, or null if the + * constraints have not been set + * + * @see #setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + public AlgorithmConstraints getAlgorithmConstraints() { + return algorithmConstraints; + } + + /** + * Sets the cryptographic algorithm constraints, which will be used + * in addition to any configured by the runtime environment. + * <p> + * If the <code>constraints</code> parameter is non-null, every + * cryptographic algorithm, key and algorithm parameters used in the + * SSL/TLS handshake must be permitted by the constraints. + * + * @param constraints the algorithm constraints (or null) + * + * @since 1.7 + */ + public void setAlgorithmConstraints(AlgorithmConstraints constraints) { + // the constraints object is immutable + this.algorithmConstraints = constraints; + } + + /** + * Gets the endpoint identification algorithm. + * + * @return the endpoint identification algorithm, or null if none + * has been set. + * + * @see X509ExtendedTrustManager + * @see #setEndpointIdentificationAlgorithm(String) + * + * @since 1.7 + */ + public String getEndpointIdentificationAlgorithm() { + return identificationAlgorithm; + } + + /** + * Sets the endpoint identification algorithm. + * <p> + * If the <code>algorithm</code> parameter is non-null or non-empty, the + * endpoint identification/verification procedures must be handled during + * SSL/TLS handshaking. This is to prevent man-in-the-middle attacks. + * + * @param algorithm The standard string name of the endpoint + * identification algorithm (or null). See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * + * @see X509ExtendedTrustManager + * + * @since 1.7 + */ + public void setEndpointIdentificationAlgorithm(String algorithm) { + this.identificationAlgorithm = algorithm; + } + }
--- a/src/share/classes/javax/net/ssl/SSLServerSocket.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/net/ssl/SSLServerSocket.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -56,8 +56,8 @@ * @since 1.4 * @author David Brownell */ -public abstract class SSLServerSocket extends ServerSocket -{ +public abstract class SSLServerSocket extends ServerSocket { + /** * Used only by subclasses. * <P> @@ -449,8 +449,79 @@ * * @return true indicates that sessions may be created; this * is the default. false indicates that an existing - * session must be resumed. + * session must be resumed * @see #setEnableSessionCreation(boolean) */ public abstract boolean getEnableSessionCreation(); + + /** + * Returns the SSLParameters in effect for newly accepted connections. + * The ciphersuites and protocols of the returned SSLParameters + * are always non-null. + * + * @return the SSLParameters in effect for newly accepted connections + * + * @see #setSSLParameters(SSLParameters) + * + * @since 1.7 + */ + public SSLParameters getSSLParameters() { + SSLParameters parameters = new SSLParameters(); + + parameters.setCipherSuites(getEnabledCipherSuites()); + parameters.setProtocols(getEnabledProtocols()); + if (getNeedClientAuth()) { + parameters.setNeedClientAuth(true); + } else if (getWantClientAuth()) { + parameters.setWantClientAuth(true); + } + + return parameters; + } + + /** + * Applies SSLParameters to newly accepted connections. + * + * <p>This means: + * <ul> + * <li>if <code>params.getCipherSuites()</code> is non-null, + * <code>setEnabledCipherSuites()</code> is called with that value + * <li>if <code>params.getProtocols()</code> is non-null, + * <code>setEnabledProtocols()</code> is called with that value + * <li>if <code>params.getNeedClientAuth()</code> or + * <code>params.getWantClientAuth()</code> return <code>true</code>, + * <code>setNeedClientAuth(true)</code> and + * <code>setWantClientAuth(true)</code> are called, respectively; + * otherwise <code>setWantClientAuth(false)</code> is called. + * </ul> + * + * @param params the parameters + * @throws IllegalArgumentException if the setEnabledCipherSuites() or + * the setEnabledProtocols() call fails + * + * @see #getSSLParameters() + * + * @since 1.7 + */ + public void setSSLParameters(SSLParameters params) { + String[] s; + s = params.getCipherSuites(); + if (s != null) { + setEnabledCipherSuites(s); + } + + s = params.getProtocols(); + if (s != null) { + setEnabledProtocols(s); + } + + if (params.getNeedClientAuth()) { + setNeedClientAuth(true); + } else if (params.getWantClientAuth()) { + setWantClientAuth(true); + } else { + setWantClientAuth(false); + } + } + }
--- a/src/share/classes/javax/net/ssl/SSLSocket.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/net/ssl/SSLSocket.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -371,6 +371,51 @@ /** + * Returns the {@code SSLSession} being constructed during a SSL/TLS + * handshake. + * <p> + * TLS protocols may negotiate parameters that are needed when using + * an instance of this class, but before the {@code SSLSession} has + * been completely initialized and made available via {@code getSession}. + * For example, the list of valid signature algorithms may restrict + * the type of certificates that can used during TrustManager + * decisions, or the maximum TLS fragment packet sizes can be + * resized to better support the network environment. + * <p> + * This method provides early access to the {@code SSLSession} being + * constructed. Depending on how far the handshake has progressed, + * some data may not yet be available for use. For example, if a + * remote server will be sending a Certificate chain, but that chain + * has yet not been processed, the {@code getPeerCertificates} + * method of {@code SSLSession} will throw a + * SSLPeerUnverifiedException. Once that chain has been processed, + * {@code getPeerCertificates} will return the proper value. + * <p> + * Unlike {@link #getSession()}, this method does not initiate the + * initial handshake and does not block until handshaking is + * complete. + * + * @see SSLEngine + * @see SSLSession + * @see ExtendedSSLSession + * @see X509ExtendedKeyManager + * @see X509ExtendedTrustManager + * + * @return null if this instance is not currently handshaking, or + * if the current handshake has not progressed far enough to + * create a basic SSLSession. Otherwise, this method returns the + * {@code SSLSession} currently being negotiated. + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. + * + * @since 1.7 + */ + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + + /** * Registers an event listener to receive notifications that an * SSL handshake has completed on this connection. *
--- a/src/share/classes/javax/net/ssl/SSLSocketFactory.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/javax/net/ssl/SSLSocketFactory.java Fri Nov 12 10:49:52 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -30,6 +30,7 @@ import javax.net.SocketFactory; import java.io.IOException; import java.security.*; +import java.util.Locale; import sun.security.action.GetPropertyAction; @@ -50,7 +51,8 @@ static { String s = java.security.AccessController.doPrivileged( - new GetPropertyAction("javax.net.debug", "")).toLowerCase(); + new GetPropertyAction("javax.net.debug", "")).toLowerCase( + Locale.ENGLISH); DEBUG = s.contains("all") || s.contains("ssl"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2010, 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 javax.net.ssl; + +import java.net.Socket; +import javax.net.ssl.X509TrustManager; + +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; + +/** + * Extensions to the <code>X509TrustManager</code> interface to support + * SSL/TLS connection sensitive trust management. + * <p> + * To prevent man-in-the-middle attacks, hostname checks can be done + * to verify that the hostname in an end-entity certificate matches the + * targeted hostname. TLS does not require such checks, but some protocols + * over TLS (such as HTTPS) do. In earlier versions of the JDK, the + * certificate chain checks were done at the SSL/TLS layer, and the hostname + * verification checks were done at the layer over TLS. This class allows + * for the checking to be done during a single call to this class. + * <p> + * RFC 2830 defines the server identification specification for the "LDAPS" + * algorithm. RFC 2818 defines both the server identification and the + * client identification specification for the "HTTPS" algorithm. + * + * @see X509TrustManager + * @see HostnameVerifier + * + * @since 1.7 + */ +public abstract class X509ExtendedTrustManager implements X509TrustManager { + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * 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. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the endpoint identification + * algorithm of the <code>SSLParameters</code> is non-empty, to prevent + * man-in-the-middle attacks, the address that the <code>socket</code> + * connected to should be checked against the peer's identity presented + * in the end-entity X509 certificate, as specified in the endpoint + * identification algorithm. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the algorithm constraints of the + * <code>SSLParameters</code> is non-null, for every certificate in the + * certification path, fields such as subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to the + * algorithm constraints in place on this socket. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param socket the socket used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, Socket socket) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Note: for some exportable cipher suites, the key + * exchange algorithm is determined at run time during the + * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, + * the authType should be RSA_EXPORT when an ephemeral RSA key is + * used for the key exchange, and RSA when the key from the server + * certificate is used. Checking is case-sensitive. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the endpoint identification + * algorithm of the <code>SSLParameters</code> is non-empty, to prevent + * man-in-the-middle attacks, the address that the <code>socket</code> + * connected to should be checked against the peer's identity presented + * in the end-entity X509 certificate, as specified in the endpoint + * identification algorithm. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the algorithm constraints of the + * <code>SSLParameters</code> is non-null, for every certificate in the + * certification path, fields such as subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to the + * algorithm constraints in place on this socket. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param socket the socket used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, Socket socket) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * 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. + * <p> + * If the <code>engine</code> parameter is available, and the endpoint + * identification algorithm of the <code>SSLParameters</code> is + * non-empty, to prevent man-in-the-middle attacks, the address that + * the <code>engine</code> connected to should be checked against + * the peer's identity presented in the end-entity X509 certificate, + * as specified in the endpoint identification algorithm. + * <p> + * If the <code>engine</code> parameter is available, and the algorithm + * constraints of the <code>SSLParameters</code> is non-null, for every + * certificate in the certification path, fields such as subject public + * key, the signature algorithm, key usage, extended key usage, etc. + * need to conform to the algorithm constraints in place on this engine. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param engine the engine used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Note: for some exportable cipher suites, the key + * exchange algorithm is determined at run time during the + * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, + * the authType should be RSA_EXPORT when an ephemeral RSA key is + * used for the key exchange, and RSA when the key from the server + * certificate is used. Checking is case-sensitive. + * <p> + * If the <code>engine</code> parameter is available, and the endpoint + * identification algorithm of the <code>SSLParameters</code> is + * non-empty, to prevent man-in-the-middle attacks, the address that + * the <code>engine</code> connected to should be checked against + * the peer's identity presented in the end-entity X509 certificate, + * as specified in the endpoint identification algorithm. + * <p> + * If the <code>engine</code> parameter is available, and the algorithm + * constraints of the <code>SSLParameters</code> is non-null, for every + * certificate in the certification path, fields such as subject public + * key, the signature algorithm, key usage, extended key usage, etc. + * need to conform to the algorithm constraints in place on this engine. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param engine the engine used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) throws CertificateException; + +}
--- a/src/share/classes/sun/dyn/BoundMethodHandle.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/BoundMethodHandle.java Fri Nov 12 10:49:52 2010 -0800 @@ -48,8 +48,6 @@ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN); // Constructors in this class *must* be package scoped or private. - // Exception: JavaMethodHandle constructors are protected. - // (The link between JMH and BMH is temporary.) /** Bind a direct MH to its receiver (or first ref. argument). * The JVM will pre-dispatch the MH if it is not already static. @@ -122,55 +120,6 @@ assert(this instanceof JavaMethodHandle); } - /** Initialize the current object as a Java method handle. - */ - protected BoundMethodHandle(String entryPointName, MethodType type, boolean matchArity) { - super(Access.TOKEN, null); - MethodHandle entryPoint - = findJavaMethodHandleEntryPoint(this.getClass(), - entryPointName, type, matchArity); - MethodHandleImpl.initType(this, entryPoint.type().dropParameterTypes(0, 1)); - this.argument = this; // kludge; get rid of - this.vmargslot = this.type().parameterSlotDepth(0); - initTarget(entryPoint, 0); - assert(this instanceof JavaMethodHandle); - } - - private static - MethodHandle findJavaMethodHandleEntryPoint(Class<?> caller, - String name, - MethodType type, - boolean matchArity) { - if (matchArity) type.getClass(); // elicit NPE - List<MemberName> methods = IMPL_NAMES.getMethods(caller, true, name, null, caller); - MethodType foundType = null; - MemberName foundMethod = null; - for (MemberName method : methods) { - if (method.getDeclaringClass() == MethodHandle.class) - continue; // ignore methods inherited from MH class itself - MethodType mtype = method.getMethodType(); - if (type != null && type.parameterCount() != mtype.parameterCount()) - continue; - else if (foundType == null) - foundType = mtype; - else if (foundType != mtype) - throw newIllegalArgumentException("more than one method named "+name+" in "+caller.getName()); - // discard overrides - if (foundMethod == null) - foundMethod = method; - else if (foundMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) - foundMethod = method; - } - if (foundMethod == null) - throw newIllegalArgumentException("no method named "+name+" in "+caller.getName()); - MethodHandle entryPoint = MethodHandleImpl.findMethod(IMPL_TOKEN, foundMethod, true, caller); - if (type != null) { - MethodType epType = type.insertParameterTypes(0, entryPoint.type().parameterType(0)); - entryPoint = MethodHandles.convertArguments(entryPoint, epType); - } - return entryPoint; - } - /** Make sure the given {@code argument} can be used as {@code argnum}-th * parameter of the given method handle {@code mh}, which must be a reference. * <p>
--- a/src/share/classes/sun/dyn/CallSiteImpl.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/CallSiteImpl.java Fri Nov 12 10:49:52 2010 -0800 @@ -26,6 +26,7 @@ package sun.dyn; import java.dyn.*; +import static sun.dyn.MemberName.uncaughtException; /** * Parts of CallSite known to the JVM. @@ -49,18 +50,21 @@ } CallSite site; try { - if (bootstrapMethod.type().parameterCount() == 3) - site = bootstrapMethod.<CallSite>invokeExact(caller, name, type); - else if (bootstrapMethod.type().parameterCount() == 4) - site = bootstrapMethod.<CallSite>invokeExact(caller, name, type, - !(info instanceof java.lang.annotation.Annotation[]) ? null - : (java.lang.annotation.Annotation[]) info); + Object binding; + if (false) // switch when invokeGeneric works + binding = bootstrapMethod.invokeGeneric(caller, name, type); else - throw new InternalError("bad BSM: "+bootstrapMethod); - if (!(site instanceof CallSite)) - throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller); - PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site, - name, type, + binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type }); + //System.out.println("BSM for "+name+type+" => "+binding); + if (binding instanceof CallSite) { + site = (CallSite) binding; + } else if (binding instanceof MethodHandleProvider) { + MethodHandle target = ((MethodHandleProvider) binding).asMethodHandle(); + site = new ConstantCallSite(target); + } else { + throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite"); + } + PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site, name, type, callerMethod, callerBCI); assert(site.getTarget() != null); assert(site.getTarget().type().equals(type)); @@ -77,11 +81,18 @@ // This method is private in CallSite because it touches private fields in CallSite. // These private fields (vmmethod, vmindex) are specific to the JVM. - private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE = + private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE; + static { + try { + PRIVATE_INITIALIZE_CALL_SITE = MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM", MethodType.methodType(void.class, String.class, MethodType.class, MemberName.class, int.class)); + } catch (NoAccessException ex) { + throw uncaughtException(ex); + } + } public static void setCallSiteTarget(Access token, CallSite site, MethodHandle target) { Access.check(token);
--- a/src/share/classes/sun/dyn/FilterGeneric.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/FilterGeneric.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,12 +25,8 @@ package sun.dyn; -import java.dyn.JavaMethodHandle; -import java.dyn.MethodHandle; -import java.dyn.MethodType; -import java.dyn.NoAccessException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import java.dyn.*; +import java.lang.reflect.*; import static sun.dyn.MemberName.newIllegalArgumentException; /** @@ -119,7 +115,7 @@ static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) { FilterGeneric fgen = of(kind, pos, filter.type(), target.type()); - return fgen.makeInstance(kind, pos, filter, target); + return fgen.makeInstance(kind, pos, filter, target).asMethodHandle(); } /** Return the adapter information for this target and filter type. */
--- a/src/share/classes/sun/dyn/FilterOneArgument.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/FilterOneArgument.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,9 +25,8 @@ package sun.dyn; -import java.dyn.JavaMethodHandle; -import java.dyn.MethodHandle; -import java.dyn.MethodType; +import java.dyn.*; +import static sun.dyn.MemberName.uncaughtException; /** * Unary function composition, useful for many small plumbing jobs. @@ -51,8 +50,16 @@ return target.invokeExact(filteredArgument); } - private static final MethodHandle INVOKE = - MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", MethodType.genericMethodType(1)); + private static final MethodHandle INVOKE; + static { + try { + INVOKE = + MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", + MethodType.genericMethodType(1)); + } catch (NoAccessException ex) { + throw uncaughtException(ex); + } + } protected FilterOneArgument(MethodHandle filter, MethodHandle target) { super(INVOKE);
--- a/src/share/classes/sun/dyn/FromGeneric.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/FromGeneric.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,15 +25,9 @@ package sun.dyn; -import java.dyn.JavaMethodHandle; -import java.dyn.MethodHandle; -import java.dyn.MethodHandles; -import java.dyn.MethodType; -import java.dyn.NoAccessException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import sun.dyn.util.ValueConversions; -import sun.dyn.util.Wrapper; +import java.dyn.*; +import java.lang.reflect.*; +import sun.dyn.util.*; /** * Adapters which mediate between incoming calls which are generic
--- a/src/share/classes/sun/dyn/Invokers.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/Invokers.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,10 +25,7 @@ package sun.dyn; -import java.dyn.MethodHandle; -import java.dyn.MethodHandles; -import java.dyn.MethodType; - +import java.dyn.*; /** * Construction and caching of often-used invokers. @@ -63,8 +60,11 @@ public MethodHandle exactInvoker() { MethodHandle invoker = exactInvoker; if (invoker != null) return invoker; - invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); - if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType); + try { + invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); + } catch (NoAccessException ex) { + throw new InternalError("JVM cannot find invoker for "+targetType); + } assert(invokerType(targetType) == invoker.type()); exactInvoker = invoker; return invoker;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/JavaMethodHandle.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2008, 2009, 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 sun.dyn; + +import java.dyn.*; +import sun.dyn.Access; + +/** + * A Java method handle is a deprecated proposal for extending + * the basic method handle type with additional + * programmer defined methods and fields. + * Its behavior as a method handle is determined at instance creation time, + * by providing the new instance with an "entry point" method handle + * to handle calls. This entry point must accept a leading argument + * whose type is the Java method handle itself or a supertype, and the + * entry point is always called with the Java method handle itself as + * the first argument. This is similar to ordinary virtual methods, which also + * accept the receiver object {@code this} as an implicit leading argument. + * The {@code MethodType} of the Java method handle is the same as that + * of the entry point method handle, with the leading parameter type + * omitted. + * <p> + * Here is an example of usage, creating a hybrid object/functional datum: + * <p><blockquote><pre> + * class Greeter extends JavaMethodHandle { + * private String greeting = "hello"; + * public void setGreeting(String s) { greeting = s; } + * public void run() { System.out.println(greeting+", "+greetee); } + * private final String greetee; + * Greeter(String greetee) { + * super(RUN); // alternatively, super("run") + * this.greetee = greetee; + * } + * // the entry point function is computed once: + * private static final MethodHandle RUN + * = MethodHandles.lookup().findVirtual(Greeter.class, "run", + * MethodType.make(void.class)); + * } + * // class Main { public static void main(String... av) { ... + * Greeter greeter = new Greeter("world"); + * greeter.run(); // prints "hello, world" + * // Statically typed method handle invocation (most direct): + * MethodHandle mh = greeter; + * mh.<void>invokeExact(); // also prints "hello, world" + * // Dynamically typed method handle invocation: + * MethodHandles.invokeExact(greeter); // also prints "hello, world" + * greeter.setGreeting("howdy"); + * mh.invokeExact(); // prints "howdy, world" (object-like mutable behavior) + * </pre></blockquote> + * <p> + * In the example of {@code Greeter}, the method {@code run} provides the entry point. + * The entry point need not be a constant value; it may be independently + * computed in each call to the constructor. The entry point does not + * even need to be a method on the {@code Greeter} class, though + * that is the typical case. + * <p> + * The entry point may also be provided symbolically, in which case the the + * {@code JavaMethodHandle} constructor performs the lookup of the entry point. + * This makes it possible to use {@code JavaMethodHandle} to create an anonymous + * inner class: + * <p><blockquote><pre> + * // We can also do this with symbolic names and/or inner classes: + * MethodHandles.invokeExact(new JavaMethodHandle("yow") { + * void yow() { System.out.println("yow, world"); } + * }); + * </pre></blockquote> + * <p> + * Here is similar lower-level code which works in terms of a bound method handle. + * <p><blockquote><pre> + * class Greeter { + * public void run() { System.out.println("hello, "+greetee); } + * private final String greetee; + * Greeter(String greetee) { this.greetee = greetee; } + * // the entry point function is computed once: + * private static final MethodHandle RUN + * = MethodHandles.findVirtual(Greeter.class, "run", + * MethodType.make(void.class)); + * } + * // class Main { public static void main(String... av) { ... + * Greeter greeter = new Greeter("world"); + * greeter.run(); // prints "hello, world" + * MethodHandle mh = MethodHanndles.insertArgument(Greeter.RUN, 0, greeter); + * mh.invokeExact(); // also prints "hello, world" + * </pre></blockquote> + * Note that the method handle must be separately created as a view on the base object. + * This increases footprint, complexity, and dynamic indirections. + * <p> + * Here is a pure functional value expressed most concisely as an anonymous inner class: + * <p><blockquote><pre> + * // class Main { public static void main(String... av) { ... + * final String greetee = "world"; + * MethodHandle greeter = new JavaMethodHandle("run") { + * private void run() { System.out.println("hello, "+greetee); } + * } + * greeter.invokeExact(); // prints "hello, world" + * </pre></blockquote> + * <p> + * Here is an abstract parameterized lvalue, efficiently expressed as a subtype of MethodHandle, + * and instantiated as an anonymous class. The data structure is a handle to 1-D array, + * with a specialized index type (long). It is created by inner class, and uses + * signature-polymorphic APIs throughout. + * <p><blockquote><pre> + * abstract class AssignableMethodHandle extends JavaMethodHandle { + * private final MethodHandle setter; + * public MethodHandle setter() { return setter; } + * public AssignableMethodHandle(String get, String set) { + * super(get); + * MethodType getType = this.type(); + * MethodType setType = getType.insertParameterType(getType.parameterCount(), getType.returnType()).changeReturnType(void.class); + * this.setter = MethodHandles.publicLookup().bind(this, set, setType); + * } + * } + * // class Main { public static void main(String... av) { ... + * final Number[] stuff = { 123, 456 }; + * AssignableMethodHandle stuffPtr = new AssignableMethodHandle("get", "set") { + * public Number get(long i) { return stuff[(int)i]; } + * public void set(long i, Object x) { stuff[(int)i] = x; } + * } + * int x = (Integer) stuffPtr.<Number>invokeExact(1L); // 456 + * stuffPtr.setter().<void>invokeExact(0L, (Number) 789); // replaces 123 with 789 + * </pre></blockquote> + * @see MethodHandle + * @deprecated The JSR 292 EG intends to replace {@code JavaMethodHandle} with + * an interface-based API for mixing method handle behavior with other classes. + * @author John Rose, JSR 292 EG + */ +public abstract class JavaMethodHandle + // Note: This is an implementation inheritance hack, and will be removed + // with a JVM change which moves the required hidden behavior onto this class. + extends sun.dyn.BoundMethodHandle +{ + private static final Access IMPL_TOKEN = Access.getToken(); + + /** + * When creating a {@code JavaMethodHandle}, the actual method handle + * invocation behavior will be delegated to the specified {@code entryPoint}. + * This may be any method handle which can take the newly constructed object + * as a leading parameter. + * <p> + * The method handle type of {@code this} (i.e, the fully constructed object) + * will be {@code entryPoint}, minus the leading argument. + * The leading argument will be bound to {@code this} on every method + * handle invocation. + * @param entryPoint the method handle to handle calls + */ + protected JavaMethodHandle(MethodHandle entryPoint) { + super(entryPoint); + } +}
--- a/src/share/classes/sun/dyn/MemberName.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/MemberName.java Fri Nov 12 10:49:52 2010 -0800 @@ -521,6 +521,11 @@ if (lookupClass != null) message += ", from " + lookupClass.getName(); return new NoAccessException(message); } + public static Error uncaughtException(Exception ex) { + Error err = new InternalError("uncaught exception"); + err.initCause(ex); + return err; + } /** Actually making a query requires an access check. */ public static Factory getFactory(Access token) { @@ -641,7 +646,7 @@ * If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown. * Otherwise a fresh copy of the given member is returned, with modifier bits filled in. */ - public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) { + public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException { MemberName result = resolveOrNull(m, searchSupers, lookupClass); if (result != null) return result;
--- a/src/share/classes/sun/dyn/MethodHandleImpl.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/MethodHandleImpl.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,11 +25,8 @@ package sun.dyn; -import java.dyn.JavaMethodHandle; -import java.dyn.MethodHandle; -import java.dyn.MethodHandles; +import java.dyn.*; import java.dyn.MethodHandles.Lookup; -import java.dyn.MethodType; import java.util.logging.Level; import java.util.logging.Logger; import sun.dyn.util.VerifyType; @@ -46,6 +43,7 @@ import sun.misc.Unsafe; import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newNoAccessException; +import static sun.dyn.MemberName.uncaughtException; /** * Base class for method handles, containing JVM-specific fields and logic. @@ -173,7 +171,7 @@ */ public static MethodHandle findMethod(Access token, MemberName method, - boolean doDispatch, Class<?> lookupClass) { + boolean doDispatch, Class<?> lookupClass) throws NoAccessException { Access.check(token); // only trusted calls MethodType mtype = method.getMethodType(); if (!method.isStatic()) { @@ -320,7 +318,7 @@ try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true)); } catch (NoAccessException ex) { - throw new InternalError(""); + throw uncaughtException(ex); } } // Corresponding generic constructor types: @@ -416,9 +414,7 @@ f = c.getDeclaredField(field.getName()); return unsafe.staticFieldBase(f); } catch (Exception ee) { - Error e = new InternalError(); - e.initCause(ee); - throw e; + throw uncaughtException(ee); } } @@ -473,10 +469,8 @@ MethodHandle mh; try { mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type); - } catch (NoAccessException ee) { - Error e = new InternalError("name,type="+name+type); - e.initCause(ee); - throw e; + } catch (NoAccessException ex) { + throw uncaughtException(ex); } if (evclass != vclass || (!isStatic && ecclass != cclass)) { MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); @@ -543,10 +537,8 @@ MethodHandle mh; try { mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type); - } catch (NoAccessException ee) { - Error e = new InternalError("name,type="+name+type); - e.initCause(ee); - throw e; + } catch (NoAccessException ex) { + throw uncaughtException(ex); } if (caclass != null) { MethodType strongType = FieldAccessor.atype(caclass, isSetter); @@ -1031,7 +1023,7 @@ try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true)); } catch (NoAccessException ex) { - throw new InternalError(""); + throw uncaughtException(ex); } } } @@ -1167,7 +1159,7 @@ try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); } catch (NoAccessException ex) { - throw new InternalError(""); + throw uncaughtException(ex); } } } @@ -1207,9 +1199,16 @@ return AdapterMethodHandle.makeRetypeRaw(token, type, THROW_EXCEPTION); } - static final MethodHandle THROW_EXCEPTION + static final MethodHandle THROW_EXCEPTION; + static { + try { + THROW_EXCEPTION = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", MethodType.methodType(Empty.class, Throwable.class)); + } catch (NoAccessException ex) { + throw new RuntimeException(ex); + } + } static <T extends Throwable> Empty throwException(T t) throws T { throw t; } public static String getNameString(Access token, MethodHandle target) {
--- a/src/share/classes/sun/dyn/MethodHandleNatives.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/MethodHandleNatives.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,9 +25,7 @@ package sun.dyn; -import java.dyn.CallSite; -import java.dyn.MethodHandle; -import java.dyn.MethodType; +import java.dyn.*; import java.dyn.MethodHandles.Lookup; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; @@ -324,18 +322,24 @@ */ static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind, Class<?> defc, String name, Object type) { - Lookup lookup = IMPL_LOOKUP.in(callerClass); - switch (refKind) { - case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type ); - case REF_getStatic: return lookup.findStaticGetter( defc, name, (Class<?>) type ); - case REF_putField: return lookup.findSetter( defc, name, (Class<?>) type ); - case REF_putStatic: return lookup.findStaticSetter( defc, name, (Class<?>) type ); - case REF_invokeVirtual: return lookup.findVirtual( defc, name, (MethodType) type ); - case REF_invokeStatic: return lookup.findStatic( defc, name, (MethodType) type ); - case REF_invokeSpecial: return lookup.findSpecial( defc, name, (MethodType) type, callerClass ); - case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type ); - case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type ); + try { + Lookup lookup = IMPL_LOOKUP.in(callerClass); + switch (refKind) { + case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type ); + case REF_getStatic: return lookup.findStaticGetter( defc, name, (Class<?>) type ); + case REF_putField: return lookup.findSetter( defc, name, (Class<?>) type ); + case REF_putStatic: return lookup.findStaticSetter( defc, name, (Class<?>) type ); + case REF_invokeVirtual: return lookup.findVirtual( defc, name, (MethodType) type ); + case REF_invokeStatic: return lookup.findStatic( defc, name, (MethodType) type ); + case REF_invokeSpecial: return lookup.findSpecial( defc, name, (MethodType) type, callerClass ); + case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type ); + case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type ); + } + throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type); + } catch (NoAccessException ex) { + Error err = new IncompatibleClassChangeError(); + err.initCause(ex); + throw err; } - throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type); } }
--- a/src/share/classes/sun/dyn/SpreadGeneric.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/SpreadGeneric.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,11 +25,7 @@ package sun.dyn; -import java.dyn.JavaMethodHandle; -import java.dyn.MethodHandle; -import java.dyn.MethodHandles; -import java.dyn.MethodType; -import java.dyn.NoAccessException; +import java.dyn.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList;
--- a/src/share/classes/sun/dyn/ToGeneric.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/ToGeneric.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,11 +25,7 @@ package sun.dyn; -import java.dyn.JavaMethodHandle; -import java.dyn.MethodHandle; -import java.dyn.MethodHandles; -import java.dyn.MethodType; -import java.dyn.NoAccessException; +import java.dyn.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import sun.dyn.util.ValueConversions;
--- a/src/share/classes/sun/dyn/util/ValueConversions.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/dyn/util/ValueConversions.java Fri Nov 12 10:49:52 2010 -0800 @@ -34,6 +34,7 @@ import sun.dyn.Access; import sun.dyn.AdapterMethodHandle; import sun.dyn.MethodHandleImpl; +import static sun.dyn.MemberName.uncaughtException; public class ValueConversions { private static final Access IMPL_TOKEN = Access.getToken(); @@ -148,11 +149,16 @@ // look up the method String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : ""); MethodType type = unboxType(wrap, raw); - if (!exact) - // actually, type is wrong; the Java method takes Object - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); - else + if (!exact) { + try { + // actually, type is wrong; the Java method takes Object + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase()); + } catch (NoAccessException ex) { + mh = null; + } + } else { mh = retype(type, unbox(wrap, !exact, raw)); + } if (mh != null) { cache.put(wrap, mh); return mh; @@ -280,10 +286,15 @@ // look up the method String name = "box" + wrap.simpleName() + (raw ? "Raw" : ""); MethodType type = boxType(wrap, raw); - if (exact) - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); - else + if (exact) { + try { + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); + } catch (NoAccessException ex) { + mh = null; + } + } else { mh = retype(type.erase(), box(wrap, !exact, raw)); + } if (mh != null) { cache.put(wrap, mh); return mh; @@ -394,10 +405,15 @@ // look up the method String name = "reboxRaw" + wrap.simpleName(); MethodType type = reboxType(wrap); - if (exact) - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); - else + if (exact) { + try { + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type); + } catch (NoAccessException ex) { + mh = null; + } + } else { mh = retype(IDENTITY.type(), rebox(wrap, !exact)); + } if (mh != null) { cache.put(wrap, mh); return mh; @@ -474,7 +490,11 @@ mh = EMPTY; break; case INT: case LONG: case FLOAT: case DOUBLE: - mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); + try { + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type); + } catch (NoAccessException ex) { + mh = null; + } break; } if (mh != null) { @@ -549,8 +569,8 @@ ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType); IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType); EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1)); - } catch (RuntimeException ex) { - throw ex; + } catch (Exception ex) { + throw uncaughtException(ex); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/java2d/pisces/Curve.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2007, 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 sun.java2d.pisces; + +import java.util.Iterator; + +class Curve { + + float ax, ay, bx, by, cx, cy, dx, dy; + float dax, day, dbx, dby; + + Curve() { + } + + void set(float[] points, int type) { + switch(type) { + case 8: + set(points[0], points[1], + points[2], points[3], + points[4], points[5], + points[6], points[7]); + break; + case 6: + set(points[0], points[1], + points[2], points[3], + points[4], points[5]); + break; + default: + throw new InternalError("Curves can only be cubic or quadratic"); + } + } + + void set(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4) + { + ax = 3 * (x2 - x3) + x4 - x1; + ay = 3 * (y2 - y3) + y4 - y1; + bx = 3 * (x1 - 2 * x2 + x3); + by = 3 * (y1 - 2 * y2 + y3); + cx = 3 * (x2 - x1); + cy = 3 * (y2 - y1); + dx = x1; + dy = y1; + dax = 3 * ax; day = 3 * ay; + dbx = 2 * bx; dby = 2 * by; + } + + void set(float x1, float y1, + float x2, float y2, + float x3, float y3) + { + ax = ay = 0f; + + bx = x1 - 2 * x2 + x3; + by = y1 - 2 * y2 + y3; + cx = 2 * (x2 - x1); + cy = 2 * (y2 - y1); + dx = x1; + dy = y1; + dax = 0; day = 0; + dbx = 2 * bx; dby = 2 * by; + } + + float xat(float t) { + return t * (t * (t * ax + bx) + cx) + dx; + } + float yat(float t) { + return t * (t * (t * ay + by) + cy) + dy; + } + + float dxat(float t) { + return t * (t * dax + dbx) + cx; + } + + float dyat(float t) { + return t * (t * day + dby) + cy; + } + + private float ddxat(float t) { + return 2 * dax * t + dbx; + } + + private float ddyat(float t) { + return 2 * day * t + dby; + } + + int dxRoots(float[] roots, int off) { + return Helpers.quadraticRoots(dax, dbx, cx, roots, off); + } + + int dyRoots(float[] roots, int off) { + return Helpers.quadraticRoots(day, dby, cy, roots, off); + } + + int infPoints(float[] pts, int off) { + // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0 + // Fortunately, this turns out to be quadratic, so there are at + // most 2 inflection points. + final float a = dax * dby - dbx * day; + final float b = 2 * (cy * dax - day * cx); + final float c = cy * dbx - cx * dby; + + return Helpers.quadraticRoots(a, b, c, pts, off); + } + + // finds points where the first and second derivative are + // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where + // * is a dot product). Unfortunately, we have to solve a cubic. + private int perpendiculardfddf(float[] pts, int off, final float err) { + assert pts.length >= off + 4; + + // these are the coefficients of g(t): + final float a = 2*(dax*dax + day*day); + final float b = 3*(dax*dbx + day*dby); + final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby; + final float d = dbx*cx + dby*cy; + // TODO: We might want to divide the polynomial by a to make the + // coefficients smaller. This won't change the roots. + return Helpers.cubicRootsInAB(a, b, c, d, pts, off, err, 0f, 1f); + } + + // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses + // a variant of the false position algorithm to find the roots. False + // position requires that 2 initial values x0,x1 be given, and that the + // function must have opposite signs at those values. To find such + // values, we need the local extrema of the ROC function, for which we + // need the roots of its derivative; however, it's harder to find the + // roots of the derivative in this case than it is to find the roots + // of the original function. So, we find all points where this curve's + // first and second derivative are perpendicular, and we pretend these + // are our local extrema. There are at most 3 of these, so we will check + // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection + // points, so roc-w can have at least 6 roots. This shouldn't be a + // problem for what we're trying to do (draw a nice looking curve). + int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) { + // no OOB exception, because by now off<=6, and roots.length >= 10 + assert off <= 6 && roots.length >= 10; + int ret = off; + int numPerpdfddf = perpendiculardfddf(roots, off, err); + float t0 = 0, ft0 = ROCsq(t0) - w*w; + roots[off + numPerpdfddf] = 1f; // always check interval end points + numPerpdfddf++; + for (int i = off; i < off + numPerpdfddf; i++) { + float t1 = roots[i], ft1 = ROCsq(t1) - w*w; + if (ft0 == 0f) { + roots[ret++] = t0; + } else if (ft1 * ft0 < 0f) { // have opposite signs + // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because + // ROC(t) >= 0 for all t. + roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err); + } + t0 = t1; + ft0 = ft1; + } + + return ret - off; + } + + private static float eliminateInf(float x) { + return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE : + (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x)); + } + + // A slight modification of the false position algorithm on wikipedia. + // This only works for the ROCsq-x functions. It might be nice to have + // the function as an argument, but that would be awkward in java6. + // It is something to consider for java7, depending on how closures + // and function objects turn out. Same goes for the newton's method + // algorithm in Helpers.java + private float falsePositionROCsqMinusX(float x0, float x1, + final float x, final float err) + { + final int iterLimit = 100; + int side = 0; + float t = x1, ft = eliminateInf(ROCsq(t) - x); + float s = x0, fs = eliminateInf(ROCsq(s) - x); + float r = s, fr; + for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) { + r = (fs * t - ft * s) / (fs - ft); + fr = ROCsq(r) - x; + if (fr * ft > 0) {// have the same sign + ft = fr; t = r; + if (side < 0) { + fs /= (1 << (-side)); + side--; + } else { + side = -1; + } + } else if (fr * fs > 0) { + fs = fr; s = r; + if (side > 0) { + ft /= (1 << side); + side++; + } else { + side = 1; + } + } else { + break; + } + } + return r; + } + + // returns the radius of curvature squared at t of this curve + // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications) + private float ROCsq(final float t) { + final float dx = dxat(t); + final float dy = dyat(t); + final float ddx = ddxat(t); + final float ddy = ddyat(t); + final float dx2dy2 = dx*dx + dy*dy; + final float ddx2ddy2 = ddx*ddx + ddy*ddy; + final float ddxdxddydy = ddx*dx + ddy*dy; + float ret = ((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy))*dx2dy2; + return ret; + } + + // curve to be broken should be in pts[0] + // this will change the contents of both pts and Ts + // TODO: There's no reason for Ts to be an array. All we need is a sequence + // of t values at which to subdivide. An array statisfies this condition, + // but is unnecessarily restrictive. Ts should be an Iterator<Float> instead. + // Doing this will also make dashing easier, since we could easily make + // LengthIterator an Iterator<Float> and feed it to this function to simplify + // the loop in Dasher.somethingTo. + static Iterator<float[]> breakPtsAtTs(final float[][] pts, final int type, + final float[] Ts, final int numTs) + { + assert pts.length >= 2 && pts[0].length >= 8 && numTs <= Ts.length; + return new Iterator<float[]>() { + int nextIdx = 0; + int nextCurveIdx = 0; + float prevT = 0; + + @Override public boolean hasNext() { + return nextCurveIdx < numTs + 1; + } + + @Override public float[] next() { + float[] ret; + if (nextCurveIdx < numTs) { + float curT = Ts[nextCurveIdx]; + float splitT = (curT - prevT) / (1 - prevT); + Helpers.subdivideAt(splitT, + pts[nextIdx], 0, + pts[nextIdx], 0, + pts[1-nextIdx], 0, type); + updateTs(Ts, Ts[nextCurveIdx], nextCurveIdx + 1, numTs - nextCurveIdx - 1); + ret = pts[nextIdx]; + nextIdx = 1 - nextIdx; + } else { + ret = pts[nextIdx]; + } + nextCurveIdx++; + return ret; + } + + @Override public void remove() {} + }; + } + + // precondition: ts[off]...ts[off+len-1] must all be greater than t. + private static void updateTs(float[] ts, final float t, final int off, final int len) { + for (int i = off; i < off + len; i++) { + ts[i] = (ts[i] - t) / (1 - t); + } + } +} +
--- a/src/share/classes/sun/java2d/pisces/Dasher.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/java2d/pisces/Dasher.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,6 +25,8 @@ package sun.java2d.pisces; +import sun.awt.geom.PathConsumer2D; + /** * The <code>Dasher</code> class takes a series of linear commands * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and @@ -36,18 +38,16 @@ * semantics are unclear. * */ -public class Dasher implements LineSink { - private final LineSink output; +public class Dasher implements sun.awt.geom.PathConsumer2D { + + private final PathConsumer2D out; private final float[] dash; private final float startPhase; private final boolean startDashOn; private final int startIdx; - private final float m00, m10, m01, m11; - private final float det; - - private boolean firstDashOn; private boolean starting; + private boolean needsMoveTo; private int idx; private boolean dashOn; @@ -55,28 +55,23 @@ private float sx, sy; private float x0, y0; - private float sx1, sy1; + // temporary storage for the current curve + private float[] curCurvepts; /** * Constructs a <code>Dasher</code>. * - * @param output an output <code>LineSink</code>. - * @param dash an array of <code>int</code>s containing the dash pattern - * @param phase an <code>int</code> containing the dash phase - * @param transform a <code>Transform4</code> object indicating - * the transform that has been previously applied to all incoming - * coordinates. This is required in order to compute dash lengths - * properly. + * @param out an output <code>PathConsumer2D</code>. + * @param dash an array of <code>float</code>s containing the dash pattern + * @param phase a <code>float</code> containing the dash phase */ - public Dasher(LineSink output, - float[] dash, float phase, - float a00, float a01, float a10, float a11) { + public Dasher(PathConsumer2D out, float[] dash, float phase) { if (phase < 0) { throw new IllegalArgumentException("phase < 0 !"); } - this.output = output; + this.out = out; // Normalize so 0 <= phase < dash[0] int idx = 0; @@ -92,16 +87,19 @@ this.startPhase = this.phase = phase; this.startDashOn = dashOn; this.startIdx = idx; + this.starting = true; - m00 = a00; - m01 = a01; - m10 = a10; - m11 = a11; - det = m00 * m11 - m01 * m10; + // we need curCurvepts to be able to contain 2 curves because when + // dashing curves, we need to subdivide it + curCurvepts = new float[8 * 2]; } public void moveTo(float x0, float y0) { - output.moveTo(x0, y0); + if (firstSegidx > 0) { + out.moveTo(sx, sy); + emitFirstSegments(); + } + needsMoveTo = true; this.idx = startIdx; this.dashOn = this.startDashOn; this.phase = this.startPhase; @@ -110,88 +108,108 @@ this.starting = true; } - public void lineJoin() { - output.lineJoin(); + private void emitSeg(float[] buf, int off, int type) { + switch (type) { + case 8: + out.curveTo(buf[off+0], buf[off+1], + buf[off+2], buf[off+3], + buf[off+4], buf[off+5]); + break; + case 6: + out.quadTo(buf[off+0], buf[off+1], + buf[off+2], buf[off+3]); + break; + case 4: + out.lineTo(buf[off], buf[off+1]); + } } - private void goTo(float x1, float y1) { + private void emitFirstSegments() { + for (int i = 0; i < firstSegidx; ) { + emitSeg(firstSegmentsBuffer, i+1, (int)firstSegmentsBuffer[i]); + i += (((int)firstSegmentsBuffer[i]) - 1); + } + firstSegidx = 0; + } + + // We don't emit the first dash right away. If we did, caps would be + // drawn on it, but we need joins to be drawn if there's a closePath() + // So, we store the path elements that make up the first dash in the + // buffer below. + private float[] firstSegmentsBuffer = new float[7]; + private int firstSegidx = 0; + // precondition: pts must be in relative coordinates (relative to x0,y0) + // fullCurve is true iff the curve in pts has not been split. + private void goTo(float[] pts, int off, final int type) { + float x = pts[off + type - 4]; + float y = pts[off + type - 3]; if (dashOn) { if (starting) { - this.sx1 = x1; - this.sy1 = y1; - firstDashOn = true; - starting = false; + firstSegmentsBuffer = Helpers.widenArray(firstSegmentsBuffer, + firstSegidx, type - 2); + firstSegmentsBuffer[firstSegidx++] = type; + System.arraycopy(pts, off, firstSegmentsBuffer, firstSegidx, type - 2); + firstSegidx += type - 2; + } else { + if (needsMoveTo) { + out.moveTo(x0, y0); + needsMoveTo = false; + } + emitSeg(pts, off, type); } - output.lineTo(x1, y1); } else { - if (starting) { - firstDashOn = false; - starting = false; - } - output.moveTo(x1, y1); + starting = false; + needsMoveTo = true; } - this.x0 = x1; - this.y0 = y1; + this.x0 = x; + this.y0 = y; } public void lineTo(float x1, float y1) { - // The widened line is squished to a 0 width one, so no drawing is done - if (det == 0) { - goTo(x1, y1); - return; - } float dx = x1 - x0; float dy = y1 - y0; + float len = (float) Math.hypot(dx, dy); - // Compute segment length in the untransformed - // coordinate system - - float la = (dy*m00 - dx*m10)/det; - float lb = (dy*m01 - dx*m11)/det; - float origLen = (float) Math.hypot(la, lb); - - if (origLen == 0) { - // Let the output LineSink deal with cases where dx, dy are 0. - goTo(x1, y1); + if (len == 0) { return; } // The scaling factors needed to get the dx and dy of the // transformed dash segments. - float cx = dx / origLen; - float cy = dy / origLen; + float cx = dx / len; + float cy = dy / len; while (true) { float leftInThisDashSegment = dash[idx] - phase; - if (origLen < leftInThisDashSegment) { - goTo(x1, y1); + if (len <= leftInThisDashSegment) { + curCurvepts[0] = x1; + curCurvepts[1] = y1; + goTo(curCurvepts, 0, 4); // Advance phase within current dash segment - phase += origLen; - return; - } else if (origLen == leftInThisDashSegment) { - goTo(x1, y1); - phase = 0f; - idx = (idx + 1) % dash.length; - dashOn = !dashOn; + phase += len; + if (len == leftInThisDashSegment) { + phase = 0f; + idx = (idx + 1) % dash.length; + dashOn = !dashOn; + } return; } - float dashx, dashy; float dashdx = dash[idx] * cx; float dashdy = dash[idx] * cy; if (phase == 0) { - dashx = x0 + dashdx; - dashy = y0 + dashdy; + curCurvepts[0] = x0 + dashdx; + curCurvepts[1] = y0 + dashdy; } else { - float p = (leftInThisDashSegment) / dash[idx]; - dashx = x0 + p * dashdx; - dashy = y0 + p * dashdy; + float p = leftInThisDashSegment / dash[idx]; + curCurvepts[0] = x0 + p * dashdx; + curCurvepts[1] = y0 + p * dashdy; } - goTo(dashx, dashy); + goTo(curCurvepts, 0, 4); - origLen -= (dash[idx] - phase); + len -= leftInThisDashSegment; // Advance to next dash segment idx = (idx + 1) % dash.length; dashOn = !dashOn; @@ -199,15 +217,289 @@ } } + private LengthIterator li = null; - public void close() { - lineTo(sx, sy); - if (firstDashOn) { - output.lineTo(sx1, sy1); + // preconditions: curCurvepts must be an array of length at least 2 * type, + // that contains the curve we want to dash in the first type elements + private void somethingTo(int type) { + if (pointCurve(curCurvepts, type)) { + return; + } + if (li == null) { + li = new LengthIterator(4, 0.0001f); + } + li.initializeIterationOnCurve(curCurvepts, type); + + int curCurveoff = 0; // initially the current curve is at curCurvepts[0...type] + float lastSplitT = 0; + float t = 0; + float leftInThisDashSegment = dash[idx] - phase; + while ((t = li.next(leftInThisDashSegment)) < 1) { + if (t != 0) { + Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT), + curCurvepts, curCurveoff, + curCurvepts, 0, + curCurvepts, type, type); + lastSplitT = t; + goTo(curCurvepts, 2, type); + curCurveoff = type; + } + // Advance to next dash segment + idx = (idx + 1) % dash.length; + dashOn = !dashOn; + phase = 0; + leftInThisDashSegment = dash[idx]; + } + goTo(curCurvepts, curCurveoff+2, type); + phase += li.lastSegLen(); + if (phase >= dash[idx]) { + phase = 0f; + idx = (idx + 1) % dash.length; + dashOn = !dashOn; } } - public void end() { - output.end(); + private static boolean pointCurve(float[] curve, int type) { + for (int i = 2; i < type; i++) { + if (curve[i] != curve[i-2]) { + return false; + } + } + return true; + } + + // Objects of this class are used to iterate through curves. They return + // t values where the left side of the curve has a specified length. + // It does this by subdividing the input curve until a certain error + // condition has been met. A recursive subdivision procedure would + // return as many as 1<<limit curves, but this is an iterator and we + // don't need all the curves all at once, so what we carry out a + // lazy inorder traversal of the recursion tree (meaning we only move + // through the tree when we need the next subdivided curve). This saves + // us a lot of memory because at any one time we only need to store + // limit+1 curves - one for each level of the tree + 1. + // NOTE: the way we do things here is not enough to traverse a general + // tree; however, the trees we are interested in have the property that + // every non leaf node has exactly 2 children + private static class LengthIterator { + private enum Side {LEFT, RIGHT}; + // Holds the curves at various levels of the recursion. The root + // (i.e. the original curve) is at recCurveStack[0] (but then it + // gets subdivided, the left half is put at 1, so most of the time + // only the right half of the original curve is at 0) + private float[][] recCurveStack; + // sides[i] indicates whether the node at level i+1 in the path from + // the root to the current leaf is a left or right child of its parent. + private Side[] sides; + private int curveType; + private final int limit; + private final float ERR; + private final float minTincrement; + // lastT and nextT delimit the current leaf. + private float nextT; + private float lenAtNextT; + private float lastT; + private float lenAtLastT; + private float lenAtLastSplit; + private float lastSegLen; + // the current level in the recursion tree. 0 is the root. limit + // is the deepest possible leaf. + private int recLevel; + private boolean done; + + public LengthIterator(int reclimit, float err) { + this.limit = reclimit; + this.minTincrement = 1f / (1 << limit); + this.ERR = err; + this.recCurveStack = new float[reclimit+1][8]; + this.sides = new Side[reclimit]; + // if any methods are called without first initializing this object on + // a curve, we want it to fail ASAP. + this.nextT = Float.MAX_VALUE; + this.lenAtNextT = Float.MAX_VALUE; + this.lenAtLastSplit = Float.MIN_VALUE; + this.recLevel = Integer.MIN_VALUE; + this.lastSegLen = Float.MAX_VALUE; + this.done = true; + } + + public void initializeIterationOnCurve(float[] pts, int type) { + System.arraycopy(pts, 0, recCurveStack[0], 0, type); + this.curveType = type; + this.recLevel = 0; + this.lastT = 0; + this.lenAtLastT = 0; + this.nextT = 0; + this.lenAtNextT = 0; + goLeft(); // initializes nextT and lenAtNextT properly + this.lenAtLastSplit = 0; + if (recLevel > 0) { + this.sides[0] = Side.LEFT; + this.done = false; + } else { + // the root of the tree is a leaf so we're done. + this.sides[0] = Side.RIGHT; + this.done = true; + } + this.lastSegLen = 0; + } + + // returns the t value where the remaining curve should be split in + // order for the left subdivided curve to have length len. If len + // is >= than the length of the uniterated curve, it returns 1. + public float next(float len) { + float targetLength = lenAtLastSplit + len; + while(lenAtNextT < targetLength) { + if (done) { + lastSegLen = lenAtNextT - lenAtLastSplit; + return 1; + } + goToNextLeaf(); + } + lenAtLastSplit = targetLength; + float t = binSearchForLen(lenAtLastSplit - lenAtLastT, + recCurveStack[recLevel], curveType, lenAtNextT - lenAtLastT, ERR); + // t is relative to the current leaf, so we must make it a valid parameter + // of the original curve. + t = t * (nextT - lastT) + lastT; + if (t >= 1) { + t = 1; + done = true; + } + // even if done = true, if we're here, that means targetLength + // is equal to, or very, very close to the total length of the + // curve, so lastSegLen won't be too high. In cases where len + // overshoots the curve, this method will exit in the while + // loop, and lastSegLen will still be set to the right value. + lastSegLen = len; + return t; + } + + public float lastSegLen() { + return lastSegLen; + } + + // Returns t such that if leaf is subdivided at t the left + // curve will have length len. leafLen must be the length of leaf. + private static Curve bsc = new Curve(); + private static float binSearchForLen(float len, float[] leaf, int type, + float leafLen, float err) + { + assert len <= leafLen; + bsc.set(leaf, type); + float errBound = err*len; + float left = 0, right = 1; + while (left < right) { + float m = (left + right) / 2; + if (m == left || m == right) { + return m; + } + float x = bsc.xat(m); + float y = bsc.yat(m); + float leftLen = Helpers.linelen(leaf[0], leaf[1], x, y); + if (Math.abs(leftLen - len) < errBound) { + return m; + } + if (leftLen < len) { + left = m; + } else { + right = m; + } + } + return left; + } + + // go to the next leaf (in an inorder traversal) in the recursion tree + // preconditions: must be on a leaf, and that leaf must not be the root. + private void goToNextLeaf() { + // We must go to the first ancestor node that has an unvisited + // right child. + recLevel--; + while(sides[recLevel] == Side.RIGHT) { + if (recLevel == 0) { + done = true; + return; + } + recLevel--; + } + + sides[recLevel] = Side.RIGHT; + System.arraycopy(recCurveStack[recLevel], 0, recCurveStack[recLevel+1], 0, curveType); + recLevel++; + goLeft(); + } + + // go to the leftmost node from the current node. Return its length. + private void goLeft() { + float len = onLeaf(); + if (len >= 0) { + lastT = nextT; + lenAtLastT = lenAtNextT; + nextT += (1 << (limit - recLevel)) * minTincrement; + lenAtNextT += len; + } else { + Helpers.subdivide(recCurveStack[recLevel], 0, + recCurveStack[recLevel+1], 0, + recCurveStack[recLevel], 0, curveType); + sides[recLevel] = Side.LEFT; + recLevel++; + goLeft(); + } + } + + // this is a bit of a hack. It returns -1 if we're not on a leaf, and + // the length of the leaf if we are on a leaf. + private float onLeaf() { + float polylen = Helpers.polyLineLength(recCurveStack[recLevel], 0, curveType); + float linelen = Helpers.linelen(recCurveStack[recLevel][0], recCurveStack[recLevel][1], + recCurveStack[recLevel][curveType - 2], recCurveStack[recLevel][curveType - 1]); + return (polylen - linelen < ERR || recLevel == limit) ? + (polylen + linelen)/2 : -1; + } + } + + @Override + public void curveTo(float x1, float y1, + float x2, float y2, + float x3, float y3) + { + curCurvepts[0] = x0; curCurvepts[1] = y0; + curCurvepts[2] = x1; curCurvepts[3] = y1; + curCurvepts[4] = x2; curCurvepts[5] = y2; + curCurvepts[6] = x3; curCurvepts[7] = y3; + somethingTo(8); + } + + @Override + public void quadTo(float x1, float y1, float x2, float y2) { + curCurvepts[0] = x0; curCurvepts[1] = y0; + curCurvepts[2] = x1; curCurvepts[3] = y1; + curCurvepts[4] = x2; curCurvepts[5] = y2; + somethingTo(6); + } + + public void closePath() { + lineTo(sx, sy); + if (firstSegidx > 0) { + if (!dashOn || needsMoveTo) { + out.moveTo(sx, sy); + } + emitFirstSegments(); + } + moveTo(sx, sy); + } + + public void pathDone() { + if (firstSegidx > 0) { + out.moveTo(sx, sy); + emitFirstSegments(); + } + out.pathDone(); + } + + @Override + public long getNativeConsumer() { + throw new InternalError("Dasher does not use a native consumer"); } } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/java2d/pisces/Helpers.java Fri Nov 12 10:49:52 2010 -0800 @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2007, 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 sun.java2d.pisces; + +import java.util.Arrays; + +final class Helpers { + private Helpers() { + throw new Error("This is a non instantiable class"); + } + + static boolean within(final float x, final float y, final float err) { + final float d = y - x; + return (d <= err && d >= -err); + } + + static boolean within(final double x, final double y, final double err) { + final double d = y - x; + return (d <= err && d >= -err); + } + + static int quadraticRoots(final float a, final float b, + final float c, float[] zeroes, final int off) + { + int ret = off; + float t; + if (a != 0f) { + final float dis = b*b - 4*a*c; + if (dis > 0) { + final float sqrtDis = (float)Math.sqrt(dis); + // depending on the sign of b we use a slightly different + // algorithm than the traditional one to find one of the roots + // so we can avoid adding numbers of different signs (which + // might result in loss of precision). + if (b >= 0) { + zeroes[ret++] = (2 * c) / (-b - sqrtDis); + zeroes[ret++] = (-b - sqrtDis) / (2 * a); + } else { + zeroes[ret++] = (-b + sqrtDis) / (2 * a); + zeroes[ret++] = (2 * c) / (-b + sqrtDis); + } + } else if (dis == 0f) { + t = (-b) / (2 * a); + zeroes[ret++] = t; + } + } else { + if (b != 0f) { + t = (-c) / b; + zeroes[ret++] = t; + } + } + return ret - off; + } + + // find the roots of g(t) = a*t^3 + b*t^2 + c*t + d in [A,B) + // We will not use Cardano's method, since it is complicated and + // involves too many square and cubic roots. We will use Newton's method. + // TODO: this should probably return ALL roots. Then the user can do + // his own filtering of roots outside [A,B). + static int cubicRootsInAB(final float a, final float b, + final float c, final float d, + float[] pts, final int off, final float E, + final float A, final float B) + { + if (a == 0) { + return quadraticRoots(b, c, d, pts, off); + } + // the coefficients of g'(t). no dc variable because dc=c + // we use these to get the critical points of g(t), which + // we then use to chose starting points for Newton's method. These + // should be very close to the actual roots. + final float da = 3 * a; + final float db = 2 * b; + int numCritPts = quadraticRoots(da, db, c, pts, off+1); + numCritPts = filterOutNotInAB(pts, off+1, numCritPts, A, B) - off - 1; + // need them sorted. + if (numCritPts == 2 && pts[off+1] > pts[off+2]) { + float tmp = pts[off+1]; + pts[off+1] = pts[off+2]; + pts[off+2] = tmp; + } + + int ret = off; + + // we don't actually care much about the extrema themselves. We + // only use them to ensure that g(t) is monotonic in each + // interval [pts[i],pts[i+1] (for i in off...off+numCritPts+1). + // This will allow us to determine intervals containing exactly + // one root. + // The end points of the interval are always local extrema. + pts[off] = A; + pts[off + numCritPts + 1] = B; + numCritPts += 2; + + float x0 = pts[off], fx0 = evalCubic(a, b, c, d, x0); + for (int i = off; i < off + numCritPts - 1; i++) { + float x1 = pts[i+1], fx1 = evalCubic(a, b, c, d, x1); + if (fx0 == 0f) { + pts[ret++] = x0; + } else if (fx1 * fx0 < 0f) { // have opposite signs + pts[ret++] = CubicNewton(a, b, c, d, + x0 + fx0 * (x1 - x0) / (fx0 - fx1), E); + } + x0 = x1; + fx0 = fx1; + } + return ret - off; + } + + // precondition: the polynomial to be evaluated must not be 0 at x0. + static float CubicNewton(final float a, final float b, + final float c, final float d, + float x0, final float err) + { + // considering how this function is used, 10 should be more than enough + final int itlimit = 10; + float fx0 = evalCubic(a, b, c, d, x0); + float x1; + int count = 0; + while(true) { + x1 = x0 - (fx0 / evalCubic(0, 3 * a, 2 * b, c, x0)); + if (Math.abs(x1 - x0) < err * Math.abs(x1 + x0) || count == itlimit) { + break; + } + x0 = x1; + fx0 = evalCubic(a, b, c, d, x0); + count++; + } + return x1; + } + + // fills the input array with numbers 0, INC, 2*INC, ... + static void fillWithIdxes(final float[] data, final int[] idxes) { + if (idxes.length > 0) { + idxes[0] = 0; + for (int i = 1; i < idxes.length; i++) { + idxes[i] = idxes[i-1] + (int)data[idxes[i-1]]; + } + } + } + + static void fillWithIdxes(final int[] idxes, final int inc) { + if (idxes.length > 0) { + idxes[0] = 0; + for (int i = 1; i < idxes.length; i++) { + idxes[i] = idxes[i-1] + inc; + } + } + } + + // These use a hardcoded factor of 2 for increasing sizes. Perhaps this + // should be provided as an argument. + static float[] widenArray(float[] in, final int cursize, final int numToAdd) { + if (in == null) { + return new float[5 * numToAdd]; + } + if (in.length >= cursize + numToAdd) { + return in; + } + return Arrays.copyOf(in, 2 * (cursize + numToAdd)); + } + static int[] widenArray(int[] in, final int cursize, final int numToAdd) { + if (in.length >= cursize + numToAdd) { + return in; + } + return Arrays.copyOf(in, 2 * (cursize + numToAdd)); + } + + static float evalCubic(final float a, final float b, + final float c, final float d, + final float t) + { + return t * (t * (t * a + b) + c) + d; + } + + static float evalQuad(final float a, final float b, + final float c, final float t) + { + return t * (t * a + b) + c; + } + + // returns the index 1 past the last valid element remaining after filtering + static int filterOutNotInAB(float[] nums, final int off, final int len, + final float a, final float b) + { + int ret = off; + for (int i = off; i < off + len; i++) { + if (nums[i] > a && nums[i] < b) { + nums[ret++] = nums[i]; + } + } + return ret; + } + + static float polyLineLength(float[] poly, final int off, final int nCoords) { + assert nCoords % 2 == 0 && poly.length >= off + nCoords : ""; + float acc = 0; + for (int i = off + 2; i < off + nCoords; i += 2) { + acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]); + } + return acc; + } + + static float linelen(float x1, float y1, float x2, float y2) { + return (float)Math.hypot(x2 - x1, y2 - y1); + } + + static void subdivide(float[] src, int srcoff, float[] left, int leftoff, + float[] right, int rightoff, int type) + { + switch(type) { + case 6: + Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff); + break; + case 8: + Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff); + break; + default: + throw new InternalError("Unsupported curve type"); + } + } + + static void isort(float[] a, int off, int len) { + for (int i = off + 1; i < off + len; i++) { + float ai = a[i]; + int j = i - 1; + for (; j >= off && a[j] > ai; j--) { + a[j+1] = a[j]; + } + a[j+1] = ai; + } + } + + // Most of these are copied from classes in java.awt.geom because we need + // float versions of these functions, and Line2D, CubicCurve2D, + // QuadCurve2D don't provide them. + /** + * Subdivides the cubic curve specified by the coordinates + * stored in the <code>src</code> array at indices <code>srcoff</code> + * through (<code>srcoff</code> + 7) and stores the + * resulting two subdivided curves into the two result arrays at the + * corresponding indices. + * Either or both of the <code>left</code> and <code>right</code> + * arrays may be <code>null</code> or a reference to the same array + * as the <code>src</code> array. + * Note that the last point in the first subdivided curve is the + * same as the first point in the second subdivided curve. Thus, + * it is possible to pass the same array for <code>left</code> + * and <code>right</code> and to use offsets, such as <code>rightoff</code> + * equals (<code>leftoff</code> + 6), in order + * to avoid allocating extra storage for this common point. + * @param src the array holding the coordinates for the source curve + * @param srcoff the offset into the array of the beginning of the + * the 6 source coordinates + * @param left the array for storing the coordinates for the first + * half of the subdivided curve + * @param leftoff the offset into the array of the beginning of the + * the 6 left coordinates + * @param right the array for storing the coordinates for the second + * half of the subdivided curve + * @param rightoff the offset into the array of the beginning of the + * the 6 right coordinates + * @since 1.7 + */ + static void subdivideCubic(float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx1 = src[srcoff + 2]; + float ctrly1 = src[srcoff + 3]; + float ctrlx2 = src[srcoff + 4]; + float ctrly2 = src[srcoff + 5]; + float x2 = src[srcoff + 6]; + float y2 = src[srcoff + 7]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 6] = x2; + right[rightoff + 7] = y2; + } + x1 = (x1 + ctrlx1) / 2.0f; + y1 = (y1 + ctrly1) / 2.0f; + x2 = (x2 + ctrlx2) / 2.0f; + y2 = (y2 + ctrly2) / 2.0f; + float centerx = (ctrlx1 + ctrlx2) / 2.0f; + float centery = (ctrly1 + ctrly2) / 2.0f; + ctrlx1 = (x1 + centerx) / 2.0f; + ctrly1 = (y1 + centery) / 2.0f; + ctrlx2 = (x2 + centerx) / 2.0f; + ctrly2 = (y2 + centery) / 2.0f; + centerx = (ctrlx1 + ctrlx2) / 2.0f; + centery = (ctrly1 + ctrly2) / 2.0f; + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx1; + left[leftoff + 5] = ctrly1; + left[leftoff + 6] = centerx; + left[leftoff + 7] = centery; + } + if (right != null) { + right[rightoff + 0] = centerx; + right[rightoff + 1] = centery; + right[rightoff + 2] = ctrlx2; + right[rightoff + 3] = ctrly2; + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + } + + + static void subdivideCubicAt(float t, float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx1 = src[srcoff + 2]; + float ctrly1 = src[srcoff + 3]; + float ctrlx2 = src[srcoff + 4]; + float ctrly2 = src[srcoff + 5]; + float x2 = src[srcoff + 6]; + float y2 = src[srcoff + 7]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 6] = x2; + right[rightoff + 7] = y2; + } + x1 = x1 + t * (ctrlx1 - x1); + y1 = y1 + t * (ctrly1 - y1); + x2 = ctrlx2 + t * (x2 - ctrlx2); + y2 = ctrly2 + t * (y2 - ctrly2); + float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); + float centery = ctrly1 + t * (ctrly2 - ctrly1); + ctrlx1 = x1 + t * (centerx - x1); + ctrly1 = y1 + t * (centery - y1); + ctrlx2 = centerx + t * (x2 - centerx); + ctrly2 = centery + t * (y2 - centery); + centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); + centery = ctrly1 + t * (ctrly2 - ctrly1); + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx1; + left[leftoff + 5] = ctrly1; + left[leftoff + 6] = centerx; + left[leftoff + 7] = centery; + } + if (right != null) { + right[rightoff + 0] = centerx; + right[rightoff + 1] = centery; + right[rightoff + 2] = ctrlx2; + right[rightoff + 3] = ctrly2; + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + } + + static void subdivideQuad(float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx = src[srcoff + 2]; + float ctrly = src[srcoff + 3]; + float x2 = src[srcoff + 4]; + float y2 = src[srcoff + 5]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + x1 = (x1 + ctrlx) / 2.0f; + y1 = (y1 + ctrly) / 2.0f; + x2 = (x2 + ctrlx) / 2.0f; + y2 = (y2 + ctrly) / 2.0f; + ctrlx = (x1 + x2) / 2.0f; + ctrly = (y1 + y2) / 2.0f; + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx; + left[leftoff + 5] = ctrly; + } + if (right != null) { + right[rightoff + 0] = ctrlx; + right[rightoff + 1] = ctrly; + right[rightoff + 2] = x2; + right[rightoff + 3] = y2; + } + } + + static void subdivideQuadAt(float t, float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx = src[srcoff + 2]; + float ctrly = src[srcoff + 3]; + float x2 = src[srcoff + 4]; + float y2 = src[srcoff + 5]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + x1 = x1 + t * (ctrlx - x1); + y1 = y1 + t * (ctrly - y1); + x2 = ctrlx + t * (x2 - ctrlx); + y2 = ctrly + t * (y2 - ctrly); + ctrlx = x1 + t * (x2 - x1); + ctrly = y1 + t * (y2 - y1); + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx; + left[leftoff + 5] = ctrly; + } + if (right != null) { + right[rightoff + 0] = ctrlx; + right[rightoff + 1] = ctrly; + right[rightoff + 2] = x2; + right[rightoff + 3] = y2; + } + } + + static void subdivideAt(float t, float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff, int size) + { + switch(size) { + case 8: + subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff); + break; + case 6: + subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff); + break; + } + } +}
--- a/src/share/classes/sun/java2d/pisces/LineSink.java Wed Nov 03 14:12:45 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2007, 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 sun.java2d.pisces; - -/** - * The <code>LineSink</code> interface accepts a series of line - * drawing commands: <code>moveTo</code>, <code>lineTo</code>, - * <code>close</code> (equivalent to a <code>lineTo</code> command - * with an argument equal to the argument of the last - * <code>moveTo</code> command), and <code>end</code>. - * - * <p> A <code>Flattener</code> may be used to connect a general path - * source to a <code>LineSink</code>. - * - * <p> The <code>Renderer</code> class implements the - * <code>LineSink</code> interface. - * - */ -public interface LineSink { - - /** - * Moves the current drawing position to the point <code>(x0, - * y0)</code>. - * - * @param x0 the X coordinate - * @param y0 the Y coordinate - */ - public void moveTo(float x0, float y0); - - /** - * Provides a hint that the current segment should be joined to - * the following segment using an explicit miter or round join if - * required. - * - * <p> An application-generated path will generally have no need - * to contain calls to this method; they are typically introduced - * by a <code>Flattener</code> to mark segment divisions that - * appear in its input, and consumed by a <code>Stroker</code> - * that is responsible for emitting the miter or round join - * segments. - * - * <p> Other <code>LineSink</code> classes should simply pass this - * hint to their output sink as needed. - */ - public void lineJoin(); - - /** - * Draws a line from the current drawing position to the point - * <code>(x1, y1)</code> and sets the current drawing position to - * <code>(x1, y1)</code>. - * - * @param x1 the X coordinate - * @param y1 the Y coordinate - */ - public void lineTo(float x1, float y1); - - /** - * Closes the current path by drawing a line from the current - * drawing position to the point specified by the moset recent - * <code>moveTo</code> command. - */ - public void close(); - - /** - * Ends the current path. It may be necessary to end a path in - * order to allow end caps to be drawn. - */ - public void end(); - -}
--- a/src/share/classes/sun/java2d/pisces/PiscesCache.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/java2d/pisces/PiscesCache.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,6 +25,8 @@ package sun.java2d.pisces; +import java.util.Arrays; + /** * An object used to cache pre-rendered complex paths. * @@ -32,115 +34,153 @@ */ public final class PiscesCache { - int bboxX0, bboxY0, bboxX1, bboxY1; + final int bboxX0, bboxY0, bboxX1, bboxY1; - byte[] rowAARLE; - int alphaRLELength; + // rowAARLE[i] holds the encoding of the pixel row with y = bboxY0+i. + // The format of each of the inner arrays is: rowAARLE[i][0,1] = (x0, n) + // where x0 is the first x in row i with nonzero alpha, and n is the + // number of RLE entries in this row. rowAARLE[i][j,j+1] for j>1 is + // (val,runlen) + final int[][] rowAARLE; - int[] rowOffsetsRLE; - int[] minTouched; - int alphaRows; + // RLE encodings are added in increasing y rows and then in increasing + // x inside those rows. Therefore, at any one time there is a well + // defined position (x,y) where a run length is about to be added (or + // the row terminated). x0,y0 is this (x,y)-(bboxX0,bboxY0). They + // are used to get indices into the current tile. + private int x0 = Integer.MIN_VALUE, y0 = Integer.MIN_VALUE; - private PiscesCache() {} + // touchedTile[i][j] is the sum of all the alphas in the tile with + // y=i*TILE_SIZE+bboxY0 and x=j*TILE_SIZE+bboxX0. + private final int[][] touchedTile; - public static PiscesCache createInstance() { - return new PiscesCache(); + static final int TILE_SIZE_LG = 5; + static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32 + private static final int INIT_ROW_SIZE = 8; // enough for 3 run lengths + + PiscesCache(int minx, int miny, int maxx, int maxy) { + assert maxy >= miny && maxx >= minx; + bboxX0 = minx; + bboxY0 = miny; + bboxX1 = maxx + 1; + bboxY1 = maxy + 1; + // we could just leave the inner arrays as null and allocate them + // lazily (which would be beneficial for shapes with gaps), but we + // assume there won't be too many of those so we allocate everything + // up front (which is better for other cases) + rowAARLE = new int[bboxY1 - bboxY0 + 1][INIT_ROW_SIZE]; + x0 = 0; + y0 = -1; // -1 makes the first assert in startRow succeed + // the ceiling of (maxy - miny + 1) / TILE_SIZE; + int nyTiles = (maxy - miny + TILE_SIZE) >> TILE_SIZE_LG; + int nxTiles = (maxx - minx + TILE_SIZE) >> TILE_SIZE_LG; + + touchedTile = new int[nyTiles][nxTiles]; } - private static final float ROWAA_RLE_FACTOR = 1.5f; - private static final float TOUCHED_FACTOR = 1.5f; - private static final int MIN_TOUCHED_LEN = 64; - - private void reallocRowAARLE(int newLength) { - if (rowAARLE == null) { - rowAARLE = new byte[newLength]; - } else if (rowAARLE.length < newLength) { - int len = Math.max(newLength, - (int)(rowAARLE.length*ROWAA_RLE_FACTOR)); - byte[] newRowAARLE = new byte[len]; - System.arraycopy(rowAARLE, 0, newRowAARLE, 0, rowAARLE.length); - rowAARLE = newRowAARLE; + void addRLERun(int val, int runLen) { + if (runLen > 0) { + addTupleToRow(y0, val, runLen); + if (val != 0) { + // the x and y of the current row, minus bboxX0, bboxY0 + int tx = x0 >> TILE_SIZE_LG; + int ty = y0 >> TILE_SIZE_LG; + int tx1 = (x0 + runLen - 1) >> TILE_SIZE_LG; + // while we forbid rows from starting before bboxx0, our users + // can still store rows that go beyond bboxx1 (although this + // shouldn't happen), so it's a good idea to check that i + // is not going out of bounds in touchedTile[ty] + if (tx1 >= touchedTile[ty].length) { + tx1 = touchedTile[ty].length - 1; + } + if (tx <= tx1) { + int nextTileXCoord = (tx + 1) << TILE_SIZE_LG; + if (nextTileXCoord > x0+runLen) { + touchedTile[ty][tx] += val * runLen; + } else { + touchedTile[ty][tx] += val * (nextTileXCoord - x0); + } + tx++; + } + // don't go all the way to tx1 - we need to handle the last + // tile as a special case (just like we did with the first + for (; tx < tx1; tx++) { +// try { + touchedTile[ty][tx] += (val << TILE_SIZE_LG); +// } catch (RuntimeException e) { +// System.out.println("x0, y0: " + x0 + ", " + y0); +// System.out.printf("tx, ty, tx1: %d, %d, %d %n", tx, ty, tx1); +// System.out.printf("bboxX/Y0/1: %d, %d, %d, %d %n", +// bboxX0, bboxY0, bboxX1, bboxY1); +// throw e; +// } + } + // they will be equal unless x0>>TILE_SIZE_LG == tx1 + if (tx == tx1) { + int lastXCoord = Math.min(x0 + runLen, (tx + 1) << TILE_SIZE_LG); + int txXCoord = tx << TILE_SIZE_LG; + touchedTile[ty][tx] += val * (lastXCoord - txXCoord); + } + } + x0 += runLen; } } - private void reallocRowInfo(int newHeight) { - if (minTouched == null) { - int len = Math.max(newHeight, MIN_TOUCHED_LEN); - minTouched = new int[len]; - rowOffsetsRLE = new int[len]; - } else if (minTouched.length < newHeight) { - int len = Math.max(newHeight, - (int)(minTouched.length*TOUCHED_FACTOR)); - int[] newMinTouched = new int[len]; - int[] newRowOffsetsRLE = new int[len]; - System.arraycopy(minTouched, 0, newMinTouched, 0, - alphaRows); - System.arraycopy(rowOffsetsRLE, 0, newRowOffsetsRLE, 0, - alphaRows); - minTouched = newMinTouched; - rowOffsetsRLE = newRowOffsetsRLE; - } + void startRow(int y, int x) { + // rows are supposed to be added by increasing y. + assert y - bboxY0 > y0; + assert y <= bboxY1; // perhaps this should be < instead of <= + + y0 = y - bboxY0; + // this should be a new, uninitialized row. + assert rowAARLE[y0][1] == 0; + + x0 = x - bboxX0; + assert x0 >= 0 : "Input must not be to the left of bbox bounds"; + + // the way addTupleToRow is implemented it would work for this but it's + // not a good idea to use it because it is meant for adding + // RLE tuples, not the first tuple (which is special). + rowAARLE[y0][0] = x; + rowAARLE[y0][1] = 2; } - void addRLERun(byte val, int runLen) { - reallocRowAARLE(alphaRLELength + 2); - rowAARLE[alphaRLELength++] = val; - rowAARLE[alphaRLELength++] = (byte)runLen; + int alphaSumInTile(int x, int y) { + x -= bboxX0; + y -= bboxY0; + return touchedTile[y>>TILE_SIZE_LG][x>>TILE_SIZE_LG]; } - void startRow(int y, int x0, int x1) { - if (alphaRows == 0) { - bboxY0 = y; - bboxY1 = y+1; - bboxX0 = x0; - bboxX1 = x1+1; - } else { - if (bboxX0 > x0) bboxX0 = x0; - if (bboxX1 < x1 + 1) bboxX1 = x1 + 1; - while (bboxY1++ < y) { - reallocRowInfo(alphaRows+1); - minTouched[alphaRows] = 0; - // Assuming last 2 entries in rowAARLE are 0,0 - rowOffsetsRLE[alphaRows] = alphaRLELength-2; - alphaRows++; + int minTouched(int rowidx) { + return rowAARLE[rowidx][0]; + } + + int rowLength(int rowidx) { + return rowAARLE[rowidx][1]; + } + + private void addTupleToRow(int row, int a, int b) { + int end = rowAARLE[row][1]; + rowAARLE[row] = Helpers.widenArray(rowAARLE[row], end, 2); + rowAARLE[row][end++] = a; + rowAARLE[row][end++] = b; + rowAARLE[row][1] = end; + } + + @Override + public String toString() { + String ret = "bbox = ["+ + bboxX0+", "+bboxY0+" => "+ + bboxX1+", "+bboxY1+"]\n"; + for (int[] row : rowAARLE) { + if (row != null) { + ret += ("minTouchedX=" + row[0] + + "\tRLE Entries: " + Arrays.toString( + Arrays.copyOfRange(row, 2, row[1])) + "\n"); + } else { + ret += "[]\n"; } } - reallocRowInfo(alphaRows+1); - minTouched[alphaRows] = x0; - rowOffsetsRLE[alphaRows] = alphaRLELength; - alphaRows++; - } - - public synchronized void dispose() { - rowAARLE = null; - alphaRLELength = 0; - - minTouched = null; - rowOffsetsRLE = null; - alphaRows = 0; - - bboxX0 = bboxY0 = bboxX1 = bboxY1 = 0; - } - - public void print(java.io.PrintStream out) { - synchronized (out) { - out.println("bbox = ["+ - bboxX0+", "+bboxY0+" => "+ - bboxX1+", "+bboxY1+"]"); - - out.println("alphRLELength = "+alphaRLELength); - - for (int y = bboxY0; y < bboxY1; y++) { - int i = y-bboxY0; - out.println("row["+i+"] == {"+ - "minX = "+minTouched[i]+ - ", off = "+rowOffsetsRLE[i]+"}"); - } - - for (int i = 0; i < alphaRLELength; i += 2) { - out.println("rle["+i+"] = "+ - (rowAARLE[i+1]&0xff)+" of "+(rowAARLE[i]&0xff)); - } - } + return ret; } }
--- a/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Fri Nov 12 10:49:52 2010 -0800 @@ -27,7 +27,7 @@ import java.awt.Shape; import java.awt.BasicStroke; -import java.awt.geom.FlatteningPathIterator; +import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Path2D; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; @@ -38,8 +38,6 @@ import sun.java2d.pipe.AATileGenerator; public class PiscesRenderingEngine extends RenderingEngine { - public static double defaultFlat = 0.1; - private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA} /** @@ -78,20 +76,29 @@ miterlimit, dashes, dashphase, - new LineSink() { + new PathConsumer2D() { public void moveTo(float x0, float y0) { p2d.moveTo(x0, y0); } - public void lineJoin() {} public void lineTo(float x1, float y1) { p2d.lineTo(x1, y1); } - public void close() { + public void closePath() { p2d.closePath(); } - public void end() {} + public void pathDone() {} + public void curveTo(float x1, float y1, + float x2, float y2, + float x3, float y3) { + p2d.curveTo(x1, y1, x2, y2, x3, y3); + } + public void quadTo(float x1, float y1, float x2, float y2) { + p2d.quadTo(x1, y1, x2, y2); + } + public long getNativeConsumer() { + throw new InternalError("Not using a native peer"); + } }); - return p2d; } @@ -133,22 +140,7 @@ NormMode norm = (normalize) ? ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) : NormMode.OFF; - strokeTo(src, at, bs, thin, norm, antialias, - new LineSink() { - public void moveTo(float x0, float y0) { - consumer.moveTo(x0, y0); - } - public void lineJoin() {} - public void lineTo(float x1, float y1) { - consumer.lineTo(x1, y1); - } - public void close() { - consumer.closePath(); - } - public void end() { - consumer.pathDone(); - } - }); + strokeTo(src, at, bs, thin, norm, antialias, consumer); } void strokeTo(Shape src, @@ -157,7 +149,7 @@ boolean thin, NormMode normalize, boolean antialias, - LineSink lsink) + PathConsumer2D pc2d) { float lw; if (thin) { @@ -178,7 +170,7 @@ bs.getMiterLimit(), bs.getDashArray(), bs.getDashPhase(), - lsink); + pc2d); } private float userSpaceLineWidth(AffineTransform at, float lw) { @@ -256,28 +248,113 @@ float miterlimit, float dashes[], float dashphase, - LineSink lsink) + PathConsumer2D pc2d) { - float a00 = 1f, a01 = 0f, a10 = 0f, a11 = 1f; + // We use inat and outat so that in Stroker and Dasher we can work only + // with the pre-transformation coordinates. This will repeat a lot of + // computations done in the path iterator, but the alternative is to + // work with transformed paths and compute untransformed coordinates + // as needed. This would be faster but I do not think the complexity + // of working with both untransformed and transformed coordinates in + // the same code is worth it. + // However, if a path's width is constant after a transformation, + // we can skip all this untransforming. + + // If normalization is off we save some transformations by not + // transforming the input to pisces. Instead, we apply the + // transformation after the path processing has been done. + // We can't do this if normalization is on, because it isn't a good + // idea to normalize before the transformation is applied. + AffineTransform inat = null; + AffineTransform outat = null; + + PathIterator pi = null; + if (at != null && !at.isIdentity()) { - a00 = (float)at.getScaleX(); - a01 = (float)at.getShearX(); - a10 = (float)at.getShearY(); - a11 = (float)at.getScaleY(); + final double a = at.getScaleX(); + final double b = at.getShearX(); + final double c = at.getShearY(); + final double d = at.getScaleY(); + final double det = a * d - c * b; + if (Math.abs(det) <= 2 * Float.MIN_VALUE) { + // this rendering engine takes one dimensional curves and turns + // them into 2D shapes by giving them width. + // However, if everything is to be passed through a singular + // transformation, these 2D shapes will be squashed down to 1D + // again so, nothing can be drawn. + + // Every path needs an initial moveTo and a pathDone. If these + // aren't there this causes a SIGSEV in libawt.so (at the time + // of writing of this comment (September 16, 2010)). Actually, + // I'm not sure if the moveTo is necessary to avoid the SIGSEV + // but the pathDone is definitely needed. + pc2d.moveTo(0, 0); + pc2d.pathDone(); + return; + } + + // If the transform is a constant multiple of an orthogonal transformation + // then every length is just multiplied by a constant, so we just + // need to transform input paths to stroker and tell stroker + // the scaled width. This condition is satisfied if + // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we + // leave a bit of room for error. + if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) { + double scale = Math.sqrt(a*a + c*c); + if (dashes != null) { + dashes = java.util.Arrays.copyOf(dashes, dashes.length); + for (int i = 0; i < dashes.length; i++) { + dashes[i] = (float)(scale * dashes[i]); + } + dashphase = (float)(scale * dashphase); + } + width = (float)(scale * width); + pi = src.getPathIterator(at); + if (normalize != NormMode.OFF) { + pi = new NormalizingPathIterator(pi, normalize); + } + // leave inat and outat null. + } else { + // We only need the inverse if normalization is on. Otherwise + // we just don't transform the input paths, do all the stroking + // and then transform out output (instead of making PathIterator + // apply the transformation, us applying the inverse, and then + // us applying the transform again to our output). + outat = at; + if (normalize != NormMode.OFF) { + try { + inat = outat.createInverse(); + } catch (NoninvertibleTransformException e) { + // we made sure this can't happen + e.printStackTrace(); + } + pi = src.getPathIterator(at); + pi = new NormalizingPathIterator(pi, normalize); + } else { + pi = src.getPathIterator(null); + } + } + } else { + // either at is null or it's the identity. In either case + // we don't transform the path. + pi = src.getPathIterator(null); + if (normalize != NormMode.OFF) { + pi = new NormalizingPathIterator(pi, normalize); + } } - lsink = new Stroker(lsink, width, caps, join, miterlimit, a00, a01, a10, a11); + + pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat); + pc2d = new Stroker(pc2d, width, caps, join, miterlimit); if (dashes != null) { - lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11); + pc2d = new Dasher(pc2d, dashes, dashphase); } - PathIterator pi; - if (normalize != NormMode.OFF) { - pi = new FlatteningPathIterator( - new NormalizingPathIterator(src.getPathIterator(at), normalize), - defaultFlat); - } else { - pi = src.getPathIterator(at, defaultFlat); - } - pathTo(pi, lsink); + pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, inat); + + pathTo(pi, pc2d); + } + + private static boolean nearZero(double num, int nulps) { + return Math.abs(num) < nulps * Math.ulp(num); } private static class NormalizingPathIterator implements PathIterator { @@ -337,10 +414,10 @@ } // normalize endpoint - float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + rval - - coords[lastCoord]; - float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + rval - - coords[lastCoord + 1]; + float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + + rval - coords[lastCoord]; + float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + + rval - coords[lastCoord + 1]; coords[lastCoord ] += x_adjust; coords[lastCoord + 1] += y_adjust; @@ -393,27 +470,9 @@ } } - void pathTo(PathIterator pi, LineSink lsink) { - float coords[] = new float[2]; - while (!pi.isDone()) { - switch (pi.currentSegment(coords)) { - case PathIterator.SEG_MOVETO: - lsink.moveTo(coords[0], coords[1]); - break; - case PathIterator.SEG_LINETO: - lsink.lineJoin(); - lsink.lineTo(coords[0], coords[1]); - break; - case PathIterator.SEG_CLOSE: - lsink.lineJoin(); - lsink.close(); - break; - default: - throw new InternalError("unknown flattened segment type"); - } - pi.next(); - } - lsink.end(); + static void pathTo(PathIterator pi, PathConsumer2D pc2d) { + RenderingEngine.feedConsumer(pi, pc2d); + pc2d.pathDone(); } /** @@ -471,32 +530,29 @@ boolean normalize, int bbox[]) { - PiscesCache pc = PiscesCache.createInstance(); Renderer r; NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF; if (bs == null) { PathIterator pi; if (normalize) { - pi = new FlatteningPathIterator( - new NormalizingPathIterator(s.getPathIterator(at), norm), - defaultFlat); + pi = new NormalizingPathIterator(s.getPathIterator(at), norm); } else { - pi = s.getPathIterator(at, defaultFlat); + pi = s.getPathIterator(at); } r = new Renderer(3, 3, clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), - pi.getWindingRule(), pc); + pi.getWindingRule()); pathTo(pi, r); } else { r = new Renderer(3, 3, clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), - PathIterator.WIND_NON_ZERO, pc); + PathIterator.WIND_NON_ZERO); strokeTo(s, at, bs, thin, norm, true, r); } r.endRendering(); - PiscesTileGenerator ptg = new PiscesTileGenerator(pc, r.MAX_AA_ALPHA); + PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); ptg.getBbox(bbox); return ptg; }
--- a/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java Fri Nov 12 10:49:52 2010 -0800 @@ -25,40 +25,54 @@ package sun.java2d.pisces; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import sun.java2d.pipe.AATileGenerator; -public class PiscesTileGenerator implements AATileGenerator { - public static final int TILE_SIZE = 32; +public final class PiscesTileGenerator implements AATileGenerator { + public static final int TILE_SIZE = PiscesCache.TILE_SIZE; + + // perhaps we should be using weak references here, but right now + // that's not necessary. The way the renderer is, this map will + // never contain more than one element - the one with key 64, since + // we only do 8x8 supersampling. + private static final Map<Integer, byte[]> alphaMapsCache = new + ConcurrentHashMap<Integer, byte[]>(); PiscesCache cache; int x, y; - int maxalpha; + final int maxalpha; + private final int maxTileAlphaSum; + + // The alpha map used by this object (taken out of our map cache) to convert + // pixel coverage counts gotten from PiscesCache (which are in the range + // [0, maxalpha]) into alpha values, which are in [0,256). byte alphaMap[]; - public PiscesTileGenerator(PiscesCache cache, int maxalpha) { - this.cache = cache; + public PiscesTileGenerator(Renderer r, int maxalpha) { + this.cache = r.getCache(); this.x = cache.bboxX0; this.y = cache.bboxY0; this.alphaMap = getAlphaMap(maxalpha); this.maxalpha = maxalpha; + this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha; } - static int prevMaxAlpha; - static byte prevAlphaMap[]; + private static byte[] buildAlphaMap(int maxalpha) { + byte[] alMap = new byte[maxalpha+1]; + int halfmaxalpha = maxalpha>>2; + for (int i = 0; i <= maxalpha; i++) { + alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); + } + return alMap; + } - public synchronized static byte[] getAlphaMap(int maxalpha) { - if (maxalpha != prevMaxAlpha) { - prevAlphaMap = new byte[maxalpha+300]; - int halfmaxalpha = maxalpha>>2; - for (int i = 0; i <= maxalpha; i++) { - prevAlphaMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); - } - for (int i = maxalpha; i < prevAlphaMap.length; i++) { - prevAlphaMap[i] = (byte) 255; - } - prevMaxAlpha = maxalpha; + public static byte[] getAlphaMap(int maxalpha) { + if (!alphaMapsCache.containsKey(maxalpha)) { + alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha)); } - return prevAlphaMap; + return alphaMapsCache.get(maxalpha); } public void getBbox(int bbox[]) { @@ -96,53 +110,24 @@ * value for partial coverage of the tile */ public int getTypicalAlpha() { - if (true) return 0x80; - // Decode run-length encoded alpha mask data - // The data for row j begins at cache.rowOffsetsRLE[j] - // and is encoded as a set of 2-byte pairs (val, runLen) - // terminated by a (0, 0) pair. - - int x0 = this.x; - int x1 = x0 + TILE_SIZE; - int y0 = this.y; - int y1 = y0 + TILE_SIZE; - if (x1 > cache.bboxX1) x1 = cache.bboxX1; - if (y1 > cache.bboxY1) y1 = cache.bboxY1; - y0 -= cache.bboxY0; - y1 -= cache.bboxY0; - - int ret = -1; - for (int cy = y0; cy < y1; cy++) { - int pos = cache.rowOffsetsRLE[cy]; - int cx = cache.minTouched[cy]; - - if (cx > x0) { - if (ret > 0) return 0x80; - ret = 0x00; - } - while (cx < x1) { - int runLen = cache.rowAARLE[pos + 1] & 0xff; - if (runLen == 0) { - if (ret > 0) return 0x80; - ret = 0x00; - break; - } - cx += runLen; - if (cx > x0) { - int val = cache.rowAARLE[pos] & 0xff; - if (ret != val) { - if (ret < 0) { - if (val != 0x00 && val != maxalpha) return 0x80; - ret = val; - } else { - return 0x80; - } - } - } - pos += 2; - } - } - return ret; + int al = cache.alphaSumInTile(x, y); + // Note: if we have a filled rectangle that doesn't end on a tile + // border, we could still return 0xff, even though al!=maxTileAlphaSum + // This is because if we return 0xff, our users will fill a rectangle + // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x), + // and height min(TILE_SIZE,bboxY1-y), which is what should happen. + // However, to support this, we would have to use 2 Math.min's + // and 2 multiplications per tile, instead of just 2 multiplications + // to compute maxTileAlphaSum. The savings offered would probably + // not be worth it, considering how rare this case is. + // Note: I have not tested this, so in the future if it is determined + // that it is worth it, it should be implemented. Perhaps this method's + // interface should be changed to take arguments the width and height + // of the current tile. This would eliminate the 2 Math.min calls that + // would be needed here, since our caller needs to compute these 2 + // values anyway. + return (al == 0x00 ? 0x00 : + (al == maxTileAlphaSum ? 0xff : 0x80)); } /** @@ -179,22 +164,24 @@ int idx = offset; for (int cy = y0; cy < y1; cy++) { - int pos = cache.rowOffsetsRLE[cy]; - int cx = cache.minTouched[cy]; + int[] row = cache.rowAARLE[cy]; + assert row != null; + int cx = cache.minTouched(cy); if (cx > x1) cx = x1; - if (cx > x0) { - //System.out.println("L["+(cx-x0)+"]"); - for (int i = x0; i < cx; i++) { - tile[idx++] = 0x00; - } + for (int i = x0; i < cx; i++) { + tile[idx++] = 0x00; } - while (cx < x1) { + + int pos = 2; + while (cx < x1 && pos < row[1]) { byte val; int runLen = 0; + assert row[1] > 2; try { - val = alphaMap[cache.rowAARLE[pos] & 0xff]; - runLen = cache.rowAARLE[pos + 1] & 0xff; + val = alphaMap[row[pos]]; + runLen = row[pos + 1]; + assert runLen > 0; } catch (RuntimeException e0) { System.out.println("maxalpha = "+maxalpha); System.out.println("tile["+x0+", "+y0+ @@ -202,14 +189,12 @@ System.out.println("cx = "+cx+", cy = "+cy); System.out.println("idx = "+idx+", pos = "+pos); System.out.println("len = "+runLen); - cache.print(System.out); + System.out.print(cache.toString()); e0.printStackTrace(); System.exit(1); return; } - if (runLen == 0) { - break; - } + int rx0 = cx; cx += runLen; int rx1 = cx; @@ -228,7 +213,7 @@ System.out.println("idx = "+idx+", pos = "+pos); System.out.println("rx0 = "+rx0+", rx1 = "+rx1); System.out.println("len = "+runLen); - cache.print(System.out); + System.out.print(cache.toString()); e.printStackTrace(); System.exit(1); return; @@ -265,4 +250,4 @@ * No further calls will be made on this instance. */ public void dispose() {} -} +} \ No newline at end of file
--- a/src/share/classes/sun/java2d/pisces/Renderer.java Wed Nov 03 14:12:45 2010 -0700 +++ b/src/share/classes/sun/java2d/pisces/Renderer.java Fri Nov 12 10:49:52 2010 -0800 @@ -26,250 +26,552 @@ package sun.java2d.pisces; import java.util.Arrays; +import java.util.Iterator; -public class Renderer implements LineSink { +import sun.awt.geom.PathConsumer2D; -/////////////////////////////////////////////////////////////////////////////// -// Scan line iterator and edge crossing data. -////////////////////////////////////////////////////////////////////////////// +public class Renderer implements PathConsumer2D { - private int[] crossings; + private class ScanlineIterator { - // This is an array of indices into the edge array. It is initialized to - // [i * SIZEOF_STRUCT_EDGE for i in range(0, edgesSize/SIZEOF_STRUCT_EDGE)] - // (where range(i, j) is i,i+1,...,j-1 -- just like in python). - // The reason for keeping this is because we need the edges array sorted - // by y0, but we don't want to move all that data around, so instead we - // sort the indices into the edge array, and use edgeIndices to access - // the edges array. This is meant to simulate a pointer array (hence the name) - private int[] edgePtrs; + private int[] crossings; - // crossing bounds. The bounds are not necessarily tight (the scan line - // at minY, for example, might have no crossings). The x bounds will - // be accumulated as crossings are computed. - private int minY, maxY; - private int minX, maxX; - private int nextY; + // crossing bounds. The bounds are not necessarily tight (the scan line + // at minY, for example, might have no crossings). The x bounds will + // be accumulated as crossings are computed. + private int minY, maxY; + private int nextY; - // indices into the edge pointer list. They indicate the "active" sublist in - // the edge list (the portion of the list that contains all the edges that - // cross the next scan line). - private int lo, hi; + // indices into the segment pointer lists. They indicate the "active" + // sublist in the segment lists (the portion of the list that contains + // all the segments that cross the next scan line). + private int elo, ehi; + private final int[] edgePtrs; + private int qlo, qhi; + private final int[] quadPtrs; + private int clo, chi; + private final int[] curvePtrs; - private static final int INIT_CROSSINGS_SIZE = 50; - private void ScanLineItInitialize() { - crossings = new int[INIT_CROSSINGS_SIZE]; - edgePtrs = new int[edgesSize / SIZEOF_STRUCT_EDGE]; - for (int i = 0; i < edgePtrs.length; i++) { - edgePtrs[i] = i * SIZEOF_STRUCT_EDGE; + private static final int INIT_CROSSINGS_SIZE = 10; + + private ScanlineIterator() { + crossings = new int[INIT_CROSSINGS_SIZE]; + + edgePtrs = new int[numEdges]; + Helpers.fillWithIdxes(edgePtrs, SIZEOF_EDGE); + qsort(edges, edgePtrs, YMIN, 0, numEdges - 1); + + quadPtrs = new int[numQuads]; + Helpers.fillWithIdxes(quadPtrs, SIZEOF_QUAD); + qsort(quads, quadPtrs, YMIN, 0, numQuads - 1); + + curvePtrs = new int[numCurves]; + Helpers.fillWithIdxes(curvePtrs, SIZEOF_CURVE); + qsort(curves, curvePtrs, YMIN, 0, numCurves - 1); + + // We don't care if we clip some of the line off with ceil, since + // no scan line crossings will be eliminated (in fact, the ceil is + // the y of the first scan line crossing). + nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY)); + maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY)); + + for (elo = 0; elo < numEdges && edges[edgePtrs[elo]+YMAX] <= minY; elo++) + ; + // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive) + for (ehi = elo; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= minY; ehi++) + edgeSetCurY(edgePtrs[ehi], minY);// TODO: make minY a float to avoid casts + + for (qlo = 0; qlo < numQuads && quads[quadPtrs[qlo]+YMAX] <= minY; qlo++) + ; + for (qhi = qlo; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= minY; qhi++) + quadSetCurY(quadPtrs[qhi], minY); + + for (clo = 0; clo < numCurves && curves[curvePtrs[clo]+YMAX] <= minY; clo++) + ; + for (chi = clo; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= minY; chi++) + curveSetCurY(curvePtrs[chi], minY); } - qsort(0, edgePtrs.length - 1); + private int next() { + // we go through the active lists and remove segments that don't cross + // the nextY scanline. + int crossingIdx = 0; + for (int i = elo; i < ehi; i++) { + if (edges[edgePtrs[i]+YMAX] <= nextY) { + edgePtrs[i] = edgePtrs[elo++]; + } + } + for (int i = qlo; i < qhi; i++) { + if (quads[quadPtrs[i]+YMAX] <= nextY) { + quadPtrs[i] = quadPtrs[qlo++]; + } + } + for (int i = clo; i < chi; i++) { + if (curves[curvePtrs[i]+YMAX] <= nextY) { + curvePtrs[i] = curvePtrs[clo++]; + } + } - // We don't care if we clip some of the line off with ceil, since - // no scan line crossings will be eliminated (in fact, the ceil is - // the y of the first scan line crossing). - nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY)); - maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY)); + crossings = Helpers.widenArray(crossings, 0, ehi-elo+qhi-qlo+chi-clo); - for (lo = 0; lo < edgePtrs.length && edges[edgePtrs[lo]+Y1] <= nextY; lo++) - ; - for (hi = lo; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) - ; // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive) - for (int i = lo; i < hi; i++) { - setCurY(edgePtrs[i], nextY); + // Now every edge between lo and hi crosses nextY. Compute it's + // crossing and put it in the crossings array. + for (int i = elo; i < ehi; i++) { + int ptr = edgePtrs[i]; + addCrossing(nextY, (int)edges[ptr+CURX], edges[ptr+OR], crossingIdx); + edgeGoToNextY(ptr); + crossingIdx++; + } + for (int i = qlo; i < qhi; i++) { + int ptr = quadPtrs[i]; + addCrossing(nextY, (int)quads[ptr+CURX], quads[ptr+OR], crossingIdx); + quadGoToNextY(ptr); + crossingIdx++; + } + for (int i = clo; i < chi; i++) { + int ptr = curvePtrs[i]; + addCrossing(nextY, (int)curves[ptr+CURX], curves[ptr+OR], crossingIdx); + curveGoToNextY(ptr); + crossingIdx++; + } + + nextY++; + // Expand active lists to include new edges. + for (; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= nextY; ehi++) { + edgeSetCurY(edgePtrs[ehi], nextY); + } + for (; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= nextY; qhi++) { + quadSetCurY(quadPtrs[qhi], nextY); + } + for (; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= nextY; chi++) { + curveSetCurY(curvePtrs[chi], nextY); + } + Arrays.sort(crossings, 0, crossingIdx); + return crossingIdx; } - // We accumulate X in the iterator because accumulating it in addEdge - // like we do with Y does not do much good: if there's an edge - // (0,0)->(1000,10000), and if y gets clipped to 1000, then the x - // bound should be 100, but the accumulator from addEdge would say 1000, - // so we'd still have to accumulate the X bounds as we add crossings. - minX = boundsMinX; - maxX = boundsMaxX; - } - - private int ScanLineItCurrentY() { - return nextY - 1; - } - - private int ScanLineItGoToNextYAndComputeCrossings() { - // we go through the active list and remove the ones that don't cross - // the nextY scanline. - int crossingIdx = 0; - for (int i = lo; i < hi; i++) { - if (edges[edgePtrs[i]+Y1] <= nextY) { - edgePtrs[i] = edgePtrs[lo++]; - } - } - if (hi - lo > crossings.length) { - int newSize = Math.max(hi - lo, crossings.length * 2); - crossings = Arrays.copyOf(crossings, newSize); - } - // Now every edge between lo and hi crosses nextY. Compute it's - // crossing and put it in the crossings array. - for (int i = lo; i < hi; i++) { - addCrossing(nextY, getCurCrossing(edgePtrs[i]), (int)edges[edgePtrs[i]+OR], crossingIdx); - gotoNextY(edgePtrs[i]); - crossingIdx++; + private boolean hasNext() { + return nextY < maxY; } - nextY++; - // Expand active list to include new edges. - for (; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) { - setCurY(edgePtrs[hi], nextY); + private int curY() { + return nextY - 1; } - Arrays.sort(crossings, 0, crossingIdx); - return crossingIdx; + private void addCrossing(int y, int x, float or, int idx) { + x <<= 1; + crossings[idx] = ((or > 0) ? (x | 0x1) : x); + } } - - private boolean ScanLineItHasNext() { - return nextY < maxY; - } - - private void addCrossing(int y, int x, int or, int idx) { - if (x < minX) { - minX = x; - } - if (x > maxX) { - maxX = x; - } - x <<= 1; - crossings[idx] = ((or == 1) ? (x | 0x1) : x); - } - - // quicksort implementation for sorting the edge indices ("pointers") // by increasing y0. first, last are indices into the "pointer" array // It sorts the pointer array from first (inclusive) to last (inclusive) - private void qsort(int first, int last) { + private static void qsort(final float[] data, final int[] ptrs, + final int fieldForCmp, int first, int last) + { if (last > first) { - int p = partition(first, last); + int p = partition(data, ptrs, fieldForCmp, first, last); if (first < p - 1) { - qsort(first, p - 1); + qsort(data, ptrs, fieldForCmp, first, p - 1); } if (p < last) { - qsort(p, last); + qsort(data, ptrs, fieldForCmp, p, last); } } } // i, j are indices into edgePtrs. - private int partition(int i, int j) { - int pivotVal = edgePtrs[i]; + private static int partition(final float[] data, final int[] ptrs, + final int fieldForCmp, int i, int j) + { + int pivotValFieldForCmp = ptrs[i]+fieldForCmp; while (i <= j) { // edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C - while (edges[edgePtrs[i]+CURY] < edges[pivotVal+CURY]) { i++; } - while (edges[edgePtrs[j]+CURY] > edges[pivotVal+CURY]) { j--; } + while (data[ptrs[i]+fieldForCmp] < data[pivotValFieldForCmp]) + i++; + while (data[ptrs[j]+fieldForCmp] > data[pivotValFieldForCmp]) + j--; if (i <= j) { - int tmp = edgePtrs[i]; - edgePtrs[i] = edgePtrs[j]; - edgePtrs[j] = tmp; + int tmp = ptrs[i]; + ptrs[i] = ptrs[j]; + ptrs[j] = tmp; i++; j--; } } return i; } - //============================================================================ ////////////////////////////////////////////////////////////////////////////// // EDGE LIST ////////////////////////////////////////////////////////////////////////////// +// TODO(maybe): very tempting to use fixed point here. A lot of opportunities +// for shifts and just removing certain operations altogether. +// TODO: it might be worth it to make an EdgeList class. It would probably +// clean things up a bit and not impact performance much. - private static final int INIT_NUM_EDGES = 1000; - private static final int SIZEOF_STRUCT_EDGE = 5; + // common to all types of input path segments. + private static final int YMIN = 0; + private static final int YMAX = 1; + private static final int CURX = 2; + // this and OR are meant to be indeces into "int" fields, but arrays must + // be homogenous, so every field is a float. However floats can represent + // exactly up to 26 bit ints, so we're ok. + private static final int CURY = 3; + private static final int OR = 4; - // The following array is a poor man's struct array: - // it simulates a struct array by having - // edges[SIZEOF_STRUCT_EDGE * i + j] be the jth field in the ith element - // of an array of edge structs. - private float[] edges; - private int edgesSize; // size of the edge list. - private static final int Y1 = 0; - private static final int SLOPE = 1; - private static final int OR = 2; // the orientation. This can be -1 or 1. - // -1 means up, 1 means down. - private static final int CURY = 3; // j = 5 corresponds to the "current Y". - // Each edge keeps track of the last scanline - // crossing it computed, and this is the y coord of - // that scanline. - private static final int CURX = 4; //the x coord of the current crossing. + // for straight lines only: + private static final int SLOPE = 5; - // Note that while the array is declared as a float[] not all of it's - // elements should be floats. currentY and Orientation should be ints (or int and - // byte respectively), but they all need to be the same type. This isn't - // really a problem because floats can represent exactly all 23 bit integers, - // which should be more than enough. - // Note, also, that we only need x1 for slope computation, so we don't need - // to store it. x0, y0 don't need to be stored either. They can be put into - // curx, cury, and it's ok if they're lost when curx and cury are changed. - // We take this undeniably ugly and error prone approach (instead of simply - // making an Edge class) for performance reasons. Also, it would probably be nicer - // to have one array for each field, but that would defeat the purpose because - // it would make poor use of the processor cache, since we tend to access - // all the fields for one edge at a time. + // for quads and cubics: + private static final int X0 = 5; + private static final int Y0 = 6; + private static final int XL = 7; + private static final int COUNT = 8; + private static final int CURSLOPE = 9; + private static final int DX = 10; + private static final int DY = 11; + private static final int DDX = 12; + private static final int DDY = 13; - private float edgeMinY; - private float edgeMaxY; + // for cubics only + private static final int DDDX = 14; + private static final int DDDY = 15; + private float edgeMinY = Float.POSITIVE_INFINITY; + private float edgeMaxY = Float.NEGATIVE_INFINITY; + private float edgeMinX = Float.POSITIVE_INFINITY; + private float edgeMaxX = Float.NEGATIVE_INFINITY; - private void addEdge(float x0, float y0, float x1, float y1) { - float or = (y0 < y1) ? 1f : -1f; // orientation: 1 = UP; -1 = DOWN - if (or == -1) { - float tmp = y0; - y0 = y1; - y1 = tmp; - tmp = x0; - x0 = x1; - x1 = tmp; + private static final int SIZEOF_EDGE = 6; + private float[] edges = null; + private int numEdges; + // these are static because we need them to be usable from ScanlineIterator + private void edgeSetCurY(final int idx, int y) { + edges[idx+CURX] += (y - edges[idx+CURY]) * edges[idx+SLOPE]; + edges[idx+CURY] = y; + } + private void edgeGoToNextY(final int idx) { + edges[idx+CURY] += 1; + edges[idx+CURX] += edges[idx+SLOPE]; + } + + + private static final int SIZEOF_QUAD = 14; + private float[] quads = null; + private int numQuads; + // This function should be called exactly once, to set the first scanline + // of the curve. Before it is called, the curve should think its first + // scanline is CEIL(YMIN). + private void quadSetCurY(final int idx, final int y) { + assert y < quads[idx+YMAX]; + assert (quads[idx+CURY] > y); + assert (quads[idx+CURY] == Math.ceil(quads[idx+CURY])); + + while (quads[idx+CURY] < ((float)y)) { + quadGoToNextY(idx); } - // skip edges that don't cross a scanline - if (Math.ceil(y0) >= Math.ceil(y1)) { + } + private void quadGoToNextY(final int idx) { + quads[idx+CURY] += 1; + // this will get overriden if the while executes. + quads[idx+CURX] += quads[idx+CURSLOPE]; + int count = (int)quads[idx+COUNT]; + // this loop should never execute more than once because our + // curve is monotonic in Y. Still we put it in because you can + // never be too sure when dealing with floating point. + while(quads[idx+CURY] >= quads[idx+Y0] && count > 0) { + float x0 = quads[idx+X0], y0 = quads[idx+Y0]; + count = executeQuadAFDIteration(idx); + float x1 = quads[idx+X0], y1 = quads[idx+Y0]; + // our quads are monotonic, so this shouldn't happen, but + // it is conceivable that for very flat quads with different + // y values at their endpoints AFD might give us a horizontal + // segment. + if (y1 == y0) { + continue; + } + quads[idx+CURSLOPE] = (x1 - x0) / (y1 - y0); + quads[idx+CURX] = x0 + (quads[idx+CURY] - y0) * quads[idx+CURSLOPE]; + } + } + + + private static final int SIZEOF_CURVE = 16; + private float[] curves = null; + private int numCurves; + private void curveSetCurY(final int idx, final int y) { + assert y < curves[idx+YMAX]; + assert (curves[idx+CURY] > y); + assert (curves[idx+CURY] == Math.ceil(curves[idx+CURY])); + + while (curves[idx+CURY] < ((float)y)) { + curveGoToNextY(idx); + } + } + private void curveGoToNextY(final int idx) { + curves[idx+CURY] += 1; + // this will get overriden if the while executes. + curves[idx+CURX] += curves[idx+CURSLOPE]; + int count = (int)curves[idx+COUNT]; + // this loop should never execute more than once because our + // curve is monotonic in Y. Still we put it in because you can + // never be too sure when dealing with floating point. + while(curves[idx+CURY] >= curves[idx+Y0] && count > 0) { + float x0 = curves[idx+X0], y0 = curves[idx+Y0]; + count = executeCurveAFDIteration(idx); + float x1 = curves[idx+X0], y1 = curves[idx+Y0]; + // our curves are monotonic, so this shouldn't happen, but + // it is conceivable that for very flat curves with different + // y values at their endpoints AFD might give us a horizontal + // segment. + if (y1 == y0) { + continue; + } + curves[idx+CURSLOPE] = (x1 - x0) / (y1 - y0); + curves[idx+CURX] = x0 + (curves[idx+CURY] - y0) * curves[idx+CURSLOPE]; + } + } + + + private static final float DEC_BND = 20f; + private static final float INC_BND = 8f; + // Flattens using adaptive forward differencing. This only carries out + // one iteration of the AFD loop. All it does is update AFD variables (i.e. + // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings). + private int executeQuadAFDIteration(int idx) { + int count = (int)quads[idx+COUNT]; + float ddx = quads[idx+DDX]; + float ddy = quads[idx+DDY]; + float dx = quads[idx+DX]; + float dy = quads[idx+DY]; + + while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) { + ddx = ddx / 4; + ddy = ddy / 4; + dx = (dx - ddx) / 2; + dy = (dy - ddy) / 2; + count <<= 1; + } + // can only do this on even "count" values, because we must divide count by 2 + while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) { + dx = 2 * dx + ddx; + dy = 2 * dy + ddy; + ddx = 4 * ddx; + ddy = 4 * ddy; + count >>= 1; + } + count--; + if (count > 0) { + quads[idx+X0] += dx; + dx += ddx; + quads[idx+Y0] += dy; + dy += ddy; + } else { + quads[idx+X0] = quads[idx+XL]; + quads[idx+Y0] = quads[idx+YMAX]; + } + quads[idx+COUNT] = count; + quads[idx+DDX] = ddx; + quads[idx+DDY] = ddy; + quads[idx+DX] = dx; + quads[idx+DY] = dy; + return count; + } + private int executeCurveAFDIteration(int idx) { + int count = (int)curves[idx+COUNT]; + float ddx = curves[idx+DDX]; + float ddy = curves[idx+DDY]; + float dx = curves[idx+DX]; + float dy = curves[idx+DY]; + float dddx = curves[idx+DDDX]; + float dddy = curves[idx+DDDY]; + + while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) { + dddx /= 8; + dddy /= 8; + ddx = ddx/4 - dddx; + ddy = ddy/4 - dddy; + dx = (dx - ddx) / 2; + dy = (dy - ddy) / 2; + count <<= 1; + } + // can only do this on even "count" values, because we must divide count by 2 + while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) { + dx = 2 * dx + ddx; + dy = 2 * dy + ddy; + ddx = 4 * (ddx + dddx); + ddy = 4 * (ddy + dddy); + dddx = 8 * dddx; + dddy = 8 * dddy; + count >>= 1; + } + count--; + if (count > 0) { + curves[idx+X0] += dx; + dx += ddx; + ddx += dddx; + curves[idx+Y0] += dy; + dy += ddy; + ddy += dddy; + } else { + curves[idx+X0] = curves[idx+XL]; + curves[idx+Y0] = curves[idx+YMAX]; + } + curves[idx+COUNT] = count; + curves[idx+DDDX] = dddx; + curves[idx+DDDY] = dddy; + curves[idx+DDX] = ddx; + curves[idx+DDY] = ddy; + curves[idx+DX] = dx; + curves[idx+DY] = dy; + return count; + } + + + private void initLine(final int idx, float[] pts, int or) { + edges[idx+SLOPE] = (pts[2] - pts[0]) / (pts[3] - pts[1]); + edges[idx+CURX] = pts[0] + (edges[idx+CURY] - pts[1]) * edges[idx+SLOPE]; + } + + private void initQuad(final int idx, float[] points, int or) { + final int countlg = 3; + final int count = 1 << countlg; + + // the dx and dy refer to forward differencing variables, not the last + // coefficients of the "points" polynomial + final float ddx, ddy, dx, dy; + c.set(points, 6); + + ddx = c.dbx / (1 << (2 * countlg)); + ddy = c.dby / (1 << (2 * countlg)); + dx = c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg); + dy = c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg); + + quads[idx+DDX] = ddx; + quads[idx+DDY] = ddy; + quads[idx+DX] = dx; + quads[idx+DY] = dy; + quads[idx+COUNT] = count; + quads[idx+XL] = points[4]; + quads[idx+X0] = points[0]; + quads[idx+Y0] = points[1]; + executeQuadAFDIteration(idx); + float x1 = quads[idx+X0], y1 = quads[idx+Y0]; + quads[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]); + quads[idx+CURX] = points[0] + (quads[idx+CURY] - points[1])*quads[idx+CURSLOPE]; + } + + private void initCurve(final int idx, float[] points, int or) { + final int countlg = 3; + final int count = 1 << countlg; + + // the dx and dy refer to forward differencing variables, not the last + // coefficients of the "points" polynomial + final float dddx, dddy, ddx, ddy, dx, dy; + c.set(points, 8); + dddx = 2f * c.dax / (1 << (3 * countlg)); + dddy = 2f * c.day / (1 << (3 * countlg)); + + ddx = dddx + c.dbx / (1 << (2 * countlg)); + ddy = dddy + c.dby / (1 << (2 * countlg)); + dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg); + dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg); + + curves[idx+DDDX] = dddx; + curves[idx+DDDY] = dddy; + curves[idx+DDX] = ddx; + curves[idx+DDY] = ddy; + curves[idx+DX] = dx; + curves[idx+DY] = dy; + curves[idx+COUNT] = count; + curves[idx+XL] = points[6]; + curves[idx+X0] = points[0]; + curves[idx+Y0] = points[1]; + executeCurveAFDIteration(idx); + float x1 = curves[idx+X0], y1 = curves[idx+Y0]; + curves[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]); + curves[idx+CURX] = points[0] + (curves[idx+CURY] - points[1])*curves[idx+CURSLOPE]; + } + + private void addPathSegment(float[] pts, final int type, final int or) { + int idx; + float[] addTo; + switch (type) { + case 4: + idx = numEdges * SIZEOF_EDGE; + addTo = edges = Helpers.widenArray(edges, numEdges*SIZEOF_EDGE, SIZEOF_EDGE); + numEdges++; + break; + case 6: + idx = numQuads * SIZEOF_QUAD; + addTo = quads = Helpers.widenArray(quads, numQuads*SIZEOF_QUAD, SIZEOF_QUAD); + numQuads++; + break; + case 8: + idx = numCurves * SIZEOF_CURVE; + addTo = curves = Helpers.widenArray(curves, numCurves*SIZEOF_CURVE, SIZEOF_CURVE); + numCurves++; + break; + default: + throw new InternalError(); + } + // set the common fields, except CURX, for which we must know the kind + // of curve. NOTE: this must be done before the type specific fields + // are initialized, because those depend on the common ones. + addTo[idx+YMIN] = pts[1]; + addTo[idx+YMAX] = pts[type-1]; + addTo[idx+OR] = or; + addTo[idx+CURY] = (float)Math.ceil(pts[1]); + switch (type) { + case 4: + initLine(idx, pts, or); + break; + case 6: + initQuad(idx, pts, or); + break; + case 8: + initCurve(idx, pts, or); + break; + default: + throw new InternalError(); + } + } + + // precondition: the curve in pts must be monotonic and increasing in y. + private void somethingTo(float[] pts, final int type, final int or) { + // NOTE: it's very important that we check for or >= 0 below (as + // opposed to or == 1, or or > 0, or anything else). That's + // because if we check for or==1, when the curve being added + // is a horizontal line, or will be 0 so or==1 will be false and + // x0 and y0 will be updated to pts[0] and pts[1] instead of pts[type-2] + // and pts[type-1], which is the correct thing to do. + this.x0 = or >= 0 ? pts[type - 2] : pts[0]; + this.y0 = or >= 0 ? pts[type - 1] : pts[1]; + + float minY = pts[1], maxY = pts[type - 1]; + if (Math.ceil(minY) >= Math.ceil(maxY) || + Math.ceil(minY) >= boundsMaxY || maxY < boundsMinY) + { return; } - int newSize = edgesSize + SIZEOF_STRUCT_EDGE; - if (edges.length < newSize) { - edges = Arrays.copyOf(edges, newSize * 2); - } - edges[edgesSize+CURX] = x0; - edges[edgesSize+CURY] = y0; - edges[edgesSize+Y1] = y1; - edges[edgesSize+SLOPE] = (x1 - x0) / (y1 - y0); - edges[edgesSize+OR] = or; - // the crossing values can't be initialized meaningfully yet. This - // will have to wait until setCurY is called - edgesSize += SIZEOF_STRUCT_EDGE; + if (minY < edgeMinY) { edgeMinY = minY; } + if (maxY > edgeMaxY) { edgeMaxY = maxY; } - // Accumulate edgeMinY and edgeMaxY - if (y0 < edgeMinY) { edgeMinY = y0; } - if (y1 > edgeMaxY) { edgeMaxY = y1; } + int minXidx = (pts[0] < pts[type-2] ? 0 : type - 2); + float minX = pts[minXidx]; + float maxX = pts[type - 2 - minXidx]; + if (minX < edgeMinX) { edgeMinX = minX; } + if (maxX > edgeMaxX) { edgeMaxX = maxX; } + addPathSegment(pts, type, or); } - // As far as the following methods care, this edges extends to infinity. - // They can compute the x intersect of any horizontal line. - // precondition: idx is the index to the start of the desired edge. - // So, if the ith edge is wanted, idx should be SIZEOF_STRUCT_EDGE * i - private void setCurY(int idx, int y) { - // compute the x crossing of edge at idx and horizontal line y - // currentXCrossing = (y - y0)*slope + x0 - edges[idx + CURX] = (y - edges[idx + CURY]) * edges[idx + SLOPE] + edges[idx+CURX]; - edges[idx + CURY] = (float)y; - } +// END EDGE LIST +////////////////////////////////////////////////////////////////////////////// - private void gotoNextY(int idx) { - edges[idx + CURY] += 1f; // i.e. curY += 1 - edges[idx + CURX] += edges[idx + SLOPE]; // i.e. curXCrossing += slope - } - - private int getCurCrossing(int idx) { - return (int)edges[idx + CURX]; - } -//==================================================================================== public static final int WIND_EVEN_ODD = 0; public static final int WIND_NON_ZERO = 1; @@ -284,16 +586,13 @@ final int MAX_AA_ALPHA; // Cache to store RLE-encoded coverage mask of the current primitive - final PiscesCache cache; + PiscesCache cache; // Bounds of the drawing region, at subpixel precision. - final private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; - - // Pixel bounding box for current primitive